【问题标题】:How to make groups in a data.frame equal length?如何使 data.frame 中的组等长?
【发布时间】:2013-01-31 08:56:06
【问题描述】:

我有这个data.frame:

df <- data.frame(id=c('A','A','B','B','B','C'), amount=c(45,66,99,34,71,22))

id | amount 
-----------
A  |   45   
A  |   66   
B  |   99
B  |   34 
B  |   71
C  |   22

我需要扩展它,以便 data.frame 中的每个 by 组长度相等(用零填充),如下所示:

id | amount 
-----------
A  |   45   
A  |   66  
A  |   0     <- added 
B  |   99
B  |   34 
B  |   71
C  |   22
C  |   0     <- added 
C  |   0     <- added 

最有效的方法是什么?

注意

对我得到的 实际 100 万行 data.frame 提供的一些解决方案进行基准测试:

             plyr   | data.table  |  unstack
          -----------------------------------
Elapsed:   139.87s  |    0.09s    |   2.00s

【问题讨论】:

  • 再一次,除了这些表之外,如果有代码可以为您提供data.frame(使用dput 或通过复制您的代码),那将是很好的。
  • @Arun,第一行代码不够吗?
  • 啊哈,完全注意到这一点。诚挚的歉意!

标签: r dataframe


【解决方案1】:

一种使用data.table的方式

df <- structure(list(V1 = structure(c(1L, 1L, 2L, 2L, 2L, 3L), 
          .Label = c("A  ", "B  ", "C  "), class = "factor"), 
          V2 = c(45, 66, 99, 34, 71, 22)), 
          .Names = c("V1", "V2"), 
          class = "data.frame", row.names = c(NA, -6L))

require(data.table)
dt <- data.table(df, key="V1")

# get maximum index
idx <- max(dt[, .N, by=V1]$N)

# get final result
dt[, list(V2 = c(V2, rep(0, idx-length(V2)))), by=V1]

#     V1 V2
# 1: A   45
# 2: A   66
# 3: A    0
# 4: B   99
# 5: B   34
# 6: B   71
# 7: C   22
# 8: C    0
# 9: C    0

【讨论】:

    【解决方案2】:

    我确定有一个基本的 R 解决方案,但这里有一个在 plyr 包中使用 ddply 的解决方案

    library(plyr)
    ##N: How many values should be in each group
    N = 3
    ddply(df, "id", summarize, 
          amount = c(amount, rep(0, N-length(amount))))
    

    给予:

      id amount
    1  A     45
    2  A     66
    3  A      0
    4  B     99
    5  B     34
    6  B     71
    7  C     22
    8  C      0
    9  C      0
    

    【讨论】:

    • 只要N 是最大值就不需要max 了吗?
    【解决方案3】:

    这是使用 unstackstack 在基础 R 中的另一种方式。

    # ensure character id col
    df <- transform(df, id=as.character(id))
    # break into a list by id
    u <- unstack(df, amount ~ id)
    # get max length
    max.len <- max(sapply(u, length))
    # pad the short ones with 0s
    filled <- lapply(u, function(x) c(x, numeric(max.len - length(x))))
    # recombine into data.frame
    stack(filled)
    
    #   values ind
    # 1     45   A
    # 2     66   A
    # 3      0   A
    # 4     99   B
    # 5     34   B
    # 6     71   B
    # 7     22   C
    # 8      0   C
    # 9      0   C
    

    【讨论】:

      【解决方案4】:

      这个怎么样?

      out <- by(df, INDICES = df$id, FUN = function(x, N) {
        x <- droplevels(x)
        lng <- nrow(x)
        dif <- N - lng
        if (dif == 0) return(x)
        make.list <- lapply(1:dif, FUN = function(y) data.frame(id = levels(x$id), amount = 0))
        rbind(x, do.call("rbind", make.list))
        }, N = max(table(df$id))) # N could also be an integer
      do.call("rbind", out)
      
          id amount
      A.1  A     45
      A.2  A     66
      A.3  A      0
      B.3  B     99
      B.4  B     34
      B.5  B     71
      C.6  C     22
      C.2  C      0
      C.3  C      0
      

      【讨论】:

      • 知道了:Error in data.frame(id = levels(x$id), amount = 0) : arguments imply differing number of rows: 0, 1
      • droplevels() 之前添加browser() 并逐步完成函数,看看哪里出错了。我已经尝试过您的示例,并且有效...
      猜你喜欢
      • 2021-07-17
      • 2021-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      相关资源
      最近更新 更多