tidyr 在多个列上使用 separate_rows
tidyr use separate_rows over multiple columns
我有一个 data.frame,其中一些单元格包含逗号分隔值的字符串:
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(d, 2, separate_rows(...))
不确定如何将参数传递给 separate_rows()
函数。
您可以使用管道。请注意,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
这是使用 splitstackshape::cSplit
和 zoo::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
使用tidyr
1.2.0 版,我们可以使用everything
到select 所有列来分隔,
上的行。正如@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)
我有一个 data.frame,其中一些单元格包含逗号分隔值的字符串:
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(d, 2, separate_rows(...))
不确定如何将参数传递给 separate_rows()
函数。
您可以使用管道。请注意,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
这是使用 splitstackshape::cSplit
和 zoo::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
使用tidyr
1.2.0 版,我们可以使用everything
到select 所有列来分隔,
上的行。正如@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)