tidyr使用separate_rows处理多列数据

14

我有一个数据框,其中一些单元格包含逗号分隔值的字符串:

d <- data.frame(a=c(1:3), 
       b=c("name1, name2, name3", "name4", "name5, name6"),
       c=c("name7","name8, name9", "name10" ))

我想将那些字符串分开,每个名称都拆分到自己的单元格中。使用

很容易实现。

tidyr::separate_rows(d, b, sep=",") 

如果一次只对一列进行操作,那么这是可行的。但是我无法同时为"b"列和"c"列执行此操作,因为这需要每个字符串中的名称数量相同。而不是写

tidyr::separate_rows(d, b, sep=",") 
tidyr::separate_rows(d, c, sep=",") 
有没有一种方法可以用一行代码来完成这个操作,比如使用apply?类似于:
apply(d, 2, separate_rows(...)) 

不确定如何向separate_rows()函数传递参数。

3个回答

14

你可以使用管道符号。注意,sep = ", "会自动被检测到。

d %>% separate_rows(b) %>% separate_rows(c)
#   a     b      c
# 1 1 name1  name7
# 2 1 name2  name7
# 3 1 name3  name7
# 4 2 name4  name8
# 5 2 name4  name9
# 6 3 name5 name10
# 7 3 name6 name10

注意: 在使用tidyr版本0.6.0及以上的版本中,%>%操作符已包含在该软件包中。


更新: 根据@doscendodiscimus的评论,我们可以使用一个for()循环并在每次迭代中重新分配d。这样我们就可以拥有尽可能多的列。我们将使用一个字符向量作为列名,因此我们需要切换到标准评估版本,即separate_rows_

cols <- c("b", "c")
for(col in cols) {
    d <- separate_rows_(d, col)
}

它提供了更新后的d

  a     b      c
1 1 name1  name7
2 1 name2  name7
3 1 name3  name7
4 2 name4  name8
5 2 name4  name9
6 3 name5 name10
7 3 name6 name10

更新:在tidyr1.2.0中,separate_rows_已被弃用。我们可以使用以下代码代替:

cols <- c("b", "c")
for (col in cols) {
  d <- separate_rows(d, all_of(col))
}

1
是的,我可以为每一行编写相同的代码,但是否有一种方法可以为n列执行此操作?如果您有10个或更多列,这将变得很繁琐。关于cols参数,如果同时包含两列,则会出现错误。 - user23413
3
@user23413,如果您想继续使用tidyr,您可以尝试像这样的循环:for(col in c("b", "c")) d <- separate_rows_(d, col, sep = ",") - talat
@docendodiscimus - 你想发布那个吗?我同意那是正确的做法。 - Rich Scriven
@RichScriven,不,如果你喜欢的话,你可以将它添加到你的帖子中 :) - talat
好的,如果这是正确的方法。我只是想确保我不会错过适用于此处的一行代码。我目前正在使用for循环。 - user23413
1
也可以尝试这个变体:d %>% Reduce(f = separate_rows_, x = c("b", "c")) - G. Grothendieck

6

这里有一种另类的方法,使用splitstackshape::cSplitzoo::na.locf

library(splitstackshape)
library(zoo)

df <- cSplit(d, 1:ncol(d), "long", sep = ",")
na.locf(df[rowSums(is.na(df)) != ncol(df),])
#    a     b      c
#1:  1 name1  name7
#2:  1 name2  name7
#3:  1 name3  name7
#4:  2 name4  name8
#5:  2 name4  name9
#6:  3 name5 name10
#7:  3 name6 name10

2

使用tidyr 1.2.0版本,我们可以使用everything选择所有列来在,上分隔行。正如@RichScriven所提到的,默认分隔符为sep = ", "

library(tidyr)

d %>% 
  separate_rows(everything())

输出

      a b     c     
  <int> <chr> <chr> 
1     1 name1 name7 
2     1 name2 name7 
3     1 name3 name7 
4     2 name4 name8 
5     2 name4 name9 
6     3 name5 name10
7     3 name6 name10

另外,我们可以指定要分隔行的列,或者只需排除我们不想要的列。

d %>% 
  separate_rows(b, c)


d %>% 
  separate_rows(-a)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接