拆分列中带分隔符的字符串并作为新行插入 [duplicate]

Split delimited strings in a column and insert as new rows [duplicate]

提问人:Boxuan 提问时间:3/12/2013 最后编辑:Boxuan 更新时间:3/26/2018 访问量:125401

问:

我有一个数据框如下:

+-----+-------+
|  V1 |  V2   |
+-----+-------+
|  1  | a,b,c |
|  2  | a,c   |
|  3  | b,d   |
|  4  | e,f   |
|  .  | .     |
+-----+-------+

每个字母表都是一个用逗号分隔的字符。我想在每个逗号上拆分 V2 并将拆分字符串作为新行插入。例如,所需的输出将是:

+----+----+
| V1 | V2 |
+----+----+
|  1 |  a |
|  1 |  b |
|  1 |  c |
|  2 |  a |
|  2 |  c |
|  3 |  b |
|  3 |  d |
|  4 |  e |
|  4 |  f |
+----+----+

我正在尝试先吐出 V2,然后将列表转换为数据帧。它没有用。任何帮助将不胜感激。strsplit()

R DataFrame Reshape 数据操作 strsplit

评论


答:

19赞 A5C1D2H2I1M1N2O1R2T1 3/12/2013 #1

你可以考虑从我的“splitstackshape”包中。cSplitdirection = "long"

用法是:

cSplit(mydf, "V2", ",", "long")
##    V1 V2
## 1:  1  a
## 2:  1  b
## 3:  1  c
## 4:  2  a
## 5:  2  c
## 6:  3  b
## 7:  3  d
## 8:  4  e
## 9:  4  f

老答案....

下面是使用基本 R 的一种方法。它假设我们从一个名为“mydf”开始。它用于在第二列中读取 作为一个单独的 ,我们将其与源数据中的第一列合并。最后,您可以使用将数据转换为长格式。data.frameread.csvdata.framereshape

temp <- data.frame(Ind = mydf$V1, 
                   read.csv(text = as.character(mydf$V2), header = FALSE))
temp1 <- reshape(temp, direction = "long", idvar = "Ind", 
                 timevar = "time", varying = 2:ncol(temp), sep = "")
temp1[!temp1$V == "", c("Ind", "V")]
#     Ind  V
# 1.1   1  a
# 2.1   2  a
# 3.1   3  b
# 4.1   4  e
# 1.2   1  b
# 2.2   2  c
# 3.2   3  d
# 4.2   4  f
# 1.3   1  c

另一个相当直接的选择是:

stack(
  setNames(
    sapply(strsplit(mydf$V2, ","), 
           function(x) gsub("^\\s|\\s$", "", x)), mydf$V1))
  values ind
1      a   1
2      b   1
3      c   1
4      a   2
5      c   2
6      b   3
7      d   3
8      e   4
9      f   4

评论

0赞 indra_patil 2/4/2016
嘿,如果我在这个数据框中有其他列,并且我确实希望这些列也在最终的拆分数据框中怎么办?
1赞 Catalyst 7/9/2022
谢谢@A5C1D2H2I1M1N2O1R2T1。其他解决方案似乎对我不起作用,但对我有用。对于那些与@indra_patil有相同查询的人,您可以做到cSplitcSplit(mydf, c("V1", "V2"), ",", "long")
86赞 CHP 3/12/2013 #2

这是另一种方法。

df <- read.table(textConnection("1|a,b,c\n2|a,c\n3|b,d\n4|e,f"), header = F, sep = "|", stringsAsFactors = F)

df
##   V1    V2
## 1  1 a,b,c
## 2  2   a,c
## 3  3   b,d
## 4  4   e,f

s <- strsplit(df$V2, split = ",")
data.frame(V1 = rep(df$V1, sapply(s, length)), V2 = unlist(s))
##   V1 V2
## 1  1  a
## 2  1  b
## 3  1  c
## 4  2  a
## 5  2  c
## 6  3  b
## 7  3  d
## 8  4  e
## 9  4  f

评论

2赞 cloudscomputes 9/26/2017
这个不简单,但很周到
4赞 Jonathan Rauscher 11/29/2017
@cloudscomputes这实际上是一个非常简单的答案。谢谢。
0赞 ersan 2/24/2021
我怎样才能反转这个功能?当输出是输入时,输入将是我想要的输出。
39赞 Arun 3/12/2013 #3

这里有一个解决方案:data.table

