用于访问列表或数据框元素的和]符号之间的区别

R提供了两种不同的方法来访问列表或data.frame的元素[][[]]运算符。

两者有什么区别? 在什么情况下我应该使用一个在另一个之上?

R语言定义可以方便地回答这些types的问题:

R有三个基本的索引操作符,语法如下所示

     X [i]于
     x [i,j]
     X [[I]]
     x [[i,j]]
     X $一个
     X $ “一”

对于向量和matrix, [[forms]很less使用,虽然它们与[forms(例如,删除任何名称或暗淡名称属性,以及部分匹配用于字符索引)略有语义差异)。 当索引具有单个索引的多维结构时, x[[i]]x[i]将返回x[i]i个顺序元素。

对于列表,通常使用[[来select任何单个元素,而[返回所选元素的列表。

[[表单]只允许使用整数或字符索引来select单个元素,而[允许通过vector进行索引。 请注意,对于列表,索引可以是一个vector,vector的每个元素依次应用于列表,选定的组件,所选的组件的组件等等。 结果仍然是一个单一的元素。

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

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

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

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

所以,使用[]方法会导致以下结果:

 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中的插槽保留了原来的名称。

双括号访问一个列表元素 ,而一个括号给你一个单一元素的列表。

 lst <- list('one','two','three') a <- lst[1] class(a) ## returns "list" a <- lst[[1]] class(a) ## returns "character" 

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

 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); 

现在请注意当我尝试显示它时,列表返回的方式。 我inputr并按回车

 >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和远。 我们也可以select给单个索引或元素名称作为单个括号的input。 例如:

 > 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 

在这个例子中,我们传递了两个元素名称“富”和“远”

作为回报,我们得到了包含两个元素的列表。

简而言之,单个支架总是会返回另一个列表,其元素个数等于要传递到单个支架中的元素数量或索引数量。

相比之下,双括号将总是只返回一个元素。 在把双音符括起来之前, NOTE:THE MAJOR DIFFERENCE BETWEEN THE TWO IS THAT SINGLE BRACKET RETURNS YOU A LIST WITH AS MANY ELEMENTS AS YOU WISH WHILE A DOUBLE BRACKET WILL NEVER RETURN A LIST. RATHER A DOUBLE BRACKET WILL RETURN ONLY A SINGLE ELEMENT FROM THE LIST.

我会举几个例子。 请在下面的示例完成后,留下粗体字并记下来:

双括号会返回你在索引处的实际值(它不会返回一个列表)

  > r[[1]] [1] 1 2 3 4 5 6 7 8 9 10 >r[["foo"]] [1] 1 

对于双括号来说,如果我们试图通过传递一个向量来查看多个元素,它将会导致一个错误,只是因为它不是为满足这个需要而构build的,而只是为了返回一个元素。

考虑以下几点

 > 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 

为了帮助新手浏览手动雾,可以将[[ ... ]]符号看作折叠函数,换句话说,当您只想从命名向量中获取数据时,列表或数据框。 如果您想使用这些对象的数据进行计算,那么这样做很好。 这些简单的例子将说明。

 (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 

只是在这里添加[[也配备了recursion索引

这被@JijoMatthew的回答暗示,但没有探讨。

?"[[" ,类似于x[[y]]语法length(y) > 1其中length(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]] :recursion索引失败,级别为2

由于这段代码实际上试图评估r[[1]][[2]][[3]]r级别的嵌套,所以通过recursion索引提取的尝试失败了[[2]] ,在2级。

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

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

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

对于另一个具体的用例,当你想select由split()函数创build的数据框时,使用双括号。 如果您不知道, 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" 

作为术语, [[运算符从列表中提取元素,而[运算符则是列表的子集 ]。