Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/73.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R 将列中以逗号分隔的字符串拆分为单独的行_R_String_Split_R Faq_Data.table_Dplyr_Tidyr - Fatal编程技术网

R 将列中以逗号分隔的字符串拆分为单独的行

R 将列中以逗号分隔的字符串拆分为单独的行,r,string,split,r-faq,data.table,dplyr,tidyr,R,String,Split,R Faq,Data.table,Dplyr,Tidyr,我有一个数据框,如下所示: data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", "Alejandro Gonzalez Inarritu,Be

我有一个数据框,如下所示:

data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", 
                        "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", 
                        "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", 
                        "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", 
                        "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", 
                        "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", 
                        "Anne Fontaine", "Anthony Harvey"), AB = c('A', 'B', 'A', 'A', 'B', 'B', 'B', 'A', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'A'))

如您所见,
director
列中的某些条目是由逗号分隔的多个名称。我想将这些条目分成单独的行,同时保留另一列的值。例如,上面数据框中的第一行应分为两行,在
director
列中各有一个名称,在
AB
列中各有一个“a”。

为原始数据命名。frame
v
,我们有:

> s <- strsplit(as.character(v$director), ',')
> data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length)))
                      director AB
1                 Aaron Blaise  A
2                   Bob Walker  A
3               Akira Kurosawa  B
4               Alan J. Pakula  A
5                  Alan Parker  A
6           Alejandro Amenabar  B
7  Alejandro Gonzalez Inarritu  B
8  Alejandro Gonzalez Inarritu  B
9             Benicio Del Toro  B
10 Alejandro González Iñárritu  A
11                 Alex Proyas  B
12              Alexander Hall  A
13              Alfonso Cuaron  B
14            Alfred Hitchcock  A
15              Anatole Litvak  A
16              Andrew Adamson  B
17                 Marilyn Fox  B
18              Andrew Dominik  B
19              Andrew Stanton  B
20              Andrew Stanton  B
21                 Lee Unkrich  B
22              Angelina Jolie  B
23              John Stevenson  B
24               Anne Fontaine  B
25              Anthony Harvey  A
>s data.frame(director=unlist,AB=rep(v$AB,sapply(s,FUN=length)))
董事AB
Aaron Blaise A
2鲍勃·沃克A
3黑泽明B
4阿兰·J·帕库拉A
5艾伦·帕克A
6亚历杭德罗·阿蒙纳巴尔B
7亚历杭德罗·冈萨雷斯·伊纳里图B
8亚历杭德罗·冈萨雷斯·伊纳里图B
9贝尼西奥·德尔·托罗B酒店
10 Alejandro González Iñárritu A
11亚历克斯·普罗亚斯B
12亚历山大厅A
13阿方索·卡隆B
14阿尔弗雷德·希区柯克A
15阿纳托利·利特瓦克A
16安德鲁·亚当森B
17玛丽莲·福克斯B
18安德鲁·多米尼克B
19安德鲁·斯坦顿B
20安德鲁·斯坦顿B
21李安里奇B
22安吉丽娜·朱莉B
23约翰·史蒂文森B
24安妮芳丹B酒店
25安东尼·哈维A

注意使用
rep
构建新的AB列。这里,
sapply
返回每个原始行中的名称数。

晚到派对,但另一个通用的替代方法是使用我的“splitstackshape”包中的
cSplit
,该包有一个
方向
参数。将其设置为
“long”
,以获得指定的结果:

library(splitstackshape)
head(cSplit(mydf, "director", ",", direction = "long"))
#              director AB
# 1:       Aaron Blaise  A
# 2:         Bob Walker  A
# 3:     Akira Kurosawa  B
# 4:     Alan J. Pakula  A
# 5:        Alan Parker  A
# 6: Alejandro Amenabar  B
若干备选方案:

1)使用两种方式:

library(data.table)
# method 1 (preferred)
setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
         ][!is.na(director)]
# method 2
setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)
         ][,.(director = V1, AB)]
library(dplyr)
library(tidyr)
v %>% 
  mutate(director = strsplit(as.character(director), ",")) %>%
  unnest(director)
# if 'director' is a character-column:
stack(setNames(strsplit(df$director,','), df$AB))

# if 'director' is a factor-column:
stack(setNames(strsplit(as.character(df$director),','), df$AB))
2)a/组合:

library(data.table)
# method 1 (preferred)
setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
         ][!is.na(director)]
