【问题标题】:dplyr: mutate a factor column with 3 levels to 3 logical columns with TRUE and FALSEdplyr:将具有 3 个级别的因子列更改为具有 TRUE 和 FALSE 的 3 个逻辑列
【发布时间】:2021-03-23 18:17:51
【问题描述】:

在 Iris 数据集中,Species 是一个具有 3 个级别的因子变量(“setosa”“versicolor”“virginica”)。 我想创建 3 个名为 ("setosa" "versicolor" "virginica") 的附加列,其中 False 和 True 作为每列的逻辑因子变量。 简而言之:我想将 Iris 数据集中变量 Species 的级别分成 3 个新列作为逻辑变量。我的代码有效,但我想知道是否有更直接的方法:

df <- iris %>%
  select(Species) %>% 
  mutate(setosa = case_when(Species=="setosa" ~ 1,
                            TRUE ~ 0),
         versicolor = case_when(Species=="versicolor" ~ 1,
                            TRUE ~ 0),
         virginica = case_when(Species=="virginica" ~ 1,
                            TRUE ~ 0),
         )
df$setosa <- as.logical(df$setosa)
df$versicolor <- as.logical(df$versicolor)
df$virginica <- as.logical(df$virginica)

【问题讨论】:

    标签: r transform dplyr


    【解决方案1】:

    尝试直接为Species 创建一个逻辑变量以及一个副本,然后使用tidyverse 函数重塑为宽。您的行还需要一个 id 变量。代码如下:

    library(dplyr)
    library(tidyr)
    #Data
    data(iris)
    #Code
    df <- iris %>% mutate(id=row_number(),Species2=Species) %>%
      select(c(id,Species,Species2)) %>%
      mutate(Value=T) %>%
      pivot_wider(names_from = Species2,values_from=Value,values_fill=F) %>%
      select(-id)
    

    输出:

    # A tibble: 150 x 4
       Species setosa versicolor virginica
       <fct>   <lgl>  <lgl>      <lgl>    
     1 setosa  TRUE   FALSE      FALSE    
     2 setosa  TRUE   FALSE      FALSE    
     3 setosa  TRUE   FALSE      FALSE    
     4 setosa  TRUE   FALSE      FALSE    
     5 setosa  TRUE   FALSE      FALSE    
     6 setosa  TRUE   FALSE      FALSE    
     7 setosa  TRUE   FALSE      FALSE    
     8 setosa  TRUE   FALSE      FALSE    
     9 setosa  TRUE   FALSE      FALSE    
    10 setosa  TRUE   FALSE      FALSE    
    # ... with 140 more rows
    

    【讨论】:

      【解决方案2】:

      使用以下任何一种:

      iris %>% cbind(sapply(levels(.$Species), `==`, .$Species))
      
      iris %>% cbind(model.matrix(~ Species + 0, .) == 1)
      
      iris %>% cbind(outer(.$Species, setNames(levels(.$Species), levels(.$Species)), "=="))
      
      expand_factor <- function(f) {
        m <- matrix(0, length(f), nlevels(f), dimnames = list(NULL, levels(f)))
        replace(m, cbind(seq_along(f), f), 1)
      }
      iris %>% cbind(expand_factor(.$Species) == 1)
      
      library(nnet)
      iris %>% cbind(class.ind(.$Species) == 1)
      

      【讨论】:

      • 这是最直接的方法。谢谢格洛腾迪克。
      【解决方案3】:

      这是另一种整洁的方式。我觉得它很乏味,个人不会将它用于像您的示例这样简单的事情,但它对于更复杂的应用程序很有用。例如,如果您对多个变量进行“一个热”编码,出于某种原因,将单个变量全部存储在一列中可能会很好。然后,您可以提取它,而不必为不同的变量不断抓取不同数量的列。

      这利用了将list() 存储在tibble 内的能力,然后将其取消嵌套到列中。

      library(purrr)
      library(dplyr)
      library(tidyr)
      
      iris %>% 
        mutate(species_one_hot = map(Species, ~ set_names(levels(Species) == .x, levels(Species)))) %>% 
        unnest_wider(species_one_hot)
      

      您可以通过以下方式提前停止一个步骤以存储代码供以后使用。

      iris2 <- iris %>% 
        mutate(species_one_hot = map(Species, ~ set_names(levels(Species) == .x, levels(Species))))
      
      # now you can grab a single column and have the full encoding
      bind_rows(iris2$species_one_hot)
      

      【讨论】:

      • 这也是一种有趣的方式。也谢谢亚当。
      • @TarJae 总是乐于助人:)
      猜你喜欢
      • 1970-01-01
      • 2021-05-31
      • 1970-01-01
      • 1970-01-01
      • 2017-12-15
      • 2019-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多