Когда из-за очень больших данных вычисления будут занимать много времени и, следовательно, мы не хотим, чтобы они терпели крах, было бы полезно заранее узнать, какой метод изменения формы использовать.
В последнее время методы преобразования данных получили дальнейшее развитие в отношении производительности, например data.table::dcast и tidyr::spread. Особенно dcast.data.table кажется, задает тон [1], [2], [3], [4]. Это делает другие методы reshape базы R в бенчмарках устаревшими и почти бесполезными [5].
теория
Тем не менее, я слышал, что reshape прежнему непобедимо, когда дело доходит до очень больших наборов данных (вероятно, превышающих объем ОЗУ), потому что это единственный метод, который может их обрабатывать, и поэтому он все еще имеет право на существование. Соответствующий отчет о reshape2::dcast с использованием reshape2::dcast поддерживает эту точку [6]. По крайней мере, одна ссылка дает подсказку, что reshape() действительно может иметь преимущества перед reshape2::dcast для действительно "больших вещей" [7].
метод
В поисках доказательств этого я подумал, что стоит потратить время на некоторые исследования. Так что я сделал тест с имитацией данных разного размера, которые все больше и больше исчерпать RAM сравнить reshape, dcast, dcast.data.table и spread. Я посмотрел на простые наборы данных с тремя столбцами, с разным количеством строк, чтобы получить разные размеры (см. Код в самом низу).
> head(df1, 3)
id tms y
1 1 1970-01-01 01:00:01 0.7463622
2 2 1970-01-01 01:00:01 0.1417795
3 3 1970-01-01 01:00:01 0.6993089
Объем оперативной памяти составлял всего 8 ГБ, что было моим порогом для моделирования "очень больших" наборов данных. Чтобы сохранить разумное время для расчетов, я сделал только 3 измерения для каждого метода и сосредоточился на изменении формы с длинного на широкий.
Результаты
unit: seconds
expr min lq mean median uq max neval size.gb size.ram
1 dcast.DT NA NA NA NA NA NA 3 8.00 1.000
2 dcast NA NA NA NA NA NA 3 8.00 1.000
3 tidyr NA NA NA NA NA NA 3 8.00 1.000
4 reshape 490988.37 492843.94 494699.51 495153.48 497236.03 499772.56 3 8.00 1.000
5 dcast.DT 3288.04 4445.77 5279.91 5466.31 6375.63 10485.21 3 4.00 0.500
6 dcast 5151.06 5888.20 6625.35 6237.78 6781.14 6936.93 3 4.00 0.500
7 tidyr 5757.26 6398.54 7039.83 6653.28 7101.28 7162.74 3 4.00 0.500
8 reshape 85982.58 87583.60 89184.62 88817.98 90235.68 91286.74 3 4.00 0.500
9 dcast.DT 2.18 2.18 2.18 2.18 2.18 2.18 3 0.20 0.025
10 tidyr 3.19 3.24 3.37 3.29 3.46 3.63 3 0.20 0.025
11 dcast 3.46 3.49 3.57 3.52 3.63 3.74 3 0.20 0.025
12 reshape 277.01 277.53 277.83 278.05 278.24 278.42 3 0.20 0.025
13 dcast.DT 0.18 0.18 0.18 0.18 0.18 0.18 3 0.02 0.002
14 dcast 0.34 0.34 0.35 0.34 0.36 0.37 3 0.02 0.002
15 tidyr 0.37 0.39 0.42 0.41 0.44 0.48 3 0.02 0.002
16 reshape 29.22 29.37 29.49 29.53 29.63 29.74 3 0.02 0.002
(Примечание. Тесты проводились на вторичном MacBook Pro с процессором Intel Core i5 2,5 ГГц и 8 ГБ оперативной памяти DDR3 1600 МГц.)
Очевидно, dcast.data.table кажется всегда самым быстрым. Как и ожидалось, все упакованные подходы потерпели неудачу с очень большими наборами данных, вероятно, потому что вычисления тогда превысили объем оперативной памяти
Error: vector memory exhausted (limit reached?)
Timing stopped at: 1.597e+04 1.864e+04 5.254e+04
Только reshape обрабатывает все размеры данных, хотя и очень медленно.
Заключение
Такие методы пакета, как dcast и spread, неоценимы для наборов данных, которые меньше ОЗУ или чьи вычисления не исчерпывают ОЗУ. Если набор данных больше, чем объем оперативной памяти, методы пакета не будут выполнены, и мы должны использовать reshape.
Вопрос
Можем ли мы сделать вывод, как это? Может кто-то немного прояснить, почему data.table/reshape и tidyr терпят неудачу и каковы их методологические различия, чтобы reshape? Является ли единственная альтернатива для обширных данных надежным, но медленным reshape лошади? Что мы можем ожидать от методов, которые здесь не тестировались как tapply, unstack и xtabs [8], [9]?
Или, короче говоря: какая более быстрая альтернатива существует, если что-то кроме reshape не удается?
Данные/Код
# 8GB version
n <- 1e3
t1 <- 2.15e5 # approx. 8GB, vary to increasingly exceed RAM
df1 <- expand.grid(id=1:n, tms=as.POSIXct(1:t1, origin="1970-01-01"))
df1$y <- rnorm(nrow(df1))
dim(df1)
# [1] 450000000 3
> head(df1, 3)
id tms y
1 1 1970-01-01 01:00:01 0.7463622
2 2 1970-01-01 01:00:01 0.1417795
3 3 1970-01-01 01:00:01 0.6993089
object.size(df1)
# 9039666760 bytes
library(data.table)
DT1 <- as.data.table(df1)
library(microbenchmark)
library(tidyr)
# NOTE: this runs for quite a while!
mbk <- microbenchmark(reshape=reshape(df1, idvar="tms", timevar="id", direction="wide"),
dcast=dcast(df1, tms ~ id, value.var="y"),
dcast.dt=dcast(DT1, tms ~ id, value.var="y"),
tidyr=spread(df1, id, y),
times=3L)
