【问题标题】:Split delimited strings in a column and insert as new rows [duplicate]在列中拆分分隔字符串并作为新行插入[重复]
【发布时间】:2019-07-01 03:57:21
【问题描述】:

我有一个如下的数据框:

+-----+-------+
|  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 |
+----+----+

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

【问题讨论】:

    标签: r dataframe reshape data-manipulation strsplit


    【解决方案1】:

    截至 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
    

    【讨论】:

    • 哇从 biostar 到这里 ..找到你
    【解决方案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
    

    【讨论】:

    • 这个不简单但是很贴心
    • @cloudscomputes 这实际上是一个非常简单的答案。谢谢。
    • 我怎样才能反转这个功能?当输出是输入时,输入将是我想要的输出。
    【解决方案3】:

    现在您可以使用 tidyr 0.5.0 的 separate_rows 代替 strsplit + unnest

    例如:

    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】:

      这是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 
      

      【讨论】:

      • 看来您必须在其中抛出gsub 或其他东西才能用data.table 去除空格,但它在输出中不可见有点奇怪。 print(as.data.frame(d.dt), quote=TRUE) 表明空白仍然存在。 +1 虽然。
      • 谢谢。已添加strip.white = TRUE
      【解决方案5】:

      您可以考虑使用我的“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.frame 开始。它使用read.csv 将第二列作为单独的data.frame 读取,我们将其与源数据中的第一列相结合。最后,您使用reshape 将数据转换为长格式。

      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
      

      【讨论】:

      • 嘿,如果我在此数据框中有其他列,并且我确实希望这些列也在最终拆分的数据框中?
      【解决方案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))]
      

      重要的是by=seq_len(nrow(DT)),这是分裂发生的“假”唯一ID。改用by=.I 很诱人,因为它的定义应该相同,但.I 似乎是一个改变其价值的神奇事物,最好坚持使用by=seq_len(nrow(DT))

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多