Область действия при создании нового столбца r data.table в функции с использованием :=

Это продолжение вопроса, который я разместил здесь Создание нового столбца r data.table на основе значений в другом столбце и группировка, на что @Frank дал отличный ответ.

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

Мы будем использовать те же данные:

library(data.table)
set.seed(88)
DT <- data.table(date = Sys.Date()-365 + sort(sample(1:100, 10)), 
zip = sample(c("2000", "1150", "3000"),10, replace = TRUE), 
purchaseAmount = sample(1:20, 10))

Вот ответ @Frank:

DT[, new_col := 
  DT[.(zip = zip, d0 = date - 10, d1 = date), on=.(zip, date >= d0, date <= d1), 
    sum(purchaseAmount)
  , by=.EACHI ]$V1
]


DT 

              date  zip purchaseAmount new_col
     1: 2016-01-08 1150              5       5
     2: 2016-01-15 3000             15      15
     3: 2016-02-15 1150             16      16
     4: 2016-02-20 2000             18      18
     5: 2016-03-07 2000             19      19
     6: 2016-03-15 2000             11      30
     7: 2016-03-17 2000              6      36
     8: 2016-04-02 1150             17      17
     9: 2016-04-08 3000              7       7
    10: 2016-04-09 3000             20      27

А теперь собственно проблема, с которой я столкнулся. Я создал следующую функцию, которая позволяет динамически изменять интервал:

sumPreviousPurchases = function(dt, newColName, daysFrom, daysUntil){
    zip = substitute(zip)
    newColName = substitute(newColName)
    dt[, newColName := 
        dt[.(zip = zip, d0 = (date - daysUntil), d1 = (date - daysFrom))
        , on=.(zip, date >= d0, date <= d1), 
        sum(purchaseAmount)
        , by=.EACHI ]$V1
    ]
}

sumPreviousPurchases(DT, prevPurch1to10, 0, 10)

DT

          date  zip purchaseAmount newColName
 1: 2016-02-07 1150              5          5
 2: 2016-02-14 3000             15         15
 3: 2016-03-16 1150             16         16
 4: 2016-03-21 2000             18         18
 5: 2016-04-06 2000             19         19
 6: 2016-04-14 2000             11         30
 7: 2016-04-16 2000              6         36
 8: 2016-05-02 1150             17         17
 9: 2016-05-08 3000              7          7
10: 2016-05-09 3000             20         27

Что меня беспокоит, так это обзор. Функция называет новый столбец newColName независимо от того, что я вставляю в вызове функции. Из чтения я понял, что при вызове имен столбцов data.table в аргументах функции следует использовать substitute()-функцию. Однако здесь это не работает, результат тот же, даже если я оставлю всю строку newColName = substitute(newColName). Я предполагаю, что это потому, что столбца еще не существует, но я не знаю, как решить эту проблему.

В качестве бонуса я хотел бы спросить, есть ли способ динамически именовать столбцы, т.е. в примере, например, «daysFrom_ to_daysUntil», а имя будет «0_to_10»?

----- РЕДАКТИРОВАТЬ ----

Я также сам наткнулся на возможный ответ, несколько похожий на ответ @lmo, используя идею отсюда: http://brooksandrew.github.io/simpleblog/articles/advanced-data-table/#assign-a-column-with--named-with-a-character-object

Наиболее важные отличия в вопросе: я полностью удалил newColName = substitute(newColName) и добавил скобки вокруг (newColName) на dt[, (newColName) :=

sumPreviousPurchases = function(dt, newColName, daysFrom, daysUntil){
    zip = substitute(zip)
    #newColName = substitute(newColName)
    dt[, (newColName) := 
        dt[.(zip = zip, d0 = (date - daysUntil), d1 = (date - daysFrom))
        , on=.(zip, date >= d0, date <= d1), 
        sum(purchaseAmount)
        , by=.EACHI ]$V1
    ]
}

Кроме того, я добавил кавычки в файл "prevPurch1to10".

sumPreviousPurchases(DT, "prevPurch1to10", 0, 10)

и получил ответ

          date  zip purchaseAmount prevPurch1to10
 1: 2016-02-17 1150              7              7
 2: 2016-02-22 3000              8              8
 3: 2016-03-04 1150              2              2
 4: 2016-03-16 2000             14             14
 5: 2016-04-03 2000             11             11
 6: 2016-04-11 3000             12             12
 7: 2016-04-21 1150             17             17
 8: 2016-04-22 3000              3              3
 9: 2016-05-03 2000              9              9
10: 2016-05-11 3000              4              4

Тем не менее, есть еще две следующие странные вещи:

а) substitute() не требуется при добавлении скобок к (newColName). Это почему?

б) кавычки необходимы вокруг "prevPurch1to10". Опять же, почему? Есть ли более data.tableish способ сделать это без кавычек?


person Mantelimies    schedule 02.02.2017    source источник


Ответы (1)


Вы можете использовать замену прямо в задании:

sumPreviousPurchases = function(dt, newColName, daysFrom, daysUntil){
    zip = substitute(zip)
    dt[, substitute(newColName) := 
           dt[.(zip = zip, d0 = (date - daysUntil), d1 = (date - daysFrom))
              , on=.(zip, date >= d0, date <= d1), 
              sum(purchaseAmount)
              , by=.EACHI ]$V1
       ]
}

Тогда попробуйте

sumPreviousPurchases(DT, "prevPurch1to10", 0, 10)

который возвращает

DT
          date  zip purchaseAmount prevPurch1to10
 1: 2016-02-07 1150              5              5
 2: 2016-02-14 3000             15             15
 3: 2016-03-16 1150             16             16
 4: 2016-03-21 2000             18             18
 5: 2016-04-06 2000             19             19
 6: 2016-04-14 2000             11             30
 7: 2016-04-16 2000              6             36
 8: 2016-05-02 1150             17             17
 9: 2016-05-08 3000              7              7
10: 2016-05-09 3000             20             27

Примечания:

  • Скобки в вашем решении () принудительно оценивают аргумент. Это реализовано в базе R и является общей техникой для многих языков программирования, основанной на математической концепции порядка операций. (сначала оцените объекты в скобках, затем возведите в степень и т. д.). Использование substitute делает замену явной, возможно, для облегчения чтения.

  • Часто аргумент функции, которая будет определять будущий объект, например prevPurch1to10, требует кавычек, поскольку объект не существует до вызова функции. Использование такого аргумента без кавычек обычно приводит к ошибке: «объект X не найден».

person lmo    schedule 02.02.2017
comment
Привет, спасибо за это. Я также добавил свой собственный ответ, потому что substitute кажется не нужным, и вы также добавили кавычки вокруг "prevPurch1to10", необходимость которых я не понимаю. - person Mantelimies; 02.02.2017