【问题标题】:The difference between bracket [ ] and double bracket [[ ]] for accessing the elements of a list or dataframe括号 [ ] 和双括号 [[ ]] 用于访问列表或数据框元素的区别
【发布时间】:2019-09-05 21:13:31
【问题描述】:

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

这两者有什么区别,什么时候应该使用其中的一个?

【问题讨论】:

    标签: r list dataframe extract r-faq


    【解决方案1】:

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

    R 具有三个基本的索引运算符,语法由以下示例显示

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

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

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

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

    【讨论】:

    • 使用 [[ vs [ 以单个数字 vs 向量进行索引的原因是什么?为什么不同时使用 [ ?我猜你可以使用 [[ 来取回单个条目,并且 [ 带有一个索引返回一个长度为 1 的列表......但是为什么不让 [ 返回一个带有一个索引而不是列表的条目?为什么您可能希望返回一个长度为 1 的列表?
    • @wordsforthewise,在编程时,您可以有一个未定义长度的向量用于索引。让[ 始终返回一个列表意味着无论v 的长度如何,您都会为x[v] 获得相同的输出类。例如,有人可能想要lapply 覆盖列表的子集:lapply(x[v], fun)。如果 [ 会删除长度为 1 的向量列表,那么只要 v 的长度为 1,就会返回错误。
    • 我觉得这解释的更清楚,adv-r.had.co.nz/Subsetting.html
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        来自哈德利·威克姆:

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

        【讨论】:

        【解决方案5】:

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

        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"
        

        【讨论】:

          【解决方案6】:

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

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

          ?"[[" 中所述,类似x[[y]] 的语法(其中length(y) &gt; 1)被解释为:

          x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]
          

          请注意,这不会改变您对[[[ 之间区别的主要看法——即前者用于子集 strong>,后者用于提取单个列表元素。

          例如,

          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]](即第二级)失败。

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

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

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

          【讨论】:

          • 您好 Micheal 先生,我们可以使用 [[]] 进行多重索引吗??
          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

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

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

            >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”的向量

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

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

            相比之下,双括号总是只返回一个元素。 在移动到双括号之前,请记住一个注意事项。 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
            

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

            考虑以下

            > 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
            

            【讨论】:

            • 投了反对票,因为“传递向量......将导致错误,因为它不是为满足该需求而构建的”是不正确的;看看我的新答案。
            • 投了反对票,因为它提出了“虽然双括号永远不会返回列表”这样的强烈主张。这不是真的 - 如果我们有一个列表对象,双括号将返回另一个列表。
            • [] 会返回一个列表类,即使它是单个数字也是非常不直观的。他们应该为列表创建另一种语法,例如([]),而[[]] 可以访问实际元素。我更喜欢将[[]] 视为其他语言中的原始值。
            • 这在客观上是不正确的,正如其他人所说,[[ 会很高兴地返回一个列表,如果那是选定的元素。正确答案是 [ 将选定项目作为其父对象的子集返回,而 [[ 将原始选定项目本身返回,不包括其父对象。
            【解决方案9】:

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

            (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
            

            【讨论】:

            • 作为一个新手,我发现在对 x 的 3 个分配中(使用“
            • 虽然很简单,但是我很喜欢这个解释。另一个简单的演示:iris[[1]] 返回一个向量,而iris[1] 返回一个 data.frame
            【解决方案10】:

            对于另一个具体用例,当您要选择由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"
            

            【讨论】:

              【解决方案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 的第 9 列向量,我们编写 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 ...

              【讨论】:

                【解决方案12】:

                另外:

                在此处关注A N S W E RL I N K

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

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

                【讨论】:

                  猜你喜欢
                  • 2022-01-12
                  • 1970-01-01
                  • 2013-08-16
                  • 1970-01-01
                  • 2021-05-12
                  • 1970-01-01
                  相关资源
                  最近更新 更多