R: Как агрегировать список фреймов данных по одному столбцу, объединяя все столбцы одновременно - PullRequest
0 голосов
/ 17 октября 2018

Учитывая список фреймов данных, например:

[[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 имеют сотни столбцов, поэтому функция не может указать, какие столбцы будут агрегировать, вместо этого она должна применяться ко всем столбцам

Спасибозаранее

Ответы [ 2 ]

0 голосов
/ 17 октября 2018
> 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.

0 голосов
/ 17 октября 2018

Мы можем использовать 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)

Степень, в которой это поможет, полностью зависит от того, сколько повторений идентификаторов имеется в каждом фрейме данных,Если это по-прежнему не работает, то следующей попыткой будет использование 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 Невозможно выделить вектор размером n для дополнительных советов (таких как использование компьютера с большим объемом памяти или использование пакетов, позволяющих выполнять вычисления вне памяти).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...