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

时间:2009-07-23 03:33:18

标签: r list dataframe extract r-faq

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

两者有什么区别?我应该在什么情况下使用其中一个?

12 个答案:

答案 0 :(得分:287)

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

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

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

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

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

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

答案 1 :(得分:161)

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

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

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

假设我们想从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"

这是因为[]方法返回了一个列表,而列表不是有效的对象,而是直接传递给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中的插槽保留了原始名称。

答案 2 :(得分:100)

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

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

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

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

答案 3 :(得分:45)

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

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"

答案 4 :(得分:42)

来自Hadley Wickham:

From Hadley Wickham

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

enter image description here

答案 5 :(得分:18)

只需在此处添加[[也可用于递归索引

@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]]
  

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

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

  

r[[c("foo", "far")]]:下标超出范围

时出错

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

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

答案 6 :(得分:13)

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

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

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

> r[1]

[[1]]

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

在这个例子中,我们给出了一个索引&#34; 1&#34;并获得一个包含一个元素的列表(这是一个包含10个数字的数组)

> r[2]

$foo

[1] 1

在上面的例子中,我们给出了一个索引&#34; 2&#34;并获得一个包含一个元素的列表

> r["foo"];

$foo

[1] 1

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

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

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

> r[x];

$foo

[1] 1

$far
[1] 2

在这个例子中,我们传递了一个带有两个元素名称的向量&#34; foo&#34;和&#34;远&#34;

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

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

相比之下,双括号始终只返回一个元素。 在移动到双支架之前要记住一个注意事项。 的 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.

我将举几个例子。请记下粗体字,并在完成以下示例后再回过头来看:

双括号将返回索引处的实际值。( NOT 返回列表)

  > 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

答案 7 :(得分: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]]

所以从第三个例子来看:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2

答案 8 :(得分:10)

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

答案 9 :(得分: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"

答案 10 :(得分:0)

此外:

在此处跟随L I N KA N S W E R

这是解决以下问题的一个小例子:

x[i, j] vs x[[i, j]]

df1   <- data.frame(a = 1:3)
df1$b <- list(4:5, 6:7, 8:9)

df1[[1,2]]
df1[1,2]

str(df1[[1,2]])
str(df1[1,2])

答案 11 :(得分:-1)

请参考以下详细说明。

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

$(document).ready(function () {
    // Create random number between 19 and 120
    var number = Math.floor(Math.random() * (120 - 19 + 1) + 19);
    console.log(number);
    //show random number to user

    var randomNumberField = $("#randomNumber");
    randomNumberField.text(number);

    //Create crystals/buttons
    var Crystals = [1, 2, 3, 4];


    for (var i = 0; i < Crystals.length; i++) {
        var crystalNumber = Math.floor(Math.random() * (12 - 1 + 1) + 1);
        console.log(crystalNumber);

        if ($("crystalNumber") === $("Crystals")) {
            crystalNumber = Math.floor(Math.random() * (12 - 1 + 1) + 1);
        };

        else(var i = 0; i < Crystals.length; i++)

        var crystalImage = $("<img>");


    }



}); // end of script

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

单个方括号“ []”运算符

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

例如1:-这是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 ... 
           ............

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

> mtcars[1, 2] 
[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 ...