R提供了两种不同的方法来访问list或data.frame中的元素:[]和[[]]。

这两者之间的区别是什么,什么时候我应该使用其中一个而不是另一个?


当前回答

[]提取列表,[[]]提取列表中的元素

alist <- list(c("a", "b", "c"), c(1,2,3,4), c(8e6, 5.2e9, -9.3e7))

str(alist[[1]])
 chr [1:3] "a" "b" "c"

str(alist[1])
List of 1
 $ : chr [1:3] "a" "b" "c"

str(alist[[1]][1])
 chr "a"

其他回答

这两种方法之间的显著区别在于它们用于提取时返回的对象的类别,以及它们是否可以接受一个范围的值,或者在赋值时只接受一个值。

考虑以下列表中的数据提取情况:

foo <- list( str='R', vec=c(1,2,3), bool=TRUE )

假设我们想从foo中提取由bool存储的值,并在if()语句中使用它。这将说明在用于数据提取时[]和[[]]的返回值之间的差异。[]方法返回类list(或者data.frame,如果foo是data.frame)的对象,而[[]]方法返回类由其值的类型决定的对象。

因此,使用[]方法的结果如下:

if( foo[ 'bool' ] ){ print("Hi!") }
Error in if (foo["bool"]) { : argument is not interpretable as logical

class( foo[ 'bool' ] )
[1] "list"

这是因为[]方法返回了一个列表,而列表不是直接传递给if()语句的有效对象。在这种情况下,我们需要使用[[]],因为它将返回存储在'bool'中的“裸”对象,该对象将具有适当的类:

if( foo[[ 'bool' ]] ){ print("Hi!") }
[1] "Hi!"

class( foo[[ 'bool' ]] )
[1] "logical"

第二个区别是[]操作符可用于访问列表中的一系列槽或数据帧中的列,而[[]]操作符仅限于访问单个槽或列。考虑使用第二个列表bar()赋值的情况:

bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )

假设我们想用bar中包含的数据覆盖foo的最后两个槽。如果我们尝试使用[[]]操作符,会发生这样的情况:

foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar : 
more elements supplied than there are to replace

这是因为[[]]仅限于访问单个元素。我们需要使用[]:

foo[ 2:3 ] <- bar
print( foo )

$str
[1] "R"

$vec
     [,1] [,2]
[1,]    0    0
[2,]    0    0

$bool
[1] -0.6291121

注意,虽然赋值成功,但foo中的槽保留了它们原来的名称。

[]提取列表,[[]]提取列表中的元素

alist <- list(c("a", "b", "c"), c(1,2,3,4), c(8e6, 5.2e9, -9.3e7))

str(alist[[1]])
 chr [1:3] "a" "b" "c"

str(alist[1])
List of 1
 $ : chr [1:3] "a" "b" "c"

str(alist[[1]][1])
 chr "a"

双括号访问列表元素,而单括号返回一个包含单个元素的列表。

lst <- list('one','two','three')

a <- lst[1]
class(a)
## returns "list"

a <- lst[[1]]
class(a)
## returns "character"

对于另一个具体的用例,当您想要选择split()函数创建的数据帧时,使用双括号。如果您不知道,split()会根据关键字段将列表/数据帧分组为子集。如果你想对多个组进行操作,绘制它们,等等,这很有用。

> class(data)
[1] "data.frame"

> dsplit<-split(data, data$id)
> class(dsplit)
[1] "list"

> class(dsplit['ID-1'])
[1] "list"

> class(dsplit[['ID-1']])
[1] "data.frame"

只是在这里添加[[也用于递归索引。

@JijoMatthew在回答中暗示了这一点,但没有深入探讨。

正如在?"[["中所指出的,像x[[y]]这样的语法,其中长度(y) > 1被解释为:

x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]

注意,这并没有改变关于[和[[]之间的主要区别,即前者用于子集设置,后者用于提取单个列表元素。

例如,

x <- list(list(list(1), 2), list(list(list(3), 4), 5), 6)
x
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [1] 1
#
# [[1]][[2]]
# [1] 2
#
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
# [[2]][[1]][[1]][[1]]
# [1] 3
#
# [[2]][[1]][[2]]
# [1] 4
#
# [[2]][[2]]
# [1] 5
#
# [[3]]
# [1] 6

要得到值3,我们可以这样做:

x[[c(2, 1, 1, 1)]]
# [1] 3

回到上面@JijoMatthew的答案,回想一下r:

r <- list(1:10, foo=1, far=2)

特别是,这解释了我们在误用[[时往往会得到的错误,即:

r[[1:3]]

r[[1:3]]中的错误:递归索引在级别2失败

由于这段代码实际上试图求值r[[1]][[2]][[3]],并且r的嵌套在第一级停止,因此通过递归索引提取的尝试在[[2]]失败,即在第2级。

r[[c("foo", "far")]]错误:下标越界

这里,R正在寻找R [["foo"]][["far"]],而R并不存在,所以我们得到了下标越界错误。

如果这两个错误给出相同的信息,可能会更有帮助/一致一些。