Я пытаюсь имитировать новый набор данных из двух меньших наборов данных. Для меня важно сохранить маргинальный рассчитывается из этих меньших наборов данных в конечном наборе данных. Надеюсь, этот воспроизводимый пример должен объяснить, что Я имею в виду.
Создание поддельных данных
library(data.table) # 1.10.5
set.seed(123)
meanVal <- 40
demoDat
Здесь я имитирую некоторые возрастные и гендерные данные. Каждое место всегда будет иметь 2 уровня пола и 100 уровней возраста.
demoDat <- CJ(with(CJ(letters,letters[1:5]), paste0(V1,V2)), c("M","F"), 0:99)
setnames(demoDat, c("Location","Gender","Age"))
demoDat[, Val := rpois(.N, meanVal)]
Location Gender Age Val
1: aa F 0 36
2: aa F 1 47
3: aa F 2 29
---
25998: ze M 97 45
25999: ze M 98 38
26000: ze M 99 39
timeDat
Этот код имитирует измерение временных данных. В этом случае даты разворачиваются по неделям, но фактические данные не должны придерживаться этой однородности. Неделя могут отсутствовать.
timeDat <- with(demoDat, CJ(unique(Location), seq(from=as.Date("2016-01-01"),by=7,length.out = 52)))
setnames(timeDat, c("Location","Date"))
totals <- demoDat[, .(Val=sum(Val)), by=.(Location)]
timeDat[totals, Val := rmultinom(1:.N, i.Val, prob=rep(1,.N)), by=.EACHI,on=.(Location)]
Location Date Val
1: aa 2016-01-01 176
2: aa 2016-01-08 143
3: aa 2016-01-15 143
---
6758: ze 2016-12-09 165
6759: ze 2016-12-16 142
6760: ze 2016-12-23 156
Быстрое согласование
Каждое место должно содержать столбец Val
, который будет таким же, как в наборах demoDat
, так и timeDat
.
timeDat[, sum(Val), by=.(Location)][order(-V1)][1:5]
# Location V1
# 1: jb 8229
# 2: xb 8223
# 3: ad 8179
# 4: nc 8176
# 5: gd 8173
demoDat[, sum(Val), by=.(Location)][order(-V1)][1:5]
# Location V1
# 1: jb 8229
# 2: xb 8223
# 3: ad 8179
# 4: nc 8176
# 5: gd 8173
Желаемый окончательный набор данных
Затем я хочу создать набор данных с переменными Age
, Gender
и Date
. Но мне нужно сохранить свои предельные суммы Val
из наборов данных demoDat
и timeDat
.
У меня есть одна стратегия, которая выполняет эту задачу, но она занимает довольно много ОЗУ. Есть ли другая стратегия, которую я могу использовать, которая выполняет расширение внутри каждой группы за раз? Возможно, используя
.EACHI
?
Разверните оба набора данных и слейте
Это дорогая часть операции. Наборы данных расширяются, поэтому количество строк равно sum(Val)
. В случаях, когда sum(Val)
> 500,000,000
, это может быть дорогостоящим. Тем более, что операция повторяется для второго набора данных. Я надеюсь использовать .EACHI
, поэтому расширяются только данные внутри групп, что существенно снижает объем памяти.
library(pryr)
memUsed <- mem_used()
demoDatBig <- demoDat[rep(1:.N, Val), .(Location, Gender, Age, ID=rowid(Location))]
timeDatBig <- timeDat[rep(1:.N, Val), .(Location, Date, ID=rowid(Location))]
demoDatBig[timeDatBig, Date := i.Date, on=.(Location, ID)]
finalBigDat <- demoDatBig[, .(Val=.N), by=.(Location, Gender, Age, Date)]
mem_used() - memUsed
# 47 MB
Итак, эта операция заняла 47 МБ ОЗУ, но если я увеличу meanVal
, она значительно возрастет. Я бы хотел, чтобы это использовало столько оперативной памяти, как эта операция будет выполняться для той же функции в самой большой группе Location
и ID
. Я думаю, что это возможно с помощью .EACHI
, но я точно не знаю, как это сделать.
Итоговая таблица данных
Location Gender Age Date Val
1: aa F 0 2016-01-01 36
2: aa F 1 2016-01-01 47
3: aa F 2 2016-01-01 29
4: aa F 3 2016-01-01 40
5: aa F 4 2016-01-01 24
---
32430: ze M 96 2016-12-16 7
32431: ze M 96 2016-12-23 34
32432: ze M 97 2016-12-23 45
32433: ze M 98 2016-12-23 38
32434: ze M 99 2016-12-23 39
Решение, надеюсь, пройдет эти тесты
#### Test 1
test1 <- finalBigDat[, .(Val = sum(Val)), by=.(Location, Gender, Age)]
test1[demoDat, ValCheck := i.Val, on=.(Location, Gender, Age)]
test1[Val != ValCheck]
#Empty data.table (0 rows) of 5 cols: Location,Gender,Age,Val,ValCheck
#### Test 2
test2 <- finalBigDat[, .(Val = sum(Val)), by=.(Location, Date)]
test2[timeDat, ValCheck := i.Val, on=.(Location, Date)]
test2[Val != ValCheck]
#Empty data.table (0 rows) of 4 cols: Location,Date,Val,ValCheck
Результаты
Я прошел через оба решения и отслеживал память и системные тайминги для обоих. Оба решения были потрясающими и огромными обновлениями для того, что у меня есть. Решение @swihart масштабирует невероятно хорошо до больших meanVal
, поэтому я выбрал это как принятый ответ. Ответ Хизер поможет в ситуациях, когда meanVal
не такой большой. Частоты большого и малого meanVal
встречаются часто, поэтому мне понадобятся оба.
meanVal Ans Time Mem Rows
1: 40 Mike.Gahan 0.6245470 secs 44.54293 32434
2: 40 Heather Turner 0.6391492 secs 38.65355 1352000
3: 40 swihart 11.1602619 secs 66.97550 1352000
4: 400 Mike.Gahan 2.593275 secs 437.23832 32611
5: 400 Heather Turner 1.303993 secs 38.79871 1352000
6: 400 swihart 11.736836 secs 66.97550 1352000
7: 4000 Mike.Gahan 30.390986 secs 4364.51501 32629
8: 4000 Heather Turner 6.279249 secs 38.79871 1352000
9: 4000 swihart 11.427965 secs 66.97550 1352000
10: 20000 Mike.Gahan -------did not finish----------
11: 20000 Heather Turner 23.78948 secs 36.30617 1352000
12: 20000 swihart 11.53811 secs 66.97550 1352000
13: 30000 Mike.Gahan -------did not finish----------
14: 30000 Heather Turner 537.6459 secs 57.15375 1352000
15: 30000 swihart 11.970013 secs 66.97474 1352000