tidyverse r 中的虚拟代码分类/有序变量

Dummy code categorical / ordinal variables in the tidyverse r

假设我有一个问题。

library(tidyverse) 
tib <- as.tibble(list(record = c(1:10), 
                      gender = as.factor(sample(c("M", "F"), 10, replace = TRUE)), 
                      like_product = as.factor(sample(1:5, 10, replace = TRUE)))
tib

    # A tibble: 10 x 3
   record gender like_product
    <int> <fctr>       <fctr>
 1      1      F            2
 2      2      M            1
 3      3      M            2
 4      4      F            3
 5      5      F            4
 6      6      M            2
 7      7      F            4
 8      8      M            4
 9      9      F            4
10     10      M            5

我想用 1 和 0 对我的数据进行虚拟编码,以便数据看起来 more/less 像这样。

# A tibble: 10 x 8
   record gender_M gender_F like_product_1 like_product_2 like_product_3 like_product_4 like_product_5
    <int>    <dbl>    <dbl>          <dbl>          <dbl>          <dbl>          <dbl>          <dbl>
 1      1        0        1              0              0              1              0              0
 2      2        0        1              0              0              0              0              0
 3      3        0        1              0              1              0              0              0
 4      4        0        1              1              0              0              0              0
 5      5        1        0              0              0              0              0              0
 6      6        0        1              0              0              0              0              0
 7      7        0        1              0              0              0              0              0
 8      8        0        1              0              1              0              0              0
 9      9        1        0              0              0              0              0              0
10     10        1        0              0              0              0              0              1

我的工作流程要求我知道虚拟代码的一系列变量(即 gender:like_product),但不想手动识别每个变量(可能有数百个变量)。同样,我不想将每个变量的每个 level/unique 值都标识为虚拟代码。我最终正在寻找 tidyverse 解决方案。

我知道有几种方法可以做到这一点,但其中 none 非常适合 tidyverse。我知道我可以使用 mutate...

tib %>%
     mutate(gender_M = ifelse(gender == "M", 1, 0), 
            gender_F = ifelse(gender == "F", 1, 0), 
            like_product_1 = ifelse(like_product == 1, 1, 0), 
            like_product_2 = ifelse(like_product == 2, 1, 0), 
            like_product_3 = ifelse(like_product == 3, 1, 0), 
            like_product_4 = ifelse(like_product == 4, 1, 0), 
            like_product_5 = ifelse(like_product == 5, 1, 0)) %>%
     select(-gender, -like_product)

但这会破坏我需要指定每个虚拟编码输出的工作流程规则。

我过去曾使用 model.matrix 从 stats 包中完成此操作。

model.matrix(~ gender + like_product, tib) 

简单明了,但我想要 tidyverse 中的解决方案。 编辑: 原因是,我仍然必须指定每个变量,并且能够使用 select 助手来指定 gender:like_product 之类的东西会更受欢迎。

我认为解决方案在 purrr

library(purrr)
dummy_code <- function(x) {
     lvls <- levels(x)
     sapply(lvls, function(y) as.integer(x == y)) %>% as.tibble
} 

tib %>%
     map_at(c("gender", "like_product"), dummy_code)

$record
 [1]  1  2  3  4  5  6  7  8  9 10

$gender
# A tibble: 10 x 2
       F     M
   <int> <int>
 1     1     0
 2     0     1
 3     0     1
 4     1     0
 5     1     0
 6     0     1
 7     1     0
 8     0     1
 9     1     0
10     0     1

$like_product
# A tibble: 10 x 5
     `1`   `2`   `3`   `4`   `5`
   <int> <int> <int> <int> <int>
 1     0     1     0     0     0
 2     1     0     0     0     0
 3     0     1     0     0     0
 4     0     0     1     0     0
 5     0     0     0     1     0
 6     0     1     0     0     0
 7     0     0     0     1     0
 8     0     0     0     1     0
 9     0     0     0     1     0
10     0     0     0     0     1

此尝试生成了一个 tibbles 列表,排除的变量 record 除外,但我未能将它们全部组合回一个 tibble。此外,我仍然必须指定每一列,总的来说它看起来很笨重。

有什么更好的主意吗?谢谢!!

model.matrix 的替代方法是使用包 recipes。这项工作仍在进行中,尚未包含在 tidyverse 中。在某些时候它可能/将包含在 tidyverse packages 中。

我会留给你阅读食谱,但在步骤 step_dummy 中,你可以使用 tidyselect 包(与 recipes 一起安装)中的特殊选择器,例如您可以在 dplyr 中用作 starts_with() 的选择器。我创建了一个小示例来展示这些步骤。

示例代码如下。

但如果这更方便,我会留给你,因为评论中已经指出了这一点。函数 bake() 使用 model.matrix 创建虚拟对象。区别主要在于列名,当然还有在所有单独步骤的基础代码中进行的内部检查。

library(recipes)
library(tibble)

tib <- as.tibble(list(record = c(1:10), 
                      gender = as.factor(sample(c("M", "F"), 10, replace = TRUE)), 
                      like_product = as.factor(sample(1:5, 10, replace = TRUE))))

dum <- tib %>% 
  recipe(~ .) %>% 
  step_dummy(gender, like_product) %>% 
  prep(training = tib) %>% 
  bake(newdata = tib)

dum

# A tibble: 10 x 6
   record gender_M like_product_X2 like_product_X3 like_product_X4 like_product_X5
    <int>    <dbl>           <dbl>           <dbl>           <dbl>           <dbl>
 1      1       1.              1.              0.              0.              0.
 2      2       1.              1.              0.              0.              0.
 3      3       1.              1.              0.              0.              0.
 4      4       0.              0.              1.              0.              0.
 5      5       0.              0.              0.              0.              0.
 6      6       0.              1.              0.              0.              0.
 7      7       0.              1.              0.              0.              0.
 8      8       0.              0.              0.              1.              0.
 9      9       0.              0.              0.              0.              1.
10     10       1.              0.              0.              0.              0.