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

The difference between bracket [ ] and double bracket [[ ]] for accessing the elements of a list or dataframe

提问人:Sharpie 提问时间:7/23/2009 最后编辑:MrFlickSharpie 更新时间:1/12/2022 访问量:326490

问:

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

两者之间有什么区别,什么时候应该使用一个而不是另一个?

列表 数据帧 提取 R-FAQ

评论


答:

418赞 ars 7/23/2009 #1

R 语言定义可用于回答以下类型的问题:

R 有三个基本的索引运算符,语法如以下示例所示

    x[i]
    x[i, j]
    x[[i]]
    x[[i, j]]
    x$a
    x$"a"

对于向量和矩阵,很少使用形式,尽管它们与形式有一些细微的语义差异(例如,它删除了任何名称或 dimnames 属性,并且部分匹配用于字符索引)。当使用单个索引索引多维结构时,或者将返回 的第 个顺序元素。[[[x[[i]]x[i]ix

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

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

评论

10赞 wordsforthewise 11/18/2017
使用 [[ vs [ 用单个数字与向量进行索引的原因是什么?为什么不直接对两者都使用 [?我想你可以使用 [[ 来取回一个条目,而 [ 有一个索引返回一个长度为 1 的列表...但为什么不让 [ 返回一个带有一个索引的条目而不是列表呢?为什么需要返回长度为 1 的列表?
11赞 Axeman 12/15/2017
@wordsforthewise,在编程时,您可以有一个未定义长度的向量,用于索引。始终返回列表意味着无论 的长度如何,您都会获得相同的输出类。例如,可能希望对列表的子集进行处理:.如果删除长度为 1 的向量列表,则每当长度为 1 时都会返回错误。[x[v]vlapplylapply(x[v], fun)[v
8赞 Nate Anderson 9/13/2019
我认为这更清楚地解释了,adv-r.had.co.nz/Subsetting.html
147赞 medriscoll 7/23/2009 #2

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

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

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

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

评论

1赞 cloudscomputes 4/27/2023
简单明了
62赞 user143339 7/23/2009 #3

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

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"
208赞 Sharpie 7/23/2009 #4

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

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

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

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

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

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

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

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

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 中的插槽仍保留其原始名称。

8赞 Peter 5/5/2012 #5

对于另一个具体用例,当您想要选择由函数创建的数据框时,请使用双括号。如果您不知道,请根据关键字段将列表/数据框分组为子集。当您想对多个组进行操作、绘制它们等时,它会很有用。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"
16赞 Redfoot 1/25/2013 #6

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

(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

评论

1赞 user36800 7/6/2015
作为一个新手,我发现在对 x 的 3 次赋值(使用“<-”)中将 x=1 替换为 w=1 以避免与作为“<-”目标的 x 混淆很有帮助
0赞 stevec 6/9/2020
虽然很简单,但我真的很喜欢这个解释。另一个简单的演示:返回一个向量,而返回一个 data.frameiris[[1]]iris[1]
17赞 Jijo Mathew 8/30/2014 #7

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

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

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

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

>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

评论

3赞 MichaelChirico 5/1/2016
投了反对票,因为“传递一个向量......仅仅因为它不是为满足这种需求而建造的,就会导致错误“是不正确的;看看我的新答案。
4赞 dabsingh 1/4/2019
投了反对票,因为它提出了强烈的主张,例如“虽然双括号永远不会返回列表”。这不是真的 - 如果我们有一个对象是列表列表,双括号将返回另一个列表。
0赞 TokyoToo 6/11/2020
返回列表类,即使它是个位数,这一事实也是非常不直观的。他们应该创建另一种语法,例如列表,并且访问实际元素就可以了。我更愿意像其他语言一样将其视为原始值。[]([])[[]][[]]
0赞 D3SL 7/26/2021
这在客观上是不正确的,因为其他人已经说过,如果这是选定的元素,他会很乐意返回一个列表。正确答案是将所选项目作为其父项的子集返回,同时返回原始选定项本身,而不返回其父对象。[[[[[
24赞 MichaelChirico 5/1/2016 #8

只是在这里添加,它也配备了递归索引[[

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

如 中所述,像 这样的语法,其中 ,被解释为:?"[["x[[y]]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]]

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

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

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

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

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

评论

0赞 Therii 11/14/2018
您好 Micheal 先生,我们可以使用 [[]] 进行多重索引吗??
22赞 submartingale 8/7/2017 #9

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

129赞 jzadra 4/7/2018 #10

来自哈德利·威克姆(Hadley Wickham):

From Hadley Wickham

我的(看起来很蹩脚的)修改以显示使用 tidyverse / purrr:

enter image description here

评论

4赞 Steve Pitchers 12/17/2018
凉!你已经得到了格蕾丝·霍珀(Grace Hopper)的皮秒
0赞 jzadra 12/30/2019
@StevePitchers吧?
3赞 Steve Pitchers 3/10/2020
格蕾丝·霍珀(Grace Hopper)在莱特曼(Letterman)上展示纳秒 dailymotion.com/video/x35dsz7
-1赞 Prasan Karunarathna 9/5/2018 #11

请参考以下详细说明。

我在 R 中使用了内置数据帧,称为 mtcars。

> mtcars 
               mpg cyl disp  hp drat   wt ... 
Mazda RX4     21.0   6  160 110 3.90 2.62 ... 
Mazda RX4 Wag 21.0   6  160 110 3.90 2.88 ... 
Datsun 710    22.8   4  108  93 3.85 2.32 ... 
           ............

表的第一行称为标题,其中包含列名。之后的每条水平线表示一个数据行,该行以行的名称开头,然后是实际数据。 行的每个数据成员都称为一个单元格。

单方括号“[]”运算符

要检索单元格中的数据,我们将在方括号“[]”运算符中输入其行和列坐标。两个坐标之间用逗号分隔。换句话说,坐标以行位置开始,后跟逗号,最后以列位置结束。顺序很重要。

例如 1:- 这是 mtcars 第一行第二列的单元格值。

> mtcars[1, 2] 
[1] 6

例如 2:- 此外,我们可以使用行名和列名代替数字坐标。

> mtcars["Mazda RX4", "cyl"] 
[1] 6 

双方括号 “[[]]” 运算符

我们使用双方括号“[[]]”运算符引用数据框列。

例如 1:- 为了检索内置数据集 mtcars 的第九列向量,我们编写 mtcars[[9]]。

mtcars[[9]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...

例如 2:- 我们可以通过其名称检索相同的列向量。

MTCARS[[“AM”]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...