我最近一直在处理一些大型、复杂的列表,我看到了一些令人惊讶的行为(至少对我来说),主要与为列表分配名称有关。一个简单的例子:
Fil <- list(
a = list(A=seq(1, 5, 1), B=rnorm(5), C=runif(5)),
b = list(A="Cat", B=c("Dog", "Bird"), C=list("Squirrel", "Cheetah", "Lion")),
c = list(A=rep(TRUE, 5), B=rep(FALSE, 5), C=rep(NA, 5)))
filList <- list()
for(i in 1:3){
filList[i] <- Fil[i]
names(filList)[i] <- names(Fil[i])
}
identical(Fil,filList)
[1] TRUE
但是:
for(i in 1:3){
filList[i] <- Fil[i]
names(filList[i]) <- names(Fil[i])
}
identical(Fil,filList)
[1] FALSE
我认为它让我感到困惑的主要原因是因为第一个 for 循环中第一个
names
行的左侧形式需要与右侧的形式不同才能工作;我本以为这些应该是一样的。有人可以向我解释一下吗?
第一种情况是正确的用法。在第二种情况下,您将
filList[i]
发送到 names<-
,它仅作为临时子集对象存在。
或者,您可以在循环之外执行所有操作:
names(filList) <- names(Fil)
正如@James指出的,
names(filList)[i] <- ...
是正确的方法,而names(filList[i]) <- ...
不起作用。
来自
?names
:
可以通过一般规则仅更新部分名称属性:
。这是有效的,因为那里的表达式被计算为names(z)[3] <- "c2"
。z <- "names<-"(z, "[<-"(names(z), 3, "c3"))
z <- list(a = 1, b = "c", c = 1:3)
names(z)[3] <- "c2"
z
#> $a
#> [1] 1
#>
#> $b
#> [1] "c"
#>
#> $c2
#> [1] 1 2 3
z <- "names<-"(z, "[<-"(names(z), 3, "c3"))
z
#> $a
#> [1] 1
#>
#> $b
#> [1] "c"
#>
#> $c3
#> [1] 1 2 3
但是,您的第二种方法(
names(filList[i]) <-
)不起作用的原因不是“您创建了一个未绑定的临时变量,您可以更改其名称,但它永远不重要,因为它只是一个临时变量”,而是默认的子设置赋值运算符 [<-
不会转移名称(这是有道理的,因为 names
是整个 attributes
的 SEXPREC
而不是单个元素)。在 R 中,f(x[i])<- ...
不一定与 tmp <- x[i]; f(tmp) <- ...
相同!一般来说,虽然 R 鼓励函数式编程,但 R 函数(如 `<-`
)可以对调用环境做任何他们想做的事情,并且不是通过引用或值而是作为表达式接收参数。
我们可以重写子集分配运算符以使其也分配名称:
z <- list(a = 1, b = "c", c = 1:3)
names(z[2]) <- "b2" # does not work
z
#> $a
#> [1] 1
#>
#> $b
#> [1] "c"
#>
#> $c
#> [1] 1 2 3
`[<-` <- function(x, idx, value) {
ret <- base::`[<-`(x, idx, value)
names(ret) <- base::`[<-`(names(ret),idx, names(value))
ret
}
names(z[2]) <- "b2" #does work
z
#> $a
#> [1] 1
#>
#> $b2
#> [1] "c"
#>
#> $c
#> [1] 1 2 3
rm(`[<-`) # please don't use this in real life! At least, implement a S3 method (`[<-.yourclass`) instead
这样,您的两种方法都会起作用。
掌握了这些知识,您还可以创建一个函数来仅更改一个名称
`name<-` <- function(x,i,value) {
names(x)[i] <- value
x # `function<-` functions return a modified version of their first argument which will be bound to the name of the first arguments symbol
}
name(z, 3) <- "c4"
z
#> $a
#> [1] 1
#>
#> $b
#> [1] "c"
#>
#> $c4
#> [1] 1 2 3
创建于 2023-12-09,使用 reprex v2.0.2