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

Sha*_*pie 490 r extract list dataframe r-faq

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

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

ars*_*ars 308

R语言定义对于回答这些类型的问题非常方便:

R有三个基本的索引操作符,其语法由以下示例显示

    x[i]
    x[i, j]
    x[[i]]
    x[[i, j]]
    x$a
    x$"a"
表单很少使用,尽管它们与表单有一些细微的语义差异[[(例如,它删除任何名称或dimnames属性,并且部分匹配用于字符索引).使用单个索引索引多维结构时,[或者x[[i]]将返回第x[i]th个顺序元素i.

对于列表,通常用于x选择任何单个元素,而[[返回所选元素的列表.

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

  • 使用[[vs [用单个数字对矢量索引?]的原因是什么?为什么不直接使用[两者兼而有之?我猜你可以使用[[获取单个条目,并且[一个索引返回长度为1的列表...但为什么不只是使[返回一个带有一个索引而不是列表的条目?为什么你可能想要返回长度为1的列表? (6认同)
  • @wordsforthewise,在编程时,您可以使用未定义长度的向量来进行索引.有了`[`总是返回一个列表意味着你得到`x [v]`的相同输出类,而不管`v`的长度.例如,人们可能想要对列表的一个子集进行"lapply":`lapply(x [v],fun)`.如果`[`会删除长度为1的向量的列表,那么只要`v`的长度为1,就会返回错误. (4认同)
  • 我认为这解释得更清楚,http://adv-r.had.co.nz/Subsetting.html (4认同)

Sha*_*pie 165

两种方法之间的显着差异是它们在用于提取时返回的对象类,以及它们是否可以接受一系列值,或者在赋值期间只接受一个值.

考虑以下列表中的数据提取案例:

foo <- list( str='R', vec=c(1,2,3), bool=TRUE )
Run Code Online (Sandbox Code Playgroud)

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

因此,使用该[]方法会导致以下结果:

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

class( foo[ 'bool' ] )
[1] "list"
Run Code Online (Sandbox Code Playgroud)

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

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

class( foo[[ 'bool' ]] )
[1] "logical"
Run Code Online (Sandbox Code Playgroud)

第二个区别在于,[]操作员可以用于访问数据帧中列表或列中的一系列时隙,而[[]]操作员仅限于访问单个插槽或列.考虑使用第二个列表进行值赋值的情况bar():

bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )
Run Code Online (Sandbox Code Playgroud)

假设我们想要用bar中包含的数据覆盖foo的最后两个插槽.如果我们尝试使用[[]]运算符,则会发生以下情况:

foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar : 
more elements supplied than there are to replace
Run Code Online (Sandbox Code Playgroud)

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

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

$str
[1] "R"

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

$bool
[1] -0.6291121
Run Code Online (Sandbox Code Playgroud)

请注意,虽然赋值成功,但foo中的插槽保留了原始名称.


med*_*oll 106

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

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

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

a <- lst[[1]]
class(a)
## returns "character"
Run Code Online (Sandbox Code Playgroud)


jza*_*dra 50

来自哈德利威克姆:

来自哈德利威克姆

使用tidyverse/purrr显示我的(糟糕的外观)修改:

在此输入图像描述

  • Grace Hopper 在莱特曼上演示纳秒 https://www.dailymotion.com/video/x35dsz7。 (3认同)
  • 凉!您已经有了Grace Hopper的[皮秒](https://www.youtube.com/watch?v=1-vcErOPofQ&amp;feature=youtu.be&amp;t=455)! (2认同)

小智 46

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

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"
Run Code Online (Sandbox Code Playgroud)


Mic*_*ico 18

只需添加此处[[也可以进行递归索引.

@JijoMatthew在答案中暗示了这一点,但没有进行探讨.

如上所述?"[[",语法like x[[y]],where length(y) > 1,被解释为:

x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]
Run Code Online (Sandbox Code Playgroud)

请注意,这并不会改变主要内容之间的差异[,[[即前者用于子集化,后者用于提取单个列表元素.

例如,

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
Run Code Online (Sandbox Code Playgroud)

要获得值3,我们可以:

x[[c(2, 1, 1, 1)]]
# [1] 3
Run Code Online (Sandbox Code Playgroud)

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

r <- list(1:10, foo=1, far=2)
Run Code Online (Sandbox Code Playgroud)

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

r[[1:3]]
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


小智 13

它们都是分组的方式.单个括号将返回列表的子集,其本身将是一个列表.即:它可能包含也可能不包含多个元素.另一方面,双括号将仅返回列表中的单个元素.

- 单支架将给我们一个列表.如果我们希望从列表中返回多个元素,我们也可以使用单个括号.考虑以下清单: -

>r<-list(c(1:10),foo=1,far=2);
Run Code Online (Sandbox Code Playgroud)

现在请注意我尝试显示列表时返回列表的方式.我输入r然后按回车键

>r

#the result is:-

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1

$far

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

现在我们将看到单支架的魔力: -

>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
Run Code Online (Sandbox Code Playgroud)

这与我们试图在屏幕上显示r的值完全相同,这意味着单个括号的使用返回了一个列表,其中在索引1处我们有一个10个元素的向量,然后我们还有两个名为foo的元素到目前为止.我们也可以选择将单个索引或元素名称作为单个括号的输入.例如:

> r[1]

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们给了一个索引"1",并作为回报获得了一个包含一个元素的列表(这是一个包含10个数字的数组)

> r[2]

$foo

[1] 1
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我们给了一个索引"2",然后得到一个包含一个元素的列表

> r["foo"];

$foo

[1] 1
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们传递了一个元素的名称,作为回报,返回了一个带有一个元素的列表.

您还可以传递元素名称的向量,如: -

> x<-c("foo","far")

> r[x];

$foo

[1] 1

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

在这个例子中,我们传递了一个带有两个元素名称"foo"和"far"的向量

作为回报,我们有一个包含两个元素的列表.

简而言之,单括号将始终返回另一个列表,其中元素的数量等于元素的数量或传递到单个括号中的索引的数量.

相反,双括号将始终只返回一个元素.在移动到双支架之前要记住一个注意事项. 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
Run Code Online (Sandbox Code Playgroud)

对于双括号,如果我们尝试通过传递向量来查看多个元素,它将导致错误,因为它不是为了满足该需要而构建的,而只是为了返回单个元素.

考虑以下

> 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
Run Code Online (Sandbox Code Playgroud)

  • 被否决是因为它提出了诸如“虽然双括号永远不会返回列表”之类的强烈主张。这是不正确的 - 如果我们有一个列表列表的对象,双括号将返回另一个列表。 (4认同)
  • 被否决是因为“传递向量......将导致错误,只是因为它不是为了满足该需求而构建的”是不正确的;看我的新答案。 (3认同)

小智 12

为了帮助新手浏览手动雾,将[[ ... ]]符号视为折叠函数可能会有所帮助- 换句话说,就是当您只想从命名向量,列表或数据框中"获取数据"时.如果要使用来自这些对象的数据进行计算,最好这样做.这些简单的例子将说明.

(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]]
Run Code Online (Sandbox Code Playgroud)

所以从第三个例子来看:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2
Run Code Online (Sandbox Code Playgroud)


小智 11

作为术语,[[运算符从列表中提取元素,而[运算符获取列表的子集.


Pet*_*ter 7

对于另一个具体用例,当您要选择由该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"
Run Code Online (Sandbox Code Playgroud)