是否有一种方法可以在我的lapply()函数中获得列表索引名?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
我以前问过是否可以在lapply()返回的列表中保留索引名,但我仍然不知道是否有一种简单的方法来获取自定义函数中的每个元素名。我希望避免对名称本身调用lapply,我宁愿在函数参数中获得名称。
是否有一种方法可以在我的lapply()函数中获得列表索引名?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
我以前问过是否可以在lapply()返回的列表中保留索引名,但我仍然不知道是否有一种简单的方法来获取自定义函数中的每个元素名。我希望避免对名称本身调用lapply,我宁愿在函数参数中获得名称。
当前回答
只需编写自己的自定义lapply函数
lapply2 <- function(X, FUN){
if( length(formals(FUN)) == 1 ){
# No index passed - use normal lapply
R = lapply(X, FUN)
}else{
# Index passed
R = lapply(seq_along(X), FUN=function(i){
FUN(X[[i]], i)
})
}
# Set names
names(R) = names(X)
return(R)
}
然后像这样使用:
lapply2(letters, function(x, i) paste(x, i))
其他回答
同样的问题我已经遇到过很多次了…… 我开始用另一种方法…我不再使用lapply,而是开始使用mapply
n = names(mylist)
mapply(function(list.elem, names) { }, list.elem = mylist, names = n)
汤米的答案适用于命名向量,但我知道你对列表感兴趣。似乎他在绕一圈因为他引用了调用环境中的x。这个函数只使用传递给函数的参数,因此不假设传递对象的名称:
x <- list(a=11,b=12,c=13)
lapply(x, function(z) { attributes(deparse(substitute(z)))$names } )
#--------
$a
NULL
$b
NULL
$c
NULL
#--------
names( lapply(x, function(z) { attributes(deparse(substitute(z)))$names } ))
#[1] "a" "b" "c"
what_is_my_name <- function(ZZZ) return(deparse(substitute(ZZZ)))
what_is_my_name(X)
#[1] "X"
what_is_my_name(ZZZ=this)
#[1] "this"
exists("this")
#[1] FALSE
更新R版本3.2
免责声明:这是一个hack技巧,并可能停止工作在下一个版本。
你可以使用下面的方法获取索引:
> lapply(list(a=10,b=20), function(x){parent.frame()$i[]})
$a
[1] 1
$b
[1] 2
注意:[]是必需的,因为它欺骗R认为符号i(驻留在lapply的计算框架中)可能有更多的引用,从而激活它的惰性复制。如果没有它,R将不会保留i的分离副本:
> lapply(list(a=10,b=20), function(x){parent.frame()$i})
$a
[1] 2
$b
[1] 2
可以使用其他奇异的技巧,如function(x){parent.frame()$i+0}或function(x){——parent.frame()$i}。
性能的影响
强制复制是否会造成性能损失?是的!以下是基准:
> x <- as.list(seq_len(1e6))
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.38 0.00 2.37
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.45 0.00 2.45
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.41 0.00 2.41
> y[[2]]
[1] 2
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.92 0.00 1.93
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
2.07 0.00 2.09
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.89 0.00 1.89
> y[[2]]
[1] 1000000
结论
这个答案只是表明你不应该使用这个…如果您找到了另一个类似Tommy上面的解决方案,不仅您的代码更易于阅读,而且与未来的版本更兼容,您还可能失去核心团队努力开发的优化!
旧版本的把戏,不再起作用:
> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
结果:
$a
[1] 1
$b
[1] 2
$c
[1] 3
说明:lapply创建的调用形式为FUN(X[[1L]],…),FUN(X[[2L]],…)等。所以它传递的参数是X[[i]],其中i是循环中的当前索引。如果我们在它被求值之前得到它(即,如果我们使用替换),我们得到未求值的表达式X[[i]]。这是对[[函数的调用,参数X(一个符号)和i(一个整数)。因此,substitute(x)[[3]]返回的正是这个整数。
有了索引,你可以简单地访问名称,如果你先像这样保存它:
L <- list(a=10,b=10,c=10)
n <- names(L)
lapply(L, function(x)n[substitute(x)[[3]]])
结果:
$a
[1] "a"
$b
[1] "b"
$c
[1] "c"
或者使用第二个技巧::-)
lapply(list(a=10,b=10,c=10), function(x)names(eval(sys.call(1)[[2]]))[substitute(x)[[3]]])
(结果相同)。
解释2:sys.call(1)返回lapply(…),因此sys.call(1)[[2]]是用作lapply列表参数的表达式。将此传递给eval将创建一个名称可以访问的合法对象。有点棘手,但很有效。
好处:第二种获取名字的方法:
lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
注意,X在FUN的父框架中是一个有效的对象,它引用了lapply的list参数,所以我们可以使用eval.parent访问它。
这基本上使用了与Tommy相同的解决方法,但是使用Map(),不需要访问存储列表组件名称的全局变量。
> x <- list(a=11, b=12, c=13)
> Map(function(x, i) paste(i, x), x, names(x))
$a
[1] "a 11"
$b
[1] "b 12"
$c
[1] "c 13
或者,如果你喜欢mapply()
> mapply(function(x, i) paste(i, x), x, names(x))
a b c
"a 11" "b 12" "c 13"
您可以尝试从purrr包中使用imap()。
从文档中可以看到:
Imap (x,…)是map2(x, names(x),…)的简写,如果x有名称,则Imap (x, seq_along(x),…)
所以,你可以这样使用它:
library(purrr)
myList <- list(a=11,b=12,c=13)
imap(myList, function(x, y) paste(x, y))
这将给你以下结果:
$a
[1] "11 a"
$b
[1] "12 b"
$c
[1] "13 c"