【问题标题】:"unpacking" a factor list from a data.frame从 data.frame 中“解包”一个因子列表
【发布时间】:2012-12-22 23:45:40
【问题描述】:

我是 R 新手/可以选择轻松地重新组织数据,并且一直在寻找解决方案,但找不到我想要做的事情。 Reshape2 的熔化/铸造似乎不太奏效,而且我对 plyr 的掌握还不够好,无法将其考虑在内。

基本上,我有一个 data.frame,其结构如下所示,其中每个元素都是一个可变长度的类别列表(更紧凑,因为 # 列更大,我实际上有多个 category_lists我想分开):

>mydf
       ID      category_list    xval    yval
1     ID1   cat1, cat2, cat3   xnum1   ynum1
2     ID2         cat2, cat3   xnum2   ynum2
3     ID3               cat1   xnum3   ynum3

我想将类别作为因素(以及相关的值,即第 3/4 列)进行操作,所以我认为我最终需要这样的东西,其中 ID 和 x/y/其他列值是重复的根据类别列表的长度:

       ID           category    xval    yval
1     ID1               cat1   xnum1   ynum1
2     ID1               cat2   xnum1   ynum1
3     ID1               cat3   xnum1   ynum1
4     ID2               cat2   xnum2   ynum2
5     ID2               cat3   xnum2   ynum2
6     ID3               cat3   xnum2   ynum2

如果 category_list 上的因子/方面有另一种解决方案,那将是一个更简单的解决方案,但我还没有遇到支持此的方法, 例如以下抛出错误

>ggplot(mydf, aes(x=x, y=y)) + geom_point() + facet_grid(~cat_list)

layout_base(data, cols, drop = drop) 中的错误: 至少一层必须包含用于分面的所有变量

谢谢!

【问题讨论】:

  • 你能把dput(mydf)的输出贴出来吗。

标签: r dataframe reshape2


【解决方案1】:

一种可能性:

x <- read.table(textConnection('
    ID      category_list    xval    yval
     ID1   "cat1, cat2, cat3"   xnum1   ynum1
     ID2         "cat2, cat3"   xnum2   ynum2
     ID3               "cat1"   xnum3   ynum3'),
          header=TRUE,stringsAsFactors=FALSE)

library(plyr)
ddply(x,"ID",transform,category=strsplit(category_list,",")[[1]])

##    ID    category_list  xval  yval category
## 1 ID1 cat1, cat2, cat3 xnum1 ynum1     cat1
## 2 ID1 cat1, cat2, cat3 xnum1 ynum1     cat2
## 3 ID1 cat1, cat2, cat3 xnum1 ynum1     cat3
## 4 ID2       cat2, cat3 xnum2 ynum2     cat2
## 5 ID2       cat2, cat3 xnum2 ynum2     cat3

【讨论】:

    【解决方案2】:

    一个缓慢但看似强大的解决方案:

    ## Some example data
    df <- as.data.frame(cbind(ID = paste0("ID", 1:2), 
                              category_list = list(4:1, 2:3), 
                              xvar = 8:9, 
                              yvar = 10:9))
    
    ## Calculate number of times each row of df will be repeated 
    nn <- sapply(df$category_list, length)  
    ii <- rep(seq_along(nn), times=nn)       
    
    ## Reshape data.frame
    transform(df[ii,], 
              category = unlist(df$category_list),
              category_list = NULL, 
              row.names = NULL)
    #    ID xvar yvar category
    # 1 ID1    8   10        4
    # 2 ID1    8   10        3
    # 3 ID1    8   10        2
    # 4 ID1    8   10        1
    # 5 ID2    9    9        2
    # 6 ID2    9    9        3
    

    【讨论】:

    • transform 的使用,尤其是 df[ii,] 扩展它的技巧,真的很棒。绝对是一个有用的选择。这也帮助我更好地理解了sapplyseq_along。谢谢。
    【解决方案3】:

    答案取决于category_list 的格式。如果实际上每行都是list

    类似

    mydf <- data.frame(ID = paste0('ID',1:3), 
     category_list = I(list(c('cat1','cat2','cat3'),  c('cat2','cat3'), c('cat1'))), 
     xval = 1:3, yval = 1:3)
    

    library(data.table)
    mydf <- as.data.frame(data.table(ID = paste0('ID',1:3), 
     category_list = list(c('cat1','cat2','cat3'),  c('cat2','cat3'), c('cat1')), 
     xval = 1:3, yval = 1:3) )
    

    然后您可以使用plyrmerge 来创建您的长表单数据

     newdf <- merge(mydf, ddply(mydf, .(ID), summarize, cat_list = unlist(category_list)), by = 'ID')
    
    
       ID    category_list xval yval cat_list
    1 ID1 cat1, cat2, cat3    1    1     cat1
    2 ID1 cat1, cat2, cat3    1    1     cat2
    3 ID1 cat1, cat2, cat3    1    1     cat3
    4 ID2       cat2, cat3    2    2     cat2
    5 ID2       cat2, cat3    2    2     cat3
    6 ID3             cat1    3    3     cat1
    

    或不需要merge的非plyr方法

     do.call(rbind,lapply(split(mydf, mydf$ID), transform, cat_list = unlist(category_list)))
    

    【讨论】:

    • 因为最简洁而被接受。我喜欢使用I(),但不知道merge()。谢谢!
    • 绝对漂亮!谢谢
    • 出于某种原因,在我的情况下(解包 json),do.call 解决方案效果最好。
    【解决方案4】:

    这将是一种非 plyr 方法:

    cbind( x[ rep(1:nrow(x), 
                  times=sapply(x$category_list, 
                                function(xx) sapply( strsplit(xx, ","), length) ) ),
              -2],    # to get rid of the old category column
           new_cats = unlist( strsplit(x$category_list, ",") ) )
     # this used Bolker's example. If these are factor will need to add `as.character`
    
         ID  xval  yval new_cats
    1   ID1 xnum1 ynum1     cat1
    1.1 ID1 xnum1 ynum1     cat2
    1.2 ID1 xnum1 ynum1     cat3
    2   ID2 xnum2 ynum2     cat2
    2.1 ID2 xnum2 ynum2     cat3
    3   ID3 xnum3 ynum3     cat1
    

    【讨论】:

      【解决方案5】:

      使用by 的另一种基本 R 可能性:

      do.call(rbind,
      by(mydf,
         mydf$ID,
         function(x) {
           data.frame(
                      ID=x$ID,
                      category_list = unlist(strsplit(x$category_list,",")),
                      xval=x$xval,
                      yval=x$yval
                     ) 
         }
        )
      )
      

      结果:

             ID category_list  xval  yval
      ID1.1 ID1          cat1 xnum1 ynum1
      ID1.2 ID1          cat2 xnum1 ynum1
      ID1.3 ID1          cat3 xnum1 ynum1
      ID2.1 ID2          cat2 xnum2 ynum2
      ID2.2 ID2          cat3 xnum2 ynum2
      ID3   ID3          cat1 xnum3 ynum3
      

      【讨论】:

        【解决方案6】:

        注意:原始答案已删除,因为我的答案基于与 OP 实际拥有的数据结构不同的数据结构。


        场景 1:列是 list

        使用@mnel 的样本数据:

        mydf <- data.frame(ID = paste0('ID',1:3), 
         category_list = I(list(c('cat1','cat2','cat3'),  c('cat2','cat3'), c('cat1'))), 
         xval = 1:3, yval = 1:3)
        

        使用我的“splitstackshape”包中的listCol_l

        library(splitstackshape)
        listCol_l(mydf, "category_list")
        #     ID xval yval category_list_ul
        # 1: ID1    1    1             cat1
        # 2: ID1    1    1             cat2
        # 3: ID1    1    1             cat3
        # 4: ID2    2    2             cat2
        # 5: ID2    2    2             cat3
        # 6: ID3    3    3             cat1
        

        使用“tidyr”包中的unnest

        library(tidyr)
        unnest(mydf, "category_list")
        #    ID category_list xval yval
        # 1 ID1          cat1    1    1
        # 2 ID1          cat2    1    1
        # 3 ID1          cat3    1    1
        # 4 ID2          cat2    2    2
        # 5 ID2          cat3    2    2
        # 6 ID3          cat1    3    3
        

        场景2:列是串联字符串

        使用@BenBolker 的样本数据:

        x <- read.table(textConnection('
            ID      category_list    xval    yval
             ID1   "cat1, cat2, cat3"   xnum1   ynum1
             ID2         "cat2, cat3"   xnum2   ynum2
             ID3               "cat1"   xnum3   ynum3'),
                        header=TRUE,stringsAsFactors=FALSE)
        

        使用我的“splitstackshape”包中的cSplit

        library(splitstackshape)
        cSplit(x, "category_list", ",", "long")
        #     ID category_list  xval  yval
        # 1: ID1          cat1 xnum1 ynum1
        # 2: ID1          cat2 xnum1 ynum1
        # 3: ID1          cat3 xnum1 ynum1
        # 4: ID2          cat2 xnum2 ynum2
        # 5: ID2          cat3 xnum2 ynum2
        # 6: ID3          cat1 xnum3 ynum3
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-20
          • 2015-09-20
          • 2019-12-14
          • 1970-01-01
          相关资源
          最近更新 更多