【问题标题】:Aggregate variables in list of data frames into single data frame将数据框列表中的变量聚合为单个数据框
【发布时间】:2017-07-13 16:14:29
【问题描述】:

我在 R 中执行每个保单的人寿保险估值。每月现金流量预测是按保单执行的,并返回以下格式的数据框(例如):

Policy1 = data.frame(ProjM = 1:200,
                     Cashflow1 = rep(5,200),
                     Cashflow2 = rep(10,200))

我的模型返回一个列表(使用 lapply 和一个执行每个保单现金流预测的函数 - 基于每个保单的各种细节、升级假设和生活意外事件)。我想通过ProjM 汇总所有保单的现金流。下面的代码做我想要的,但寻找一种更有效的内存方式(即不使用rbindlist 函数)。示例数据:

Policy1 = data.frame(ProjM = 1:5,
                     Cashflow1 = rep(5,5),
                     Cashflow2 = rep(10,5))

Policy2 = data.frame(ProjM = 1:3,
                     Cashflow1 = rep(50,3),
                     Cashflow2 = rep(-45,3))

# this is the output containing 35000 data frames:
ListOfDataFrames = list(Policy1 = Policy1, Policy2 = Policy2)

我的代码:

library(data.table)
OneBigDataFrame <- rbindlist(ListOfDataFrames)
MyOutput <- aggregate(. ~ ProjM, data = OneBigDataFrame, FUN = sum)

需要输出:

ProjM Cashflow1 Cashflow2
 1        55       -35
 2        55       -35
 3        55       -35
 4         5        10
 5         5        10

我找过例子,R aggregate list of dataframe对所有数据帧进行聚合,但不要将它们合并为1个数据帧。

【问题讨论】:

  • 由于内存限制,您是否希望 避免 使用您的所有 输入 数据创建一个大 data.frame,但将聚合值放在一个中输出 data.table? BTW:如果您已经在使用data.table,为什么不继续使用data.table 语法进行聚合?
  • 我通过使用矩阵找到了最好的解决方案:ListOfMatrices = list(Policy1 = as.matrix(Policy1), Policy2 = as.matrix(Policy2))OneBigMatrix = do.call(rbind,ListOfMatrices)MyOutputNew = rowsum(OneBigMatrix[,2:3], group = OneBigMatrix[,1])MyOutputNew 没有ProjM,但主要是我想使用Cashflow1Cashflows2

标签: r list dataframe sum aggregate-functions


【解决方案1】:

使用data.table 语法,一步法是先创建大data.table,然后进行聚合:

library(data.table)
OneBigDataFrame <- rbindlist(ListOfDataFrames)
OneBigDataFrame[, lapply(.SD, sum), by = ProjM]

或者,更简洁

rbindlist(ListOfDataFrames)[, lapply(.SD, sum), by = ProjM]
   ProjM Cashflow1 Cashflow2
1:     1        55       -35
2:     2        55       -35
3:     3        55       -35
4:     4         5        10
5:     5         5        10

现在,OP 已请求避免首先创建大 data.table 以节省内存。这需要两步方法,其中为每个 data.table 计算聚合,然后在最后一步聚合为总计:

rbindlist(
  lapply(ListOfDataFrames, 
         function(x) setDT(x)[, lapply(.SD, sum), by = ProjM])
  )[, lapply(.SD, sum), by = ProjM]
   ProjM Cashflow1 Cashflow2
1:     1        55       -35
2:     2        55       -35
3:     3        55       -35
4:     4         5        10
5:     5         5        10

请注意,此处使用setDT() 将 data.frames通过引用强制转换为 data.table,即无需创建额外的副本,从而节省时间和内存。

基准测试

使用基准数据of d.b(包含 10000 个数据帧的列表,每帧 100 行,总共 28.5 Mb)以及迄今为止提供的所有答案:

mb <- microbenchmark::microbenchmark(
  malan = {
    OneBigDataFrame <- rbindlist(test)
    malan <- aggregate(. ~ ProjM, data = OneBigDataFrame, FUN = sum)
  },
  d.b = d.b <- with(data = data.frame(do.call(dplyr::bind_rows, test)),
             expr = aggregate(x = list(Cashflow1 = Cashflow1, Cashflow2 = Cashflow2),
                              by = list(ProjM = ProjM),
                              FUN = sum)),
  a.gore = {
    newagg <- function(dataset) { 
      dataset <- data.table(dataset)
      dataset <- dataset[,lapply(.SD,sum),by=ProjM,.SDcols=c("Cashflow1","Cashflow2")]
      return(dataset)
    }
    a.gore <- newagg(rbindlist(lapply(test,newagg)))
  },
  dt1 = dt1 <- rbindlist(test)[, lapply(.SD, sum), by = ProjM],
  dt2 = dt2 <- rbindlist(
    lapply(test, 
           function(x) setDT(x)[, lapply(.SD, sum), by = ProjM])
  )[, lapply(.SD, sum), by = ProjM],
  times = 5L
)
mb
Unit: milliseconds
   expr         min          lq        mean      median          uq        max neval  cld
  malan   565.43967   583.08300   631.15898   600.45790   605.60237   801.2120     5  b  
    d.b   707.50261   710.31127   719.25591   713.54526   721.26691   743.6535     5  b  
 a.gore 14706.40442 14747.76305 14861.61641 14778.88547 14805.29412 15269.7350     5    d
    dt1    40.10061    40.92474    42.27034    41.55434    42.07951    46.6925     5 a   
    dt2  8806.85039  8846.47519  9144.00399  9295.29432  9319.17251  9452.2275     5   c

最快的解决方案是使用data.table 的一步法,比第二快的方法快 15 倍。令人惊讶的是,两步法data.table 比一步法慢很多。

为了确保所有解决方案返回相同的结果,可以使用以下方法进行检查

all.equal(malan, d.b)
all.equal(malan, as.data.frame(a.gore))
all.equal(malan, as.data.frame(dt1))
all.equal(malan, as.data.frame(dt2))

在所有情况下都返回TRUE

【讨论】:

    【解决方案2】:

    我认为这个解决方案可能是有效的。试试看告诉我

    require(data.table)
    newagg <- function(dataset) { dataset <- data.table(dataset);dataset <- dataset[,lapply(.SD,sum),by=ProjM,.SDcols=c("Cashflow1","Cashflow2")]; return(dataset)}
    newagg(rbindlist(lapply(ListOfDataFrames,newagg)))
    # ProjM Cashflow1 Cashflow2
    # 1:     1        55       -35
    # 2:     2        55       -35
    # 3:     3        55       -35
    # 4:     4         5        10
    # 5:     5         5        10
    

    【讨论】:

      猜你喜欢
      • 2021-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-20
      • 2021-11-14
      • 2017-06-23
      • 2017-10-21
      • 1970-01-01
      相关资源
      最近更新 更多