【问题标题】:Expand an list in an R dataframe into additional rows of the dataframe?将 R 数据框中的列表扩展为数据框的其他行?
【发布时间】:2015-12-10 20:00:02
【问题描述】:

separate question earlier today 中,我询问了如何将嵌套列表展平为数据框中的一行。我希望进一步了解如何在数据框中操作我的列表,这次是通过在数据框中垂直扩展列表,添加新行来容纳数据。

在这种情况下,我希望摆脱这种结构:

CAT    COUNT            TREAT
A      1,2,3            Treat-a, Treat-b
B      4,5              Treat-c,Treat-d,Treat-e

到这个结构:

CAT   COUNT  TREAT
A     1      Treat-a   
A     2      Treat-b
A     3      NA 
B     4      Treat-c 
B     5      Treat-d
B     NA     Treat-e

生成测试(源)数据的代码:

df<-data.frame(CAT=c("A","B"))
df$COUNT <-list(1:3,4:5) # as numbers
df$TREAT <-list(paste("Treat-", letters[1:2],sep=""),paste("Treat-", letters[3:5],sep=""))

我尝试以类似于我之前问题的答案的方法使用 CBIND,但由于多个列表之间的值数量不同,它失败了。感谢您在我尝试掌握这些基本操作任务时的耐心等待。

【问题讨论】:

  • 蒂姆,我知道你不是在追求性能,但我很想知道我分享的功能如何满足你的需求。这些相关问题的公认答案都做了您可能不想要的事情。例如,此处接受的答案将所有列转换为因子。对您的其他问题的接受答案只是将lists 转换为矩阵(与ncol 核对结果以查看您实际最终得到多少列。换句话说,即使它看起来像创建了一个@ 987654327@ 有 7 列,生成的 data.frame 只有 3 列)。谢谢!

标签: r


【解决方案1】:

这是我想出的,使用辅助函数 cbind.fill:(cbind a df with an empty df (cbind.fill?))

cbind.fill <- function(...){
  nm <- list(...) 
  nm <- lapply(nm, as.matrix)
  n <- max(sapply(nm, nrow)) 
  do.call(cbind, lapply(nm, function (x) 
    rbind(x, matrix(, n-nrow(x), ncol(x))))) 
}

#Split df by CAT
df.split <- split(df, df$CAT)

#Apply cbind.fill to make a matrix filled with NA where needed
rawlist <- lapply(df.split, function(x) cbind(as.character(x$CAT), cbind.fill(unlist(x$COUNT), unlist(x$TREAT) ) ))  

#Bind rows and convert matrix to data.frame
df.new <- as.data.frame(do.call(rbind, rawlist))

#Column names
colnames(df.new) <- names(df)

df.new
  CAT COUNT   TREAT
1   A     1 Treat-a
2   A     2 Treat-b
3   A     3    <NA>
4   B     4 Treat-c
5   B     5 Treat-d
6   B  <NA> Treat-e

【讨论】:

    【解决方案2】:

    从您上一个问题扩展my answer,您可以创建另一个函数,我们称之为flattenLong,它结合了“data.table”中的flattenmelt,以获得您想要的输出。

    函数如下所示:

    flattenLong <- function(indt, cols) {
      ob <- setdiff(names(indt), cols)
      x <- flatten(indt, cols, TRUE)
      mv <- lapply(cols, function(y) grep(sprintf("^%s_", y), names(x)))
      setorderv(melt(x, measure.vars = mv, value.name = cols), ob)[]
    }
    

    用法很简单:

    flattenLong(df, c("COUNT", "TREAT"))
    ##    CAT variable COUNT   TREAT
    ## 1:   A        1     1 Treat-a
    ## 2:   A        2     2 Treat-b
    ## 3:   A        3     3      NA
    ## 4:   B        1     4 Treat-c
    ## 5:   B        2     5 Treat-d
    ## 6:   B        3    NA Treat-e
    

    为方便起见,这里又是flatten 函数:

    flatten <- function(indt, cols, drop = FALSE) {
      require(data.table)
      if (!is.data.table(indt)) indt <- as.data.table(indt)
      x <- unlist(indt[, lapply(.SD, function(x) max(lengths(x))), .SDcols = cols])
      nams <- paste(rep(cols, x), sequence(x), sep = "_")
      indt[, (nams) := unlist(lapply(.SD, transpose), recursive = FALSE), .SDcols = cols]
      if (isTRUE(drop)) {
        indt[, (nams) := unlist(lapply(.SD, transpose), recursive = FALSE), 
             .SDcols = cols][, (cols) := NULL]
      }
      indt[]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-06
      • 2018-11-09
      • 2019-06-18
      • 1970-01-01
      • 2018-09-13
      • 2019-01-24
      • 2021-08-02
      • 1970-01-01
      相关资源
      最近更新 更多