Подтвердить что ты не робот

Lapply с функцией "$"

Скажем, у меня есть список data.frames

dflist <- list(data.frame(a=1:3), data.frame(b=10:12, a=4:6))

Если я хочу извлечь первый столбец из каждого элемента в списке, я могу сделать

lapply(dflist, `[[`, 1)
# [[1]]
# [1] 1 2 3
# 
# [[2]]
# [1] 10 11 12

Почему я не могу использовать функцию "$" тем же способом

lapply(dflist, `$`, "a")
# [[1]]
# NULL
# 
# [[2]]
# NULL

Но они оба работают:

lapply(dflist, function(x) x$a)
`$`(dflist[[1]], "a")

Я понимаю, что в этом случае можно было бы использовать

lapply(dflist, `[[`, "a")

но я работал с объектом S4, который, казалось, не позволял индексировать через [[. Например

library(adegenet)
data(nancycats)
catpop <- genind2genpop(nancycats)
mylist <- list(catpop, catpop)

#works
catpop[[1]]$tab

#doesn't work
lapply(mylist, "$", "tab")
# Error in slot(x, name) : 
#   no slot of name "..." for this object of class "genpop"

#doesn't work
lapply(mylist, "[[", "tab")
# Error in FUN(X[[1L]], ...) : this S4 class is not subsettable
4b9b3361

Ответ 1

В первом примере вы можете просто:

lapply(dflist, `$.data.frame`, "a")

Для второго используйте функцию доступа slot()

lapply(mylist, "slot", "tab")

Я не уверен, почему отправка метода не работает в первом случае, но в разделе Note ?lapply выполняется эта проблема его отправки borked методом для примитивных функций, таких как $:

 Note:

 [...]

 For historical reasons, the calls created by ‘lapply’ are
 unevaluated, and code has been written (e.g., ‘bquote’) that
 relies on this.  This means that the recorded call is always of
 the form ‘FUN(X[[i]], ...)’, with ‘i’ replaced by the current
 (integer or double) index.  This is not normally a problem, but it
 can be if ‘FUN’ uses ‘sys.call’ or ‘match.call’ or if it is a
 primitive function that makes use of the call.  This means that it
 is often safer to call primitive functions with a wrapper, so that
 e.g. ‘lapply(ll, function(x) is.numeric(x))’ is required to ensure
 that method dispatch for ‘is.numeric’ occurs correctly.

Ответ 2

Похоже, что эта проблема имеет больше общего с $ и как обычно ожидает, что некотируемые имена будут вторым параметром, а не строками. Посмотрите на этот пример

dflist <- list(
    data.frame(a=1:3, z=31:33), 
    data.frame(b=10:12, a=4:6, z=31:33)
)
lapply(dflist, 
    function(x, z) {
        print(paste("z:",z)); 
        `$`(x,z)
    }, 
    z="a"
)

Мы видим результаты

[1] "z: a"
[1] "z: a"
[[1]]
[1] 31 32 33

[[2]]
[1] 31 32 33

поэтому значение z устанавливается на "a", но $ не оценивает второй параметр. Поэтому он возвращает столбец "z", а не столбец "a". Это приводит к этому интересному набору результатов

a<-"z"; `$`(dflist[[1]], a)
# [1] 1 2 3
a<-"z"; `$`(dflist[[1]], "z")
# [1] 31 32 33

a<-"z"; `$.data.frame`(dflist[[1]], a)
# [1] 31 32 33
a<-"z"; `$.data.frame`(dflist[[1]], "z")
# [1] 31 32 33

Когда мы вызываем $.data.frame непосредственно, мы обходим стандартную депарафировку, которая встречается в примитиве перед отправкой (что происходит около здесь в источнике).

Добавленный catch с lapply заключается в том, что он передает аргументы функции через механизм .... Например

lapply(dflist, function(x, z) sys.call())
# [[1]]
# FUN(X[[2L]], ...)

# [[2]]
# FUN(X[[2L]], ...)

Это означает, что при вызове $ он делит ... на строку "...". Это объясняет это поведение

dflist<- list(data.frame(a=1:3, "..."=11:13, check.names=F))
lapply(dflist, `$`, "a")
# [[1]]
# [1] 11 12 13

То же самое происходит, когда вы пытаетесь использовать ... самостоятельно

f<-function(x,...) `$`(x, ...); 

f(dflist[[1]], "a");
# [1] 11 12 13
`$`(dflist[[1]], "a")
# [1] 1 2 3