Использование: = в data.table с пастой()
Я начал использовать data.table
для большой модели населения. До сих пор я был впечатлен, потому что использование структуры data.table уменьшает время выполнения моделирования примерно на 30%. Я пытаюсь еще больше оптимизировать свой код и включил упрощенный пример. Мои два вопроса:
- Можно ли использовать оператор
:=
с этим кодом?
- Будет ли использовать оператор
:=
быстрее (хотя, если я смогу ответить на свой первый вопрос, я должен ответить на мой вопрос 2!)?
Я использую R версии 3.1.2 на машине под управлением Windows 7 с data.table
версии 1.9.4.
Вот мой воспроизводимый пример:
library(data.table)
## Create example table and set initial conditions
nYears = 10
exampleTable = data.table(Site = paste("Site", 1:3))
exampleTable[ , growthRate := c(1.1, 1.2, 1.3), ]
exampleTable[ , c(paste("popYears", 0:nYears, sep = "")) := 0, ]
exampleTable[ , "popYears0" := c(10, 12, 13)] # set the initial population size
for(yearIndex in 0:(nYears - 1)){
exampleTable[[paste("popYears", yearIndex + 1, sep = "")]] <-
exampleTable[[paste("popYears", yearIndex, sep = "")]] *
exampleTable[, growthRate]
}
Я пытаюсь сделать что-то вроде:
for(yearIndex in 0:(nYears - 1)){
exampleTable[ , paste("popYears", yearIndex + 1, sep = "") :=
paste("popYears", yearIndex, sep = "") * growthRate, ]
}
Однако это не работает, потому что с помощью data.table
вставка не работает, например:
exampleTable[ , paste("popYears", yearIndex + 1, sep = "")]
# [1] "popYears10"
Я просмотрел документацию data.table. В разделе 2.9 часто задаваемых вопросов используется cat
, но это дает нулевой вывод.
exampleTable[ , cat(paste("popYears", yearIndex + 1, sep = ""))]
# [1] popYears10NULL
Кроме того, я пробовал искать Google и rseek.org, но ничего не нашел. Если у вас нет очевидного условия поиска, я был бы признателен за подсказку для поиска. Я всегда находил поиск операторов R жестким, потому что поисковые системы не любят символы (например, ":=
" ), а "R" может быть неопределенным.
Ответы
Ответ 1
## Start with 1st three columns of example data
dt <- exampleTable[,1:3,with=FALSE]
## Run for 1st five years
nYears <- 5
for(ii in seq_len(nYears)-1) {
y0 <- as.symbol(paste0("popYears", ii))
y1 <- paste0("popYears", ii+1)
dt[, (y1) := eval(y0)*growthRate]
}
## Check that it worked
dt
# Site growthRate popYears0 popYears1 popYears2 popYears3 popYears4 popYears5
#1: Site 1 1.1 10 11.0 12.10 13.310 14.6410 16.10510
#2: Site 2 1.2 12 14.4 17.28 20.736 24.8832 29.85984
#3: Site 3 1.3 13 16.9 21.97 28.561 37.1293 48.26809
Edit:
Поскольку возможность ускорения этого использования с использованием set()
продолжает появляться в комментариях, я поставлю этот дополнительный параметр там.
nYears <- 5
## Things that only need to be calculated once can be taken out of the loop
r <- dt[["growthRate"]]
yy <- paste0("popYears", seq_len(nYears+1)-1)
## A loop using set() and data.table nice compact syntax
for(ii in seq_len(nYears)) {
set(dt, , yy[ii+1], r*dt[[yy[ii]]])
}
## Check results
dt
# Site growthRate popYears0 popYears1 popYears2 popYears3 popYears4 popYears5
#1: Site 1 1.1 10 11.0 12.10 13.310 14.6410 16.10510
#2: Site 2 1.2 12 14.4 17.28 20.736 24.8832 29.85984
#3: Site 3 1.3 13 16.9 21.97 28.561 37.1293 48.26809
Ответ 2
Борьба с именами столбцов - сильный индикатор того, что широкий формат, вероятно, не лучший выбор для данной проблемы. Поэтому я предлагаю сделать вычисления в длинной форме и, наконец, переделать результат из длинного в широкий формат.
nYears = 10
params = data.table(Site = paste("Site", 1:3),
growthRate = c(1.1, 1.2, 1.3),
pop = c(10, 12, 13))
long <- params[CJ(Site = Site, Year = 0:nYears), on = "Site"][
, growth := cumprod(shift(growthRate, fill = 1)), by = Site][
, pop := pop * growth][]
dcast(long, Site + growthRate ~ sprintf("popYears%02i", Year), value.var = "pop")
Site growthRate popYears 0 popYears 1 popYears 2 popYears 3 popYears 4 popYears 5 popYears 6 popYears 7 popYears 8 popYears 9 popYears10
1: Site 1 1.1 10 11.0 12.10 13.310 14.6410 16.10510 17.71561 19.48717 21.43589 23.57948 25.93742
2: Site 2 1.2 12 14.4 17.28 20.736 24.8832 29.85984 35.83181 42.99817 51.59780 61.91736 74.30084
3: Site 3 1.3 13 16.9 21.97 28.561 37.1293 48.26809 62.74852 81.57307 106.04499 137.85849 179.21604
Объяснение
Сначала параметры расширяются до 11 лет (включая год 0) с использованием функции перекрестного соединения CJ()
и последующего правого соединения на Site
:
params[CJ(Site = Site, Year = 0:nYears), on = "Site"]
Site growthRate pop Year
1: Site 1 1.1 10 0
2: Site 1 1.1 10 1
3: Site 1 1.1 10 2
4: Site 1 1.1 10 3
5: Site 1 1.1 10 4
6: Site 1 1.1 10 5
7: Site 1 1.1 10 6
8: Site 1 1.1 10 7
9: Site 1 1.1 10 8
10: Site 1 1.1 10 9
11: Site 1 1.1 10 10
12: Site 2 1.2 12 0
13: Site 2 1.2 12 1
14: Site 2 1.2 12 2
15: Site 2 1.2 12 3
16: Site 2 1.2 12 4
17: Site 2 1.2 12 5
18: Site 2 1.2 12 6
19: Site 2 1.2 12 7
20: Site 2 1.2 12 8
21: Site 2 1.2 12 9
22: Site 2 1.2 12 10
23: Site 3 1.3 13 0
24: Site 3 1.3 13 1
25: Site 3 1.3 13 2
26: Site 3 1.3 13 3
27: Site 3 1.3 13 4
28: Site 3 1.3 13 5
29: Site 3 1.3 13 6
30: Site 3 1.3 13 7
31: Site 3 1.3 13 8
32: Site 3 1.3 13 9
33: Site 3 1.3 13 10
Site growthRate pop Year
Затем рост вычисляется по сдвинутым темпам роста, используя кумулятивную функцию продукта cumprod()
отдельно для каждого Site
. Сдвиг требуется пропустить начальный год для каждого Site
. Затем население вычисляется путем умножения на начальную популяцию.
Наконец, таблица данных преобразуется из длинного в широкий формат с помощью dcast()
. Заголовки столбцов создаются "на лету", используя sprintf()
, чтобы обеспечить правильный порядок столбцов.