在FUN中访问lapply索引名称

Rob*_*ick 151 r names lapply indices

有没有办法在我的lapply()函数中获取列表索引名称?

n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
Run Code Online (Sandbox Code Playgroud)

之前询问是否可以在lapply()返回列表中保留索引名称,但我仍然不知道是否有一种简单的方法来获取自定义函数中的每个元素名称.我想避免在名称本身上调用lapply,我宁愿在函数参数中获取名称.

Tom*_*mmy 147

不幸的是,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))
Run Code Online (Sandbox Code Playgroud)

在这里我使用lapply了索引x,但也传入x了名称x.正如您所看到的,函数参数的顺序可以是任何东西 - lapply将"元素"(此处为索引)传递给在额外参数中指定的第一个参数.在这种情况下,我指定yn,所以有唯一的i左...

产生以下内容:

[[1]]
[1] "a 11"

[[2]]
[1] "b 12"

[[3]]
[1] "c 13"
Run Code Online (Sandbox Code Playgroud)

更新更简单的例子,相同的结果:

lapply(seq_along(x), function(i) paste(names(x)[[i]], x[[i]]))
Run Code Online (Sandbox Code Playgroud)

这里函数使用"global"变量x并在每次调用中提取名称.


car*_*cal 44

这基本上使用与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
Run Code Online (Sandbox Code Playgroud)

或者,如果您愿意 mapply()

> mapply(function(x, i) paste(i, x), x, names(x))
     a      b      c 
"a 11" "b 12" "c 13"
Run Code Online (Sandbox Code Playgroud)


Fer*_*aft 38

R版本3.2的更新

免责声明:这是一个hacky技巧,可能会停止在下一个版本中工作.

您可以使用以下方法获取索引:

> lapply(list(a=10,b=20), function(x){parent.frame()$i[]})
$a
[1] 1

$b
[1] 2
Run Code Online (Sandbox Code Playgroud)

注意:这[]是必须的,因为它欺骗R认为符号i(位于评估框架中lapply)可能有更多的引用,从而激活它的懒惰重复.没有它,R将不会保留以下分开的副本i:

> lapply(list(a=10,b=20), function(x){parent.frame()$i})
$a
[1] 2

$b
[1] 2
Run Code Online (Sandbox Code Playgroud)

可以使用其他异国情调的技巧,如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
Run Code Online (Sandbox Code Playgroud)

结论

这个答案只是表明你不应该使用这个......如果你找到像Tommy上面的其他解决方案,并且与未来的版本更兼容,不仅你的代码会更具可读性,你也有可能失去核心团队努力工作的优化开发!


旧版本的技巧,不再有效:

> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
Run Code Online (Sandbox Code Playgroud)

结果:

$a
[1] 1

$b
[1] 2

$c
[1] 3
Run Code Online (Sandbox Code Playgroud)

说明:lapply创建窗体的来电FUN(X[[1L]], ...),FUN(X[[2L]], ...)所以它传递的参数是等X[[i]]在那里i是在回路中的电流指标.如果我们评估它之前得到它(即,如果我们使用substitute),我们得到未评估的表达式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]]])
Run Code Online (Sandbox Code Playgroud)

结果:

$a
[1] "a"

$b
[1] "b"

$c
[1] "c"
Run Code Online (Sandbox Code Playgroud)

或者使用第二招:-)

lapply(list(a=10,b=10,c=10), function(x)names(eval(sys.call(1)[[2]]))[substitute(x)[[3]]])
Run Code Online (Sandbox Code Playgroud)

(结果是一样的).

解释2:sys.call(1)返回lapply(...),这sys.call(1)[[2]]是用作list参数的表达式lapply.传递此信息可eval创建names可访问的合法对象.棘手,但它的工作原理.

奖励:获得名字的第二种方式:

lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
Run Code Online (Sandbox Code Playgroud)

请注意,它X是父框架中的有效对象FUN,并引用了list参数lapply,因此我们可以使用它eval.parent.

  • 代码`lapply(list(a = 10,b = 10,c = 10),function(x)substitute(x)[[3]])`返回全部为3.你能解释这3是如何选择的?和差异的原因?它是否等于列表的长度,在这种情况下,3.对不起,如果这是一个基本问题,但想知道如何在一般情况下应用它. (2认同)

Ana*_*ldi 17

我有很多次遇到同样的问题......我开始使用另一种方式......我开始使用了,而不是lapply使用mapply

n = names(mylist)
mapply(function(list.elem, names) { }, list.elem = mylist, names = n)
Run Code Online (Sandbox Code Playgroud)

  • 我也更喜欢这个,但是这个答案是[上一个](/sf/answers/1438263501/)的重复。 (2认同)

Kev*_*rca 10

你可以尝试使用imap()purrr包.

从文档:

如果x有名字,则imap(x,...)是map2(x,names(x),...)的简写,如果不是,则为map2(x,seq_along(x),...).

所以,你可以这样使用它:

library(purrr)
myList <- list(a=11,b=12,c=13) 
imap(myList, function(x, y) paste(x, y))
Run Code Online (Sandbox Code Playgroud)

这会给你以下结果:

$a
[1] "11 a"

$b
[1] "12 b"

$c
[1] "13 c"
Run Code Online (Sandbox Code Playgroud)


小智 9

只需循环输入名称即可.

sapply(names(mylist), function(n) { 
    doSomething(mylist[[n]])
    cat(n, '\n')
}
Run Code Online (Sandbox Code Playgroud)

  • @flies:是的,除了在函数内部对变量 `mylist` 进行硬编码是不好的做法。最好还是做`function(mylist, nm) ...` (2认同)

42-*_*42- 5

汤米的答案适用于命名向量,但我得到了你对列表感兴趣的想法.似乎他正在做一个端点,因为他在呼叫环境中引用了"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
Run Code Online (Sandbox Code Playgroud)