dplyr:使用 TRUE 和 FALSE 将具有 3 个级别的因子列突变为 3 个逻辑列

dplyr: mutate a factor column with 3 levels to 3 logical columns with TRUE and FALSE

在 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)

尝试直接为 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

使用其中任何一个:

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)

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

这利用了将 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)