Использование: = в 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(), чтобы обеспечить правильный порядок столбцов.