d.df <- read.table(header=T, text="V1 | V2
1 | a,b,c
2 | a,c
3 | b,d
4 | e,f", stringsAsFactors=F, sep="|", strip.white = TRUE)
require(data.table)
d.dt <- data.table(d.df, key="V1")
out <- d.dt[, list(V2 = unlist(strsplit(V2, ","))), by=V1]

#    V1 V2
# 1:  1  a
# 2:  1  b
# 3:  1  c
# 4:  2  a
# 5:  2  c
# 6:  3  b
# 7:  3  d
# 8:  4  e
# 9:  4  f

> sapply(out$V2, nchar) # (or simply nchar(out$V2))
# a b c a c b d e f 
# 1 1 1 1 1 1 1 1 1 

评论

1赞 A5C1D2H2I1M1N2O1R2T1 3/12/2013
似乎您也必须在那里扔一个或什么东西来剥离空格,但它在输出中不可见有点奇怪。 表示空格仍然存在。+1 虽然。gsubdata.tableprint(as.data.frame(d.dt), quote=TRUE)
2赞 Arun 3/12/2013
谢谢。添加。strip.white = TRUE
170赞 dalloliogm 12/8/2014 #4

截至 2014 年 12 月,这可以使用 Hadley Wickham 的 tidyr 软件包中的 unnest 函数来完成(请参阅发行说明 http://blog.rstudio.org/2014/12/08/tidyr-0-2-0/)

> library(tidyr)
> library(dplyr)
> mydf

  V1    V2
2  1 a,b,c
3  2   a,c
4  3   b,d
5  4   e,f
6  .     .


> mydf %>% 
    mutate(V2 = strsplit(as.character(V2), ",")) %>% 
    unnest(V2)

   V1 V2
1   1  a
2   1  b
3   1  c
4   2  a
5   2  c
6   3  b
7   3  d
8   4  e
9   4  f
10  .  .

2017 年更新:请注意以下@Tif所述的功能。separate_rows

它的工作方式要好得多,并且允许在单个语句中“取消嵌套”多个列:

> head(mydf)
geneid              chrom    start  end strand  length  gene_count
ENSG00000223972.5   chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1    11869;12010;12179;12613;12613;12975;13221;13221;13453   12227;12057;12227;12721;12697;13052;13374;14409;13670   +;+;+;+;+;+;+;+;+   1735    11
ENSG00000227232.5   chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1;chr1  14404;15005;15796;16607;16858;17233;17606;17915;18268;24738;29534   14501;15038;15947;16765;17055;17368;17742;18061;18366;24891;29570   -;-;-;-;-;-;-;-;-;-;-   1351    380
ENSG00000278267.1   chr1    17369   17436   -   68  14
ENSG00000243485.4   chr1;chr1;chr1;chr1;chr1    29554;30267;30564;30976;30976   30039;30667;30667;31097;31109   +;+;+;+;+   1021    22
ENSG00000237613.2   chr1;chr1;chr1  34554;35277;35721   35174;35481;36081   -;-;-   1187    24
ENSG00000268020.3   chr1    52473   53312   +   840 14


> mydf %>% separate_rows(strand, chrom, gene_start, gene_end)
geneid  length  gene_count  strand  chrom   start   end
ENSG00000223972.5   1735    11  +   chr1    11869   12227
ENSG00000223972.5   1735    11  +   chr1    12010   12057
ENSG00000223972.5   1735    11  +   chr1    12179   12227
ENSG00000223972.5   1735    11  +   chr1    12613   12721
ENSG00000223972.5   1735    11  +   chr1    12613   12697
ENSG00000223972.5   1735    11  +   chr1    12975   13052
ENSG00000223972.5   1735    11  +   chr1    13221   13374
ENSG00000223972.5   1735    11  +   chr1    13221   14409
ENSG00000223972.5   1735    11  +   chr1    13453   13670
ENSG00000227232.5   1351    380 -   chr1    14404   14501
ENSG00000227232.5   1351    380 -   chr1    15005   15038
ENSG00000227232.5   1351    380 -   chr1    15796   15947
ENSG00000227232.5   1351    380 -   chr1    16607   16765
ENSG00000227232.5   1351    380 -   chr1    16858   17055
ENSG00000227232.5   1351    380 -   chr1    17233   17368
ENSG00000227232.5   1351    380 -   chr1    17606   17742
ENSG00000227232.5   1351    380 -   chr1    17915   18061
ENSG00000227232.5   1351    380 -   chr1    18268   18366
ENSG00000227232.5   1351    380 -   chr1    24738   24891
ENSG00000227232.5   1351    380 -   chr1    29534   29570
ENSG00000278267.1   68  5   -   chr1    17369   17436
ENSG00000243485.4   1021    8   +   chr1    29554   30039
ENSG00000243485.4   1021    8   +   chr1    30267   30667
ENSG00000243485.4   1021    8   +   chr1    30564   30667
ENSG00000243485.4   1021    8   +   chr1    30976   31097
ENSG00000243485.4   1021    8   +   chr1    30976   31109
ENSG00000237613.2   1187    24  -   chr1    34554   35174
ENSG00000237613.2   1187    24  -   chr1    35277   35481
ENSG00000237613.2   1187    24  -   chr1    35721   36081
ENSG00000268020.3   840 0   +   chr1    52473   53312
75赞 Tif 6/23/2016 #5

现在你可以用 tidyr 0.5.0 代替 + .separate_rowsstrsplitunnest

例如:

library(tidyr)
(df <- read.table(textConnection("1|a,b,c\n2|a,c\n3|b,d\n4|e,f"), header = F, sep = "|", stringsAsFactors = F))
  V1    V2
1  1 a,b,c
2  2   a,c
3  3   b,d
4  4   e,f
separate_rows(df, V2)

给:

  V1 V2
1  1  a
2  1  b
3  1  c
4  2  a
5  2  c
6  3  b
7  3  d
8  4  e
9  4  f

请参阅参考资料:https://blog.rstudio.org/2016/06/13/tidyr-0-5-0/

4赞 Aaron McDaid 7/15/2016 #6

另一种解决方案,它不依赖于原始数据中存在任何唯一字段。data.table

DT = data.table(read.table(header=T, text="blah | splitme
    T | a,b,c
    T | a,c
    F | b,d
    F | e,f", stringsAsFactors=F, sep="|", strip.white = TRUE))

DT[,.( blah
     , splitme
     , splitted=unlist(strsplit(splitme, ","))
     ),by=seq_len(nrow(DT))]

重要的是,这是发生拆分的“假”唯一 ID。它很诱人,因为它应该被定义相同,但似乎是一个神奇的东西,会改变它的价值,最好坚持下去by=seq_len(nrow(DT))by=.I.Iby=seq_len(nrow(DT))

输出中有三列。我们只需命名现有的两列,然后计算第三列作为拆分

.( blah       # first column of original
 , splitme    # second column of original
 , splitted = unlist(strsplit(splitme, ","))
 )