首页 文章

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

提问于
浏览
435

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

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

12 回答

  • 93

    此外:

    在这里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])
    
  • 14

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

    (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

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

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

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

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

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

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

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

    例如,

    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)
    

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

    r[[1:3]]
    

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

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

    r [[c(“foo”,“far”)]]中的错误:下标超出范围

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

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

  • 152

    来自哈德利威克姆:

    From Hadley Wickham

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

    enter image description here

  • 40

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

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

    在这个例子中,我们给了一个索引“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.

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

    双括号将返回索引处的实际值 . (它将 NOT 返回一个列表)

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

    for double brackets if we try to view more than one elements by passing a vector it will result in an error just because it was not built to cater to that need, but just to return a single element.

    考虑以下

    > 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
    
  • 16

    请参考以下详细说明 .

    我在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 ... 
               ............
    

    表的顶行称为包含列名的 Headers . 之后的每条水平线表示一个数据行,以行的名称开头,然后是实际数据 . 一行中的每个数据成员称为一个单元格 .

    单方括号“[]”运算符

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

    例如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

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

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

    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'中的"bare"对象,该对象将具有相应的类:

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

    第二个区别是 [] 运算符可用于访问列表中的 range 个槽或数据帧中的列,而 [[]] 运算符仅限于访问 single 槽或列 . 考虑使用第二个列表进行值赋值的情况, 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中的插槽保留了原始名称 .

  • 7

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

    R有三个基本的索引操作符,语法由以下示例x [i]显示
    x [i,j]
    X [[I]]
    x [[i,j]]
    X $一个
    X $ “一”
    对于向量和矩阵,[[形式很少使用,尽管它们与[形式]有一些轻微的语义差异(例如,它删除任何名称或dimnames属性,并且部分匹配用于字符索引) . 当使用单个索引索引多维结构时,x [[i]]或x [i]将返回x的第i个连续元素 . 对于列表,通常使用[[选择任何单个元素,而[返回所选元素的列表] . [[表单只允许使用整数或字符索引选择单个元素,而[允许通过向量进行索引] . 请注意,对于列表,索引可以是向量,向量的每个元素依次应用于列表,选定组件,该组件的选定组件等 . 结果仍然只是一个元素 .

  • 7

    作为术语, [[ 运算符 extracts 列表中的元素,而 [ 运算符取列表的 subset .

  • 274

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

    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"
    
  • -1

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

相关问题