我们如何立即将 tidyr::spread() 应用于所有分类变量,为每个分类变量的每个级别创建新列?

How can we apply tidyr:: spread() to all categorical variables at once creating new columns for each level of each categorical variable?

我有一个包含 3 个分类变量(xyz)和一个 ID 列的数据框:

df <- frame_data(
  ~id, ~x, ~y, ~z,
  1, "a", "c" ,"v",
  1, "b", "d", "f",
  2, "a", "d", "v",
  2, "b", "d", "v")

我想将 spread() 应用于按 ID 分组的每个分类变量。

输出应该是这样的:

id  a  b  c  d  v  f
1  1  1  1  1  1  1
2  1  1  0  2  2  0

我试过这样做,但我一次只能对一个变量做,而不是一起做。

例如:仅对 y 列应用扩展(类似地,可以分别对 xz 应用扩展)但不能在一行中一起应用

df %>% count(id,y) %>% spread(y,n,fill=0)
# A tibble: 2 x 3
id     c     d
<dbl> <int> <int>
1.00     1     1
2.00     0     2

分三步解释我的代码:

第 1 步:计算频率

df %>% count(id,y)    
id     y         n
<dbl> <chr> <int>
1.00   c     1
1.00   d     1
2.00   d     2

第 2 步:应用 spread()

df %>% count(id,y) %>% spread(y,n)
# A tibble: 2 x 3
id     c     d
<dbl> <int> <int>
1  1.00     1     1
2  2.00    NA     2

第 3 步:添加 fill = 0,替换 NA,这意味着 id 2 的 y 列中 c 的出现次数为零(如您在 df 中所见)

df %>% count(id,y) %>% spread(y,n,fill=0)
# A tibble: 2 x 3
id     c     d
<dbl> <int> <int>
1.00     1     1
2.00     0     2

问题:在我的实际数据集中,我有20个这样的分类变量,我无法一一对应。我希望一次完成所有这一切。 是否可以将 spread() in tidyr 应用于所有分类变量?如果不能,请推荐一个替代方案

注意:我也尝试了这些答案,但对这种特殊情况没有帮助:

其他相关的有用问题:

两个分类列(例如:调查数据集)可能具有相同的值。如下图。

df <- frame_data(
  ~id, ~Do_you_Watch_TV, ~Do_you_Drive, 
  1, "yes", "yes",
  1, "yes", "no",
  2, "yes", "no",
  2, "no", "yes")

# A tibble: 4 x 3
id Do_you_Watch_TV Do_you_Drive
<dbl> <chr>           <chr>       
  1  1.00 yes             yes         
2  1.00 yes             no          
3  2.00 yes             no          
4  2.00 no              yes 

运行 下面的代码不会区分 'Do_you_Watch_TV'、'Do_you_Drive' 的是和否的计数:

df %>% gather(Key, value, -id) %>% 
  group_by(id, value) %>%
  summarise(count = n())  %>%
  spread(value, count, fill = 0) %>%
  as.data.frame()
id no yes
1  1   3
2  2   2

Whereas, expected output should be :
id Do_you_Watch_TV_no   Do_you_Watch_TV_yes  Do_you_Drive_no   Do_you_Drive_yes
1         0               2                    1                 1
2         1               1                    1                 1

因此,我们需要通过添加前缀来分别处理来自 Do_you_Watch_TV 和 Do_you_Drive 的 No 和 Yes。 Do_you_Drive_是,Do_you_Drive_否,Do_you_Watch_TV_是,Do_you_Watch_TV_否。

我们怎样才能做到这一点?

谢谢

首先,您需要先将数据框转换为长格式,然后才能实际将其转换为宽格式。因此,首先您需要使用 tidyr::gather 并将数据帧转换为长格式。之后,您有几个选择:

选项#1: 使用 tidyr::spread:

#data
df <- frame_data(
  ~id, ~x, ~y, ~z,
  1, "a", "c" ,"v",
  1, "b", "d", "f",
  2, "a", "d", "v",
  2, "b", "d", "v")

library(tidyverse)
df %>% gather(Key, value, -id) %>% 
  group_by(id, value) %>%
  summarise(count = n())  %>%
  spread(value, count, fill = 0) %>%
  as.data.frame()

#   id a b c d f v
# 1  1 1 1 1 1 1 1
# 2  2 1 1 0 2 0 2

选项#2: 另一种选择是使用 reshape2::dcast 作为:

library(tidyverse)
library(reshape2)

df %>% gather(Key, value, -id) %>% 
  dcast(id~value, fun.aggregate = length)

#   id a b c d f v
# 1  1 1 1 1 1 1 1
# 2  2 1 1 0 2 0 2

已编辑:包含第二个数据框的解决方案。

#Data
df1 <- frame_data(
  ~id, ~Do_you_Watch_TV, ~Do_you_Drive, 
  1, "yes", "yes",
  1, "yes", "no",
  2, "yes", "no",
  2, "no", "yes")

library(tidyverse)
df1 %>% gather(Key, value, -id) %>% unite("value", c(Key, value)) %>%
  group_by(id, value) %>%
  summarise(count = n())  %>%
  spread(value, count, fill = 0) %>%
  as.data.frame()

#   id Do_you_Drive_no Do_you_Drive_yes Do_you_Watch_TV_no Do_you_Watch_TV_yes
# 1  1               1                1                  0                   2
# 2  2               1                1                  1                   1