【问题标题】:How can we apply tidyr:: spread() to all categorical variables at once creating new columns for each level of each categorical variable? [duplicate]我们如何一次将 tidyr:: spread() 应用于所有分类变量,为每个分类变量的每个级别创建新列? [复制]
【发布时间】:2018-11-19 17:19:27
【问题描述】:

我有一个包含 3 个分类变量 (x,y,z) 以及一个 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

第二步:申请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() 应用于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_yes , Do_you_Drive_no , Do_you_Watch_TV _yes , Do_you_Watch_TV _no 。

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

谢谢

【问题讨论】:

  • 嗨@Jaap:上面的问题有两个部分。 第一部分由你在这里回答部分:[链接]stackoverflow.com/questions/43262085/…。您的代码建议在 collect 中明确提及列:tidyr :: spread library(dplyr) library(tidyr) dat %&gt;% gather(key, val, state:type2) %&gt;% group_by(serialno, val) %&gt;% tally() %&gt;% spread(val, n, fill = 0) 而我正在寻找隐含的方式。所以,我们可以用gather(Key, value, -serialno)代替gather(Key, value, state:type2)
  • @JAAP 第二部分是:如果我们有一个附加条件,我们如何做同样的事情 - 如果两个不同的类别列具有相同的值? 答案也是不在这里 [链接] stackoverflow.com/questions/43262085/… 。所以,这个问题可能不是重复的。这两部分都在下面回答了
  • 感谢您添加第二个链接,但这是否涵盖使用 tidyr::spread() ?如果有的话,你可以在这里添加链接吗?第一个链接部分回答。第二个链接:使用dcast() 我的问题与使用tidyr::spread() 相关,因为它的性能优于dcast()(执行时间)。请查看我的标题和问题
  • (1) 第一个链接完全回答了您问题的第一部分。 gather(Key, value, state:type2)gather(Key, value, -serialno)see here 相同。 (2) 两个附加链接涵盖了您问题的第二部分。

标签: r dataframe dplyr tidyr data-cleaning


【解决方案1】:

首先,您需要先将数据框转换为长格式,然后才能真正将其转换为宽格式。因此,首先您需要使用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

【讨论】:

  • 如果不使用spread,则选项#3:df %&gt;% gather(var, val, x:z) %&gt;% {xtabs(~ id + val, data = .)} 和...。
  • @mt1022 - 选项# 3 有效,但我们需要明确指定列名,如 x:z 。对于隐式的做事方式,我宁愿选择选项#1 或 #2
  • @Ravijeet,同意。关键信息是,我们可以通过许多比基于spread 的方法更简单的替代方法来实现所需的输出。
  • @MKR - 这行得通。但是,我还有一个相关的问题:我们如何向 a 、 b 、 c 添加前缀(父列,即 x_)?它变成 x_a ,x_b,x_c 。我刚刚意识到可以有两个具有相同级别的分类列。例如: x 具有级别 'Yes' 和 'No' 并且 y 也具有 'Yes' 和 'No' 。因此,我们需要通过添加前缀来分别对待它们。 x_yes , x_no , y_yes, y_no
  • @MKR :在问题中添加了示例和预期输出。 unite("value", c(Key, value)) - 也可以解决这个相关问题。谢谢
猜你喜欢
  • 2019-08-24
  • 2019-06-28
  • 2015-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多