# method 2
setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)
         ][,.(director = V1, AB)]
library(dplyr)
library(tidyr)
v %>% 
  mutate(director = strsplit(as.character(director), ",")) %>%
  unnest(director)
# if 'director' is a character-column:
stack(setNames(strsplit(df$director,','), df$AB))

# if 'director' is a factor-column:
stack(setNames(strsplit(as.character(df$director),','), df$AB))
3)仅带:带(及更高版本),您也可以只使用
分隔行

separate_rows(v, director, sep = ",")
您可以使用
convert=TRUE
参数将数字自动转换为数字列

4)带基数R:

library(data.table)
# method 1 (preferred)
setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
         ][!is.na(director)]
# method 2
setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)
         ][,.(director = V1, AB)]
library(dplyr)
library(tidyr)
v %>% 
  mutate(director = strsplit(as.character(director), ",")) %>%
  unnest(director)
# if 'director' is a character-column:
stack(setNames(strsplit(df$director,','), df$AB))

# if 'director' is a factor-column:
stack(setNames(strsplit(as.character(df$director),','), df$AB))

这个老问题经常被用作重复目标(标记为
r-faq
)。截至今天,已经有三次回答,提供了6种不同的方法,但缺少一个基准作为指导,哪种方法是最快的1

基准解决方案包括

  • 但根据,
  • 两种
    data.table
    方法和两种
    dplyr
    /
    tidyr
    方法
  • ,
  • 和Jaap的数据表方法的另外两种变体
使用
microbenchmark
软件包(见下面的代码),在6种不同大小的数据帧上对总共8种不同的方法进行了基准测试

OP给出的样本数据仅由20行组成。要创建更大的数据帧,只需将这20行重复1、10、100、1000、10000和100000次,问题大小可达200万行

基准结果

基准测试结果表明,对于足够大的数据帧,所有
data.table
方法都比任何其他方法更快。对于超过5000行的数据帧,Jaap的
data.table
method 2和variant
DT3
是最快的,比最慢的方法快很多

值得注意的是,两种
tidyverse
方法和
splistackshape
解决方案的计时非常相似,因此很难在图表中区分曲线。它们是所有数据帧大小中最慢的基准方法

对于较小的数据帧,Matt的base R解决方案和
data.table
方法4的开销似乎比其他方法小

代码 针对不同的问题大小运行基准测试 会话信息和程序包版本(摘录)

1才华横溢的人激起了我的好奇心!快几个数量级!一个
tidyverse
的答案作为这个问题的副本关闭。

另一个基准使用
strsplit
从base生成,目前可以建议将列中逗号分隔的字符串拆分为单独的行,因为它在各种大小范围内都是最快的:

s <- strsplit(v$director, ",", fixed=TRUE)
s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))
图书馆:

library(microbenchmark)
library(splitstackshape) #cSplit
library(data.table) #dt, dt2, dt3, dt4
#setDTthreads(1) #Looks like it has here minor effect
library(dplyr) #dplyr
library(tidyr) #dplyr, tidyr
数据:


但据我所知,这并没有被问到。

只是想问一个显而易见的问题:这些数据是你应该发布在互联网上的吗?它们“不都是B级电影”。这些人都是奥斯卡提名人,我几乎不认为这是一个秘密。)我想知道'AB=rep(v$AB,unlist(sapply(s,FUN=length))'是否比更晦涩的
vapply更容易理解?有没有什么东西能让
vapply
在这里更合适?现在
sapply(s,length)
可以被
length(s)
取代。太好了!cSplit和单独的_行(专门为此设计)似乎还有改进的余地。顺便说一句,cSplit还接受一个fixed=arg,并且是一个基于data.table的包,所以最好给它DT而不是DF。同样,fwiw,我认为从factor到char的转换不属于基准测试(因为它应该是char)。我进行了检查,但这些更改都没有对结果产生任何定性影响。@Frank感谢您提出改进基准的建议,并检查对结果的影响。在发布下一版本的
data.table
dplyr
等之后进行更新时,会发现这一点。我认为这些方法不具有可比性,至少不是在所有情况下都是如此,因为datatable方法只生成具有“选定”列的表,而dplyr生成具有所有列的结果(包括未参与分析且无h
met <- alist(base = {s <- strsplit(v$director, ",") #Matthew Lundberg
   s <- data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length)))}
 , baseLength = {s <- strsplit(v$director, ",") #Rich Scriven
   s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))}
 , baseLeFix = {s <- strsplit(v$director, ",", fixed=TRUE)
   s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))}
 , cSplit = s <- cSplit(v, "director", ",", direction = "long") #A5C1D2H2I1M1N2O1R2T1
 , dt = s <- setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, "," #Jaap
   , fixed=TRUE))), by = AB][!is.na(director)]
