在FUN中访问lapply索引名称
有没有办法在我的lapply()函数中获取列表索引名称?
n = names(mylist) lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
我之前问过是否可以保留lapply() 返回的列表中的索引名称,但我仍然不知道是否有一种简单的方法来获取自定义函数中的每个元素名称。 我想避免在名称本身上调用lapply,我宁愿在函数参数中获取名称。
不幸的是, lapply
只会给你通过它的向量的元素。 通常的解决方法是传递向量的名称或索引,而不是向量本身。
但请注意,您可以随时向函数传递额外的参数,因此以下工作:
x <- list(a=11,b=12,c=13) # Changed to list to address concerns in commments lapply(seq_along(x), function(y, n, i) { paste(n[[i]], y[[i]]) }, y=x, n=names(x))
在这里,我使用lapply
来指定x
的索引,还要传入x
和x
的名字。 正如你所看到的,函数参数的顺序可以是任何东西 – lapply
会将“元素”(这里是索引)传递给第一个参数,在额外的参数中没有指定。 在这种情况下,我指定了y
和n
,所以只剩下i
了…
其中产生以下内容:
[[1]] [1] "a 11" [[2]] [1] "b 12" [[3]] [1] "c 13"
更新更简单的例子,相同的结果:
lapply(seq_along(x), function(i) paste(names(x)[[i]], x[[i]]))
这里函数使用“全局”variablesx
并提取每个调用中的名称。
这基本上使用与Tommy相同的解决方法,但是使用Map()
,不需要访问存储列表组件名称的全局variables。
> 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)) abc "a 11" "b 12" "c 13"
更新R版本3.2
免责声明:这是一个诡计,可能会停止在下一个版本的工作。
你可以使用这个获得索引:
> 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这样的另一个解决scheme,并且与未来的版本更兼容,你的代码将更具可读性,你也有可能失去核心团队努力工作的优化发展!
旧版本的技巧,不再工作:
> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
结果:
$a [1] 1 $b [1] 2 $c [1] 3
说明: lapply
创buildFUN(X[[1L]], ...)
, FUN(X[[2L]], ...)
的调用,所以它传递的参数是X[[i]]
i
是循环中的当前索引。 如果我们在评估它之前得到这个(即,如果我们使用substitute
),我们得到未评估的expression式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
列表参数的lapply
。 将此传递给eval
创buildnames
可以访问的合法对象。 棘手,但它的作品。
奖金:获取名字的第二种方法:
lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
请注意, X
是FUN
的父框架中的有效对象,并且引用了lapply
的列表参数,所以我们可以通过eval.parent
find它。
我有很多次相同的问题…我已经开始使用另一种方式…而不是使用lapply
,我已经开始使用mapply
n = names(mylist) mapply(function(list.elem, names) { }, list.elem = mylist, names = n)
只是循环的名字。
sapply(names(mylist), function(n) { doSomething(mylist[[n]]) cat(n, '\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
我的答案与Tommy和caracals的方向是一致的,但是避免把列表保存为一个额外的对象。
lapply(seq(3), function(i, y=list(a=14,b=15,c=16)) { paste(names(y)[[i]], y[[i]]) })
结果:
[[1]] [1] "a 14" [[2]] [1] "b 15" [[3]] [1] "c 16"
这将该列表作为FUN的命名参数(而不是lapply)。 lapply只需要迭代列表中的元素(当更改列表的长度时,请小心将第一个参数更改为lapply)。
注意:直接给列表作为一个额外的参数lapply也适用:
lapply(seq(3), function(i, y) { paste(names(y)[[i]], y[[i]]) }, y=list(a=14,b=15,c=16))
只需编写您自己的定制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))
你可以尝试从purrr
包使用imap()
。
从文档:
如果x有名字,则imap(x,…)是map2(x,names(x),…)的简写forms,如果不是,则是map2(x,seq_along(x),…)。
所以,你可以这样使用它:
library(purrr) myList <- list(a=11,b=12,c=13) imap(myList, function(x, y) paste(x, y)