【问题标题】:lapply with "$" functionlapply 与 "$" 函数
【发布时间】:2018-11-03 18:56:15
【问题描述】:

假设我有一个 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

【问题讨论】:

  • 这个作品lapply(dflist, function(x) "$"(x, "a"))
  • 好问题。仅供参考,可以通过methods("$",dflist[[1]])找到答案
  • 好吧@Frank,并不是我不知道$.data.frame 的存在,我只是很惊讶这个问题是由方法调度引起的。我想不出在许多其他情况下您必须显式调用一种形式的泛型函数。
  • @MrFlick -- 我支持你。它必须(?)与lapply() 的奇怪的惰性评估方面有关,但由于其中一些(或者,实际上,全部)发生在 C 代码级别,我从来没有完全能够了解它在幕后所做的事情。
  • @JoshO'Brien 我认为这可能更多地与使用$ 解析参数有关,而不是专门用于 lapply。请参阅此示例:f&lt;-function(x,...) `$`(x, ...); f(dflist[[1]], "a"); `$`(dflist[[1]], "a")。这是因为$ 不是“典型”通用,而是.Primitive(),所以我敢打赌秘密在于here

标签: r lapply


【解决方案1】:

对于第一个例子,你可以这样做:

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

第二个,使用slot()访问函数

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

我不确定 为什么 方法分派在第一种情况下不起作用,但 ?lapplyNote 部分确实解决了原始函数的方法分派的问题喜欢$:

 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.

【讨论】:

  • 非常感谢您的见解。 slot() 功能是我最终真正追求的功能,因此感谢您引起我的注意。我添加了另一个答案,我认为在这种情况下更接近“为什么”。从我目前的理解来看,这实际上更多是关于通用 $ 实现而不是 lapply()
【解决方案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 时,我们绕过了在调度之前发生在原语中的标准解析(发生在源代码中here 附近)。

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

【讨论】:

  • 我觉得我可能很密集,但我不太明白这如何解释为什么lapply(dflist, `$`, "a") 返回NULL。毕竟,"$"(dflist[[1]], "z") 返回 31:33,但显然等效的调用 lapply(dflist[1], `$`, "z") 返回 NULL...我错过了什么?
  • 啊,好的。好吧,lapply 有一个额外的级别。它使用... 传递参数。那么如果你拦截调用会发生什么,你会看到传递给函数的第二个参数是“...”。所以你是对的。它还与 lapply 如何通过... 传递参数有关。我也会加进去的。
  • 哦,伙计,我明白你的意思了。这很迷人!看看这个:df &lt;- data.frame("..."=1:3, z=31:33); dflist &lt;- list(df, df); lapply(dflist, `$`, "z")
  • @JoshO'Brien Heh,是的,这正是我正在编辑的内容! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多