Почему мне нужно оборачивать `get` в фиктивную функцию внутри вызова J `lapply`?

Я хочу обрабатывать столбцы по таким критериям, как сопоставление класса или общего шаблона через grep.

Моя первая попытка не сработала:

require(data.table)
test.table <- data.table(a=1:10,ab=1:10,b=101:110)
##this does not work and hangs on my machine
test.table[,lapply(names(test.table)[grep("a",names(test.table))], get)]

Рикардо Сапорта отмечает в ответе, что вы можете использовать эту конструкцию, но вы должны обернуть get в фиктивную функцию:

##this works
test.table[,lapply(names(test.table)[grep("a",names(test.table))], function(x) get(x))]

Зачем вам анонимная функция?

(Предпочтительный/более чистый метод — через .SDcols:)

test.table[,.SD,.SDcols=grep("a",names(test.table))]
test.table[, grep("a", names(test.table), with = FALSE]

person Blue Magister    schedule 05.08.2013    source источник
comment
get — это стандартный метод преобразования символьного значения в языковой объект.   -  person IRTFM    schedule 05.08.2013
comment
Примечание. grep имеет параметр value=TRUE. Можно было просто написать: lapply(grep("a", names(test.table), value=TRUE), get)   -  person Arun    schedule 06.08.2013


Ответы (3)


Это функция lapply, а не data.table. Из документации lapply:

По историческим причинам вызовы, созданные lapply, не оцениваются, и был написан код (например, bquote), который полагается на это. Это означает, что записанный вызов всегда имеет форму FUN(X[[0L]], ...), где 0L заменен текущим целочисленным индексом. Обычно это не проблема, но может быть, если FUN использует sys.call или match.call или если это примитивная функция, использующая вызов. Это означает, что часто безопаснее вызывать примитивные функции с помощью оболочки, так что, например. lapply(ll, function(x) is.numeric(x)) требуется в R 2.7.1, чтобы обеспечить правильную диспетчеризацию метода для is.numeric.

Обновите комментарии @Hadley и @DWin:

EE <- new.env()
EE$var1 <- "I am var1 in EE"
EE$var2 <- "I am var2 in EE"

## Calling get directly
with(EE, lapply(c("var1", "var2"), get))
Error in FUN(c("var1", "var2")[[1L]], ...) : object 'var1' not found

## Calling get via an anonymous function
with(EE, lapply(c("var1", "var2"), function(x) get(x)))
[[1]]
[1] "I am var1 in EE"

[[2]]
[1] "I am var2 in EE"

with(EE, lapply(c("var1", "var2"), rm))
Error in FUN(c("var1", "var2")[[1L]], ...) : 
  ... must contain names or character strings

with(EE, lapply(c("var1", "var2"), function(x) rm(x)))
[[1]]
NULL

[[2]]
NULL

# var1 & var2 have now been removed
EE
<environment: 0x1154d0060>
person Ricardo Saporta    schedule 05.08.2013
comment
Но get не примитивен и не использует sys.call или match.call. - person hadley; 07.08.2013
comment
@hadley, вы правы в том, что get не является ни одной из явно описанных вещей. Может быть, в документации следует читать ..или если это примитивная или внутренняя функция, использующая вызов? - person Ricardo Saporta; 30.10.2013
comment
Я думаю, что это действительно связано с причудой data.table, а не get или lapply. Обратите внимание, что если test.table является фреймом данных, то ни один из примеров кода OP не работает. Т.е. не имеет значения, если вы обернете get в анонимную функцию. - person Hong Ooi; 30.10.2013
comment
@Hong Ooi, поскольку аргумент j в [.data.table ожидает выражение, тогда как в [.data.frame j ожидает, какие элементы извлекать, то да, это особенность синтаксиса. Однако, что касается OP и почему для get необходима фиктивная функция, это требование lapply практически в любом контексте (где не используется аргумент envir= для get). - person Ricardo Saporta; 30.10.2013
comment
Конечно, я знаю об этой проблеме с примитивными функциями. Просто в данном случае это кажется неуместным. Кроме того, в вашем примере вам также не нужна анонимная функция: lapply(c("var1", "var2"), get, EE) - person Hong Ooi; 30.10.2013
comment
Ни в том, ни в другом случае анонимная функция не необходима. Отсюда и замечание в скобках в конце моего последнего комментария :) - person Ricardo Saporta; 30.10.2013
comment
@HongOoi, я не уверен, что это не имеет значения? Обновленный пример не имеет ничего общего с data.table, но дает точно такой же эффект. - person Ricardo Saporta; 30.10.2013

Хотя @Ricardo прав в том, что безопаснее обертывать примитивы или функции, которые полагаются на диспетчеризацию методов, внутри оболочки, здесь мы можем избежать этого, установив правильный environment для get, в котором нужно искать. Хитрость с lapply состоит в том, чтобы использовать sys.parent(n) (в этом случае n = 0 будет работать) для получения соответствующих вызывающих сред.

test.table[,lapply(grep('a',names(test.table),value=TRUE), 
                    get, envir = sys.parent(0))]

(Дополнительную информацию можно найти здесь Использование get inside lapply внутри функции)

person mnel    schedule 05.08.2013

Это только потому, что data.table оценивает j() expression (проще говоря, все после первой запятой в DT[,...]) как фактическое выражение. Таким образом, DT[,"Column1"] возвращает "Column1", так же как with(DT, "Column1") возвращает "Column1". Это в часто задаваемых вопросах таблицы данных.

Если вы хотите, вы можете сделать:

DT[,names(test.table),with=F]
person Señor O    schedule 05.08.2013
comment
Я не понимаю, как это отвечает на вопрос...? - person eddi; 05.08.2013
comment
Вопрос не имеет ничего общего с get или lapply. Это связано с тем фактом, что объект-символ оценивается как объект-символ в data.table. - person Señor O; 05.08.2013
comment
извините, сеньор, pero это имеет прямое отношение к get и lapply :) Проблема не в том, является ли j выражением или нет. Вопрос в том, почему lapply("Column1", function(x) get(x)) находит Column1, а lapply("Column1", get) нет. - person Ricardo Saporta; 05.08.2013
comment
^ Да, я неправильно понял вопрос - person Señor O; 05.08.2013