#, dt2 = s <- setDT(v)[, strsplit(director, "," #Jaap #Only Unique
#  , fixed=TRUE), by = .(AB, director)][,.(director = V1, AB)]
 , dplyr = {s <- v %>%  #Jaap
    mutate(director = strsplit(director, ",", fixed=TRUE)) %>%
    unnest(director)}
 , tidyr = s <- separate_rows(v, director, sep = ",") #Jaap
 , stack = s <- stack(setNames(strsplit(v$director, ",", fixed=TRUE), v$AB)) #Jaap
#, dt3 = {s <- setDT(v)[, strsplit(director, ",", fixed=TRUE), #Uwe #Only Unique
#  by = .(AB, director)][, director := NULL][, setnames(.SD, "V1", "director")]}
 , dt4 = {s <- setDT(v)[, .(director = unlist(strsplit(director, "," #Uwe
   , fixed = TRUE))), by = .(AB)]}
 , dt5 = {s <- vT[, .(director = unlist(strsplit(director, "," #Uwe
   , fixed = TRUE))), by = .(AB)]}
   )
library(microbenchmark)
library(splitstackshape) #cSplit
library(data.table) #dt, dt2, dt3, dt4
#setDTthreads(1) #Looks like it has here minor effect
library(dplyr) #dplyr
library(tidyr) #dplyr, tidyr
v0 <- data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", 
                        "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", 
                        "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", 
                        "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", 
                        "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", 
                        "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", 
                        "Anne Fontaine", "Anthony Harvey"), AB = c('A', 'B', 'A', 'A', 'B', 'B', 'B', 'A', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'A'))
n <- 10^(0:5)
x <- lapply(n, function(n) {v <- v0[rep(seq_len(nrow(v0)), n),]
  vT <- setDT(v)
  ti <- min(100, max(3, 1e4/n))
  microbenchmark(list = met, times = ti, control=list(order="block"))})

y <- do.call(cbind, lapply(x, function(y) aggregate(time ~ expr, y, median)))
y <- cbind(y[1], y[-1][c(TRUE, FALSE)])
y[-1] <- y[-1] / 1e6 #ms
names(y)[-1] <- paste("n:", n * nrow(v0))
y #Time in ms
#         expr     n: 20    n: 200    n: 2000   n: 20000   n: 2e+05   n: 2e+06
#1        base 0.2989945 0.6002820  4.8751170  46.270246  455.89578  4508.1646
#2  baseLength 0.2754675 0.5278900  3.8066300  37.131410  442.96475  3066.8275
#3   baseLeFix 0.2160340 0.2424550  0.6674545   4.745179   52.11997   555.8610
#4      cSplit 1.7350820 2.5329525 11.6978975  99.060448 1053.53698 11338.9942
#5          dt 0.7777790 0.8420540  1.6112620   8.724586  114.22840  1037.9405
#6       dplyr 6.2425970 7.9942780 35.1920280 334.924354 4589.99796 38187.5967
#7       tidyr 4.0323765 4.5933730 14.7568235 119.790239 1294.26959 11764.1592
#8       stack 0.2931135 0.4672095  2.2264155  22.426373  289.44488  2145.8174
#9         dt4 0.5822910 0.6414900  1.2214470   6.816942   70.20041   787.9639
#10        dt5 0.5015235 0.5621240  1.1329110   6.625901   82.80803   636.1899
(v <- rbind(v0[1:2,], v0[1,]))
#                 director AB
#1 Aaron Blaise,Bob Walker  A
#2          Akira Kurosawa  B
#3 Aaron Blaise,Bob Walker  A

setDT(v)[, strsplit(director, "," #Jaap #Only Unique
  , fixed=TRUE), by = .(AB, director)][,.(director = V1, AB)]
#         director AB
#1:   Aaron Blaise  A
#2:     Bob Walker  A
#3: Akira Kurosawa  B
tmp <- unique(v)
s <- strsplit(tmp$director, ",", fixed=TRUE)
s <- data.frame(director=unlist(s), AB=rep(tmp$AB, lengths(s)))