【问题标题】:How to strsplit data frame column and replicate rows accordingly? [duplicate]如何拆分数据框列并相应地复制行? [复制]
【发布时间】:2014-08-27 00:57:35
【问题描述】:

我有一个这样的数据框:

> df <- data.frame(Column1=c("id1", "id2", "id3"), Column2=c("text1,text2,text3", "text4", "text5,text6"), Column3=c("text7", "text8,text9,text10,text11", "text12,text13"))

> df
  Column1           Column2                   Column3
1     id1 text1,text2,text3                     text7
2     id2             text4 text8,text9,text10,text11
3     id3       text5,text6             text12,text13

如何将其转换为这种格式?

  Column1 variable                     value
1     id1  Column2                     text1
2     id1  Column2                     text2
3     id1  Column2                     text3
4     id2  Column2                     text4
5     id3  Column2                     text5
6     id3  Column2                     text6
7     id1  Column3                     text7
8     id2  Column3                     text8
9     id2  Column3                     text9
10    id2  Column3                    text10
11    id2  Column3                    text11
12    id3  Column3                    text12
13    id3  Column3                    text13

我想第一步是 melt() 数据框(顺便说一句,我应该担心那个警告吗?):

> library(reshape2)    
> mdf <- melt(df, id.vars="Column1", measure.vars=c("Column2", "Column3"))
> mdf
  Column1 variable                     value
1     id1  Column2         text1,text2,text3
2     id2  Column2                     text4
3     id3  Column2               text5,text6
4     id1  Column3                     text7
5     id2  Column3 text8,text9,text10,text11
6     id3  Column3             text12,text13
Warning message:
attributes are not identical across measure variables; they will be dropped

然后我基本上需要 ``strsplit()` 'value' 列并相应地复制行,但我想不出办法。

> strsplit(mdf$value, ",")
[[1]]
[1] "text1" "text2" "text3"

[[2]]
[1] "text4"

[[3]]
[1] "text5" "text6"

[[4]]
[1] "text7"

[[5]]
[1] "text8"  "text9"  "text10" "text11"

[[6]]
[1] "text12" "text13"

感谢任何帮助!谢谢。

【问题讨论】:

    标签: r split dataframe reshape2 melt


    【解决方案1】:

    data.table 解决方案:

    library(data.table)
    mdt <- melt(setDT(df), id.vars="Column1")[,strsplit(as.character(value),",",fixed=TRUE),
                                              by=list(Column1,variable)]
    

    结果:

    > mdt
        Column1 variable     V1
     1:     id1  Column2  text1
     2:     id1  Column2  text2
     3:     id1  Column2  text3
    ....
    

    您还可以使用latest version of data.table (v1.9.5+) 中的tstrsplit 函数,该函数保留value 列的名称,而不是将其重命名为V1

    mdt <- melt(setDT(df), id.vars="Column1")[,lapply(.SD, function(x) tstrsplit(x, ",", fixed=TRUE)),
                                              by=list(Column1,variable)]
    

    结果:

    > mdt
        Column1 variable  value
     1:     id1  Column2  text1
     2:     id1  Column2  text2
     3:     id1  Column2  text3
    ....
    

    dplyr & tidyr 的替代解决方案:

    library(dplyr)
    library(tidyr)
    mdf <- df %>% gather(variable, value, -Column1) %>% 
      transform(value = strsplit(as.character(value),",")) %>%
      unnest(value)
    

    结果:

    > mdf
       Column1 variable  value
    1      id1  Column2  text1
    2      id1  Column2  text2
    3      id1  Column2  text3
    ....
    

    使用最新版tidyr,还可以使用separate_rows-函数:

    mdf <- df %>% 
      gather(variable, value, -Column1) %>% 
      separate_rows(value)
    

    【讨论】:

    • “data.table”方法与cSplit 的做法差不多,但还有一些其他选项(例如让拆分数据为宽格式)。
    • data.table 和 dplyr 在功能上经常重叠。有没有办法用 dplyr 达到同样的效果?
    • @enrico16 我更新了我的答案,加入了dplyr 解决方案,我还更新了data.table 解决方案。
    【解决方案2】:

    关于警告:它出现是因为您正在使用因子变量进行熔化。

    在您的示例中,您可以避免在 df 声明的末尾添加 stringAsFactors=FALSE 的警告:

    df <- data.frame(Column1=c("id1", "id2", "id3"), Column2=c("text1,text2,text3", "text4", "text5,text6"), Column3=c("text7", "text8,text9,text10,text11", "text12,text13"), stringsAsFactors=FALSE)
    

    【讨论】:

      【解决方案3】:

      你可以试试:

       library(reshape2)
      

      cSplit 来自https://gist.github.com/mrdwab/11380733

       cSplit(melt(df, id.vars="Column1"), "value", ",", "long")
       #      Column1 variable  value
       # 1:     id1  Column2  text1
       # 2:     id1  Column2  text2
       # 3:     id1  Column2  text3
       # 4:     id2  Column2  text4
       # 5:     id3  Column2  text5
       # 6:     id3  Column2  text6
       # 7:     id1  Column3  text7
       # 8:     id2  Column3  text8
       # 9:     id2  Column3  text9
       #10:     id2  Column3 text10
       #11:     id2  Column3 text11
       #12:     id3  Column3 text12
       #13:     id3  Column3 text13
      

      或者,如果想坚持使用 CRAN 包中提供的功能:

      library(reshape2)
      library(splitstackshape)
      library(dplyr)
      select(na.omit(concat.split.multiple(melt(df, id.vars="Column1"), split.col="value", sep=",", direction="long")), -time)
      

      【讨论】:

      • 这个答案很震撼!!! :-) 但是,我可能会改用cSplit(melt(df, id.vars="Column1"), "value", ",", "long")
      • @Ananda Mahto,谢谢。我用您建议的紧凑版本更新了代码。当我第一次运行代码时,它给出了一条警告消息;警告消息:度量变量的属性不相同;他们将被丢弃。现在,我没有收到消息。
      • @AnandaMahto,cSplit() 是包的一部分吗?我发现 splitstackshape::concat.split.multiple() 的工作方式类似,但在使用 direction='long' 时会令人讨厌地插入一个“时间”列。
      • @Enrico,还没有。一旦我有时间弄清楚需要从“splitstackshape”中修剪什么,它将取代concat.split.multiple。您提到的时间变量是reshape 函数的副作用。
      • cSplit() 于 1.4.0 发布(2014 年 10 月)r-bloggers.com/splitstackshape-v1-4-0-for-r
      【解决方案4】:

      你已经走到这一步了:

      mdf <- melt(df, id.vars="Column1", measure.vars=c("Column2", "Column3"))
      values <- strsplit(mdf$value, ",")
      

      现在您需要做的就是创建要使用mdf 的哪些行的索引:

      n <- vapply(values, length, integer(1))
      index <- rep.int(seq_along(n), n)
      

      然后将其与值结合起来:

      cbind(mdf[index,], unlist(values, use.names = FALSE))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-20
        • 1970-01-01
        • 1970-01-01
        • 2019-05-20
        • 2018-08-17
        • 2014-10-09
        • 1970-01-01
        • 2021-09-06
        相关资源
        最近更新 更多