Разница между скобкой [] и двойной скобкой [[]] для доступа к элементам списка или фрейма данных

R предоставляет два различных метода доступа к элементам списка или данных.кадр-то [] и [[]] операторы.

в чем разница между этими двумя? В каких ситуациях я должен использовать один над другим?

11 ответов


определение языка R удобно для ответа на эти типы вопросов:

R имеет три основных оператора индексирования, синтаксис которых показан в следующих примерах

Для векторов и матриц [[ формы редко используются, хотя они имеют некоторые незначительные семантические отличия от [ форма (например, она отбрасывает любые имена или атрибуты dimnames, и это частичное сопоставление используется для индексов символов). При индексации многомерных структур с одним индексом,x[[i]] или x[i] вернет iй последовательный элемент x.

Для списков обычно используется [[ для выбора любого отдельного элемента, тогда как [ возвращает список выбранных элементов.

The [[ форма позволяет выбрать только один элемент с помощью целочисленные или символьные индексы, тогда как [ позволяет индексировать по векторам. Обратите внимание, что для списка индекс может быть вектором, и каждый элемент вектора применяется по очереди к списку, выбранному компоненту, выбранному компоненту этого компонента и т. д. В результате остается один элемент.


существенными различиями между двумя методами являются класс объектов, которые они возвращают при использовании для извлечения, и могут ли они принимать диапазон значений или только одно значение во время назначения.

рассмотрим случай извлечения данных из следующего списка:

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

скажем, мы хотели бы извлечь значение, сохраненное bool из foo, и использовать его внутри if() заявление. Это проиллюстрирует различия между возвращаемыми значениями [] и [[]], когда они используются для извлечения данных. The [] метод возвращает объекты списка классов (или данные.кадр, если фу был данными.рамы), а [[]] метод возвращает объекты, класс которых определяется типом их значений.

Итак, используя [] метод приводит к следующему:

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

скажем, мы хотим перезаписать последние два слота foo данными, содержащимися в bar. Если мы попытаемся использовать [[]] оператор, вот что происходит:

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 сохранили свои оригинальные имена.


двойные скобки обращаются к списку элемент, в то время как одна скобка возвращает список с одним элементом.

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

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

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

[] извлечение списка, [[]] извлекает элементы из списка

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"

От Хэдли Уикхем:

From Hadley Wickham

моя (дерьмовая) модификация для показа с помощью tidyverse / purrr:

enter image description here


просто добавив сюда, что [[ также оборудован для рекурсивные индексации.

на это намекнул в ответе @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")]]: subscript out of bounds

вот, R искал r[["foo"]][["far"]], которого не существует, поэтому мы получаем ошибку subscript out of bounds.

это, вероятно, было бы немного больше полезно / согласованно, если обе эти ошибки дали одно и то же сообщение.


оба они являются способами подстановки. Одиночная скобка вернет подмножество списка, которое само по себе будет списком. ie: он может содержать или не содержать более одного элемента. С другой стороны, двойная скобка вернет только один элемент из списка.

-одиночный кронштейн передаст нам список. Мы также можем использовать одну скобку, если хотим вернуть несколько элементов из списка. рассмотрим следующий список:-

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

теперь обратите внимание, как список возвращается, когда я пытаюсь его отобразить. Я набираю r и нажимаю enter

>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

чтобы помочь новичкам перемещаться по ручному туману, может быть полезно увидеть [[ ... ]] нотация как рушится функция-другими словами, это когда вы просто хотите "получить данные" из именованного вектора, списка или фрейма данных. Это хорошо сделать, если вы хотите использовать данные из этих объектов для вычислений. Проиллюстрируем это простыми примерами.

(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

для еще одного конкретного случая использования используйте двойные скобки, когда вы хотите выбрать фрейм данных, созданный


быть терминологического, [[ оператор выдержки элемент из списка, тогда как [ оператор подмножество из списка.


пожалуйста см. ниже-детальное объяснение.

я использовал встроенный фрейм данных в 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 ...