R提供了两种不同的方法来访问list或data.frame中的元素:[]和[[]]。
这两者之间的区别是什么,什么时候我应该使用其中一个而不是另一个?
R提供了两种不同的方法来访问list或data.frame中的元素:[]和[[]]。
这两者之间的区别是什么,什么时候我应该使用其中一个而不是另一个?
当前回答
它们都是子集的方法。 单括号将返回列表的一个子集,该子集本身就是一个列表。也就是说,它可能包含也可能不包含一个以上的元素。 另一方面,双括号将只返回列表中的单个元素。
-单括号会给出一个列表。如果希望从列表中返回多个元素,也可以使用单个括号。 考虑以下列表:
>r<-list(c(1:10),foo=1,far=2);
现在,请注意当我试图显示列表时返回的方式。 我输入r并按enter。
>r
#the result is:-
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
现在我们来看看单括号的神奇之处:
>r[c(1,2,3)]
#the above command will return a list with all three elements of the actual list r as below
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
这与我们试图在屏幕上显示r值时完全相同,这意味着使用单括号返回了一个列表,其中在索引1处我们有一个包含10个元素的向量,然后我们有两个名称为foo和far的元素。 我们也可以选择一个索引或元素名作为单个括号的输入。 例如,:
> r[1]
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
在这个例子中,我们给出了一个索引“1”,作为回报,我们得到了一个包含一个元素的列表(它是一个包含10个数字的数组)
> r[2]
$foo
[1] 1
在上面的例子中,我们给出了一个索引“2”,作为回报,我们得到了一个包含一个元素的列表:
> r["foo"];
$foo
[1] 1
在本例中,我们传递一个元素的名称,返回一个包含一个元素的列表。
你也可以传递一个包含元素名称的向量,比如:
> x<-c("foo","far")
> r[x];
$foo
[1] 1
$far
[1] 2
在这个例子中,我们传递了一个有两个元素名为“foo”和“far”的向量。
作为回报,我们得到了一个包含两个元素的列表。
简而言之,单个括号将总是返回另一个列表,其中元素的数量等于传递到单个括号中的元素的数量或索引的数量。
相反,双括号总是只返回一个元素。 在转到双括号之前,要记住一点。 注意:两者之间的主要区别是,单括号将返回包含任意数量元素的列表,而双括号将永远不会返回列表。相反,双括号只返回列表中的单个元素。
我将举几个例子。请记下加粗的单词,在你完成下面的例子后再回头看:
双括号将返回索引处的实际值。(它不会返回一个列表)
> r[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
>r[["foo"]]
[1] 1
对于双括号,如果我们试图通过传递一个向量来查看多个元素,就会导致错误,因为它不是为了满足这个需要而构建的,而是为了返回单个元素。
考虑以下几点
> r[[c(1:3)]]
Error in r[[c(1:3)]] : recursive indexing failed at level 2
> r[[c(1,2,3)]]
Error in r[[c(1, 2, 3)]] : recursive indexing failed at level 2
> r[[c("foo","far")]]
Error in r[[c("foo", "far")]] : subscript out of bounds
其他回答
这两种方法之间的显著区别在于它们用于提取时返回的对象的类别,以及它们是否可以接受一个范围的值,或者在赋值时只接受一个值。
考虑以下列表中的数据提取情况:
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中的槽保留了它们原来的名称。
只是在这里添加[[也用于递归索引。
@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并不存在,所以我们得到了下标越界错误。
如果这两个错误给出相同的信息,可能会更有帮助/一致一些。
为了帮助新手在手动迷雾中导航,看看[[…]]]符号作为一个折叠函数-换句话说,当你只是想从一个命名向量,列表或数据帧中“获取数据”时。如果您想使用来自这些对象的数据进行计算,那么这样做是很好的。这些简单的例子将说明。
(x <- c(x=1, y=2)); x[1]; x[[1]]
(x <- list(x=1, y=2, z=3)); x[1]; x[[1]]
(x <- data.frame(x=1, y=2, z=3)); x[1]; x[[1]]
从第三个例子来看
> 2 * x[1]
x
1 2
> 2 * x[[1]]
[1] 2
[]提取列表,[[]]提取列表中的元素
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"
它们都是子集的方法。 单括号将返回列表的一个子集,该子集本身就是一个列表。也就是说,它可能包含也可能不包含一个以上的元素。 另一方面,双括号将只返回列表中的单个元素。
-单括号会给出一个列表。如果希望从列表中返回多个元素,也可以使用单个括号。 考虑以下列表:
>r<-list(c(1:10),foo=1,far=2);
现在,请注意当我试图显示列表时返回的方式。 我输入r并按enter。
>r
#the result is:-
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
现在我们来看看单括号的神奇之处:
>r[c(1,2,3)]
#the above command will return a list with all three elements of the actual list r as below
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
这与我们试图在屏幕上显示r值时完全相同,这意味着使用单括号返回了一个列表,其中在索引1处我们有一个包含10个元素的向量,然后我们有两个名称为foo和far的元素。 我们也可以选择一个索引或元素名作为单个括号的输入。 例如,:
> r[1]
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
在这个例子中,我们给出了一个索引“1”,作为回报,我们得到了一个包含一个元素的列表(它是一个包含10个数字的数组)
> r[2]
$foo
[1] 1
在上面的例子中,我们给出了一个索引“2”,作为回报,我们得到了一个包含一个元素的列表:
> r["foo"];
$foo
[1] 1
在本例中,我们传递一个元素的名称,返回一个包含一个元素的列表。
你也可以传递一个包含元素名称的向量,比如:
> x<-c("foo","far")
> r[x];
$foo
[1] 1
$far
[1] 2
在这个例子中,我们传递了一个有两个元素名为“foo”和“far”的向量。
作为回报,我们得到了一个包含两个元素的列表。
简而言之,单个括号将总是返回另一个列表,其中元素的数量等于传递到单个括号中的元素的数量或索引的数量。
相反,双括号总是只返回一个元素。 在转到双括号之前,要记住一点。 注意:两者之间的主要区别是,单括号将返回包含任意数量元素的列表,而双括号将永远不会返回列表。相反,双括号只返回列表中的单个元素。
我将举几个例子。请记下加粗的单词,在你完成下面的例子后再回头看:
双括号将返回索引处的实际值。(它不会返回一个列表)
> r[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
>r[["foo"]]
[1] 1
对于双括号,如果我们试图通过传递一个向量来查看多个元素,就会导致错误,因为它不是为了满足这个需要而构建的,而是为了返回单个元素。
考虑以下几点
> r[[c(1:3)]]
Error in r[[c(1:3)]] : recursive indexing failed at level 2
> r[[c(1,2,3)]]
Error in r[[c(1, 2, 3)]] : recursive indexing failed at level 2
> r[[c("foo","far")]]
Error in r[[c("foo", "far")]] : subscript out of bounds