Эффективно распаковать список кадров данных различной длины - PullRequest
0 голосов
/ 11 июня 2019

У меня есть список, включающий большое количество временных рядов временных рядов, охватывающих разные годы.Я использую lapply, чтобы успешно распаковать список, но я хочу что-то быстрее.Одна сложность заключается в том, что некоторые кадры данных пусты, но я хочу вести их учет, чтобы после распаковки я мог cbind правильно метить данные.

Я синхронизирую свою текущую попытку с примерами данных, используя microbenchmark.

library("plyr")
library("microbenchmark")

# Create some example dataframes of varying length.
ts1 <- data.frame(year=2004:2019, value=14:29)
ts2 <- data.frame(year=2006:2018, value=18:6)
ts3 <- NULL
ts4 <- data.frame(year=2005:2017, value=25:37)
ts5 <- NULL

# Combine the example dataframes into a list.
ts_data <- list(ts1, ts2, ts3, ts4, ts5)

# Function to unpack time series data if not empty and return a dataframe.
fn_unpack_ts <- function(ts) {
  if (!plyr::empty(ts)) {  
    df <- t(ts$value)
    colnames(df) <- ts$year
  } else {
    df <- NA
  }
  return(as.data.frame(df))
}

# Use lapply to run through each dataframe.
microbenchmark::microbenchmark(
l_ts <- Reduce(plyr::rbind.fill, lapply(ts_data, fn_unpack_ts)), times=100
)

# Tidy up the final dataframe.
l_ts$df <- NULL

Требуемый выходной фрейм данных выглядит следующим образом:

> l_ts
   2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
 1   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29
 2   NA   NA   18   17   16   15   14   13   12   11   10    9    8    7    6   NA
 3   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA
 4   NA   25   26   27   28   29   30   31   32   33   34   35   36   37   NA   NA
 5   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA

И мои собственные временные рамки для100 повторений в миллисекундах - это:

           min       lq     mean   median       uq     max neval
l_ts  2.844698 3.024238 3.283312 3.093525 3.357831 9.21223   100

Я хотел бы знать, существует ли более эффективный подход к распаковке моих примеров данных.Я подозреваю, что это повлечет за собой не возвращение кадра данных каждый раз, но это единственный способ заставить его работать при использовании rbind.fill для обработки разного количества лет.

ОБНОВЛЕНИЕ

Очень хорошие решения, предложенные #A.Сулиман и # Уве.Мое тестирование с реальными данными, содержащим 1098 строк и 10 повторений, показывает:

expr                                                               mean (ms)     
Reduce(rbind.fill, lapply(ts_data, fn_unpack_ts))                  1326.2   
purrr::map_dfr(ts_data, fn_unpack_ts)                               133.7 
dcast(rbindlist(ts_data, idcol="id")[CJ(id=seq_along(ts_data),
  year, unique=TRUE), on=.(id, year)], id~year)                      15.0

... поэтому я объявляю rbindlist подход победителем.

Ответы [ 2 ]

2 голосов
/ 11 июня 2019

Вот один вариант, используя purrr::map_dfr

microbenchmark::microbenchmark(
  l_ts <- purrr::map_dfr(ts_data, fn_unpack_ts), unit = "ms",times=100
)

Unit: milliseconds
                                  expr      min        lq      mean    median       uq      max neval
l_ts <- map_dfr(ts_data, fn_unpack_ts) 0.367476 0.3829495 0.4368147 0.3925645 0.417654 1.181447   100
1 голос
/ 11 июня 2019

Вот альтернативный подход, который использует rbindlist() для объединения кадров данных, перекрестное объединение CJ() для завершения идентификаторов пропущенных временных последовательностей и dcast() для преобразования из длинного в широкий формат:

library(data.table)
dcast(rbindlist(ts_data, idcol = "id")[CJ(id = seq_along(ts_data), year, unique = TRUE), on = .(id, year)], id ~ year)
   id 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
1:  1   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29
2:  2   NA   NA   18   17   16   15   14   13   12   11   10    9    8    7    6   NA
3:  3   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA
4:  4   NA   25   26   27   28   29   30   31   32   33   34   35   36   37   NA   NA
5:  5   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA

У меня , а не , включены временные параметры эталонного теста для данного очень небольшого выборочного набора данных, поскольку это будет только измерять накладные расходы на вызовы функций. Значительный эталонный тест потребовал бы изучения сроков всех решений на одном компьютере при различных (малых и больших) размерах задач.

...