【问题标题】:R: How to aggregate a list of dataframes by a single column, aggregating all columns at onceR:如何按单列聚合数据框列表,一次聚合所有列
【发布时间】:2018-10-16 21:30:54
【问题描述】:

给定一个数据框列表,像这样

[[1]]
ID    X1   X2   X3   X4   X5
123   1    2    1    0    4
123   2    4    1    2    2
123   0    0    1    0    2
567   2    3    4    0    2
899   2    3    5    3    5

[[2]]
ID    X1   X2   X3   X4
123   7    1    3    2
452   1    3    6    3
899   2    1    4    2

[[3]]
ID    X1   X2   X3   X4   Y1
123   4    2    5    0    1
567   0    1    2    0    2
567   1    2    1    0    3
101   2    3    7    2    1

我想在所有其他列中按“ID”聚合所有数据帧,这个插图中的结果应该是这样的

new_df
ID    X1   X2   X3   X4   X5   Y1
123   14   9    11   2    8    1
567   3    6    7    0    2    5    
899   4    4    9    5    5    0
452   1    3    6    3    0    0
101   2    3    7    2    0    1

这只是一个说明,原始列表有 51 个 dfs,原始 dfs 有数百列,因此该函数无法指定哪些列将聚合,而是应该应用于所有列

提前致谢

【问题讨论】:

  • 如果错误发生在bind_rows ...也许data.table::rbindlist 也许能够做到(我不太有信心)...也许你的最好的办法是将它们标准化(相同的列名,这意味着您需要“填充”一些空列),将它们转储到单个文件中,然后再试一次。列有多不同?也就是说,在您提供的示例中,从 1 到 2 缺少一列,然后在 3 中添加一个新列......差异的数量相对较小(1s 或 2s)还是差异很大?
  • data.table::rbindlist 不起作用。列非常不同,每个 df 中大约有 22-40 列,并且它们都彼此不同。重复的 ID 是什么
  • 现在成功了!我单独聚合了列表中的每个 DF,因为原始列表中的列彼此不同,然后将所有 df 与 reduce 合并

标签: r list aggregate


【解决方案1】:

我们可以使用dplyr::summarize_all

# some sample data (different from yours, just to illustrate)
df_list = list(
  data.frame(ID = c(123, 123, 234), x1 = c(1, 2, 3), x2 = 2:4),
  data.frame(ID = c(123, 123, 234), x1 = c(1, 2, 3), x2 = 2:4, x3 = 4:6),
  data.frame(ID = c(123, 123), x1 = c(1, 2), x3 = 2:3)
)

library(dplyr)
bind_rows(df_list) %>%
  group_by(ID) %>%
  summarize_all("sum", na.rm = TRUE)
# # A tibble: 2 x 4
#      ID    x1    x2    x3
#   <dbl> <dbl> <int> <int>
# 1   123     9    10    14
# 2   234     6     8     6

编辑:由于您遇到内存限制,您可以在dplyr 中做的最好的事情是单独聚合每个数据帧,从而减小它的大小,然后再次组合和聚合。我建议使用简单的for 循环方法来避免尝试一次复制所有数据:

for (i in seq_along(df_list)) {
  df_list[[i]] = df_list[[i]] %>%
    group_by(ID) %>%
    summarize_all("sum", na.rm = TRUE)
}
# Then use the code from above
result = bind_rows(df_list) %>%
  group_by(ID) %>%
  summarize_all("sum", na.rm = TRUE)

这在多大程度上会有所帮助,完全取决于每个数据帧中的 ID 重复次数。如果这仍然不起作用,那么接下来尝试使用data.table,它允许就地修改:

library(data.table)

for (i in seq_along(df_list)) {
  setDT(df_list[[i]]) # convert data frames to data.tables
  # pre-aggregate to reduce size
  df_list[[i]] = df_list[[i]][ , lapply(.SD, sum, na.rm = TRUE), by = .(ID)]  
}

# combine and aggregate
big_dt = rbindlist(df_list, fill = TRUE)
big_dt = big_dt[ , lapply(.SD, sum, na.rm = TRUE), by = .(ID)]

data.table 解决方案的内存效率应该很高。如果您仍然遇到内存问题,请确保您的工作区尽可能空,并且您也没有其他应用程序占用内存。如果这不起作用,请转到 R-FAQ Cannot allocate vector of size n 以获取更多提示(例如使用内存更大的计算机或使用允许内存不足计算的软件包)。

【讨论】:

  • 非常感谢您的回答。不幸的是,它没有工作,由于内存,给出错误:错误:无法分配大小为 18.2 Mb 的向量
【解决方案2】:
> microbenchmark(
+     test1 <- df_list %>% 
+     ldply %>% 
+     group_by(ID) %>% 
+     summarize_all('sum', na.rm=TRUE), 
+     
+     test2 <- df_list %>% 
+         ldply %>% 
+         ddply(.(ID), summarize, 
+               x1 = sum(x1, na.rm=TRUE), 
+               x2 = sum(x2, na.rm=TRUE), 
+               x3 = sum(x3, na.rm=TRUE)), 
+     
+     test3 <- df_list %>% 
+         bind_rows %>% 
+         group_by(ID) %>% 
+         summarize_all('sum', na.rm=TRUE), 
+     
+     test4 <- df_list %>% 
+         bind_rows %>% 
+         ddply(.(ID), summarize, 
+               x1 = sum(x1, na.rm=TRUE), 
+               x2 = sum(x2, na.rm=TRUE), 
+               x3 = sum(x3, na.rm=TRUE)), 
+     
+     test5 <- rbindlist(df_list, fill = TRUE) %>% 
+         .[ , lapply(.SD, sum, na.rm = TRUE), by = .(ID)]
+     )
Unit: milliseconds

  expr
  test1 <- df_list %>% ldply %>% group_by(ID) %>% summarize_all("sum", na.rm = TRUE)
  test2 <- df_list %>% ldply %>% ddply(.(ID), summarize, x1 = sum(x1, na.rm = TRUE), x2 = sum(x2, na.rm = TRUE), x3 = sum(x3, na.rm = TRUE))
  test3 <- df_list %>% bind_rows %>% group_by(ID) %>% summarize_all("sum", na.rm = TRUE)
  test4 <- df_list %>% bind_rows %>% ddply(.(ID), summarize, x1 = sum(x1, na.rm = TRUE), x2 = sum(x2, na.rm = TRUE), x3 = sum(x3, na.rm = TRUE))
  test5 <- rbindlist(df_list, fill = TRUE) %>% .[, lapply(.SD, sum, na.rm = TRUE), by = .(ID)]
          min       lq     mean   median       uq       max neval cld
     2.798383 2.977228 3.147271 3.130156 3.279235  3.840657   100   c
     2.797870 3.034704 3.408260 3.220733 3.340305 22.279300   100   c
     2.146642 2.309835 2.633888 2.429919 2.563603 20.407723   100  b 
     2.254924 2.431202 2.598176 2.533325 2.688306  3.581499   100  b 
     1.209571 1.331708 1.430511 1.423055 1.508243  2.172301   100 a

这里我比较@Gregor提供的答案的效率。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    • 2020-07-20
    • 2021-11-14
    • 1970-01-01
    相关资源
    最近更新 更多