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


Ответы:


327

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

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

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

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

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

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


6
Какова причина использования [[vs [для индексации с одним числом против вектора? Почему бы просто не использовать [для обоих? Я предполагаю, что вы можете использовать [[чтобы вернуть одну запись, и [с одним индексом возвращает список длиной 1 ... но почему бы просто не сделать [вернуть одну запись с одним индексом вместо списка? Почему вы можете захотеть вернуть список длины 1?
словами

4
@wordsforhewise, при программировании у вас может быть вектор неопределенной длины, который вы хотите использовать для индексации. Наличие [всегда возвращаемого списка означает, что вы получаете один и тот же класс вывода x[v]независимо от длины v. Например, один может понадобиться lapplyболее чем подмножество списка: lapply(x[v], fun). Если [бы исключить список для векторов длины один, это вернуло бы ошибку всякий раз, когда vимеет длину один.
Axeman

1
Я думаю, что это объясняется более четко, adv-r.had.co.nz/Subsetting.html
The Red Pea

171

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

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

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

Скажем, мы хотели бы извлечь значение, сохраненное в bool, из foo и использовать его внутри if()оператора. Это проиллюстрирует различия между возвращаемыми значениями []и [[]]когда они используются для извлечения данных. В []метод возвращает список объектов класса (или data.frame , если Foo был 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) )

Скажем, мы хотим перезаписать последние два слота 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 сохранили свои первоначальные имена.


111

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

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

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

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

66

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

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

Моя (на вид дрянная) модификация для отображения с использованием tidyverse / purrr:

введите описание изображения здесь


2
Круто! У тебя есть пикосекунды Грейс Хоппер !
Стив Питчерс

@ StevePitchers, а?
Джадра

Грейс Хоппер на Леттермане демонстрирует нано секунды dailymotion.com/video/x35dsz7 .
Стив Питчерс

48

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

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"

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"]], чего не существует, поэтому мы получаем ошибку индекса за пределами границ.

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


Здравствуйте, Майкл, сэр, мы можем использовать [[]] для множественной индексации ??
Therii

14

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

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

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

Я приведу несколько примеров. Пожалуйста, запишите слова жирным шрифтом и вернитесь к ним после того, как закончите с примерами ниже:

Двойная скобка вернет вам фактическое значение по индексу. ( НЕ вернет список)

  > 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

1
Недопустимо, потому что «передача вектора ... приведет к ошибке только потому, что он не был создан для удовлетворения этой потребности» - неверно; смотри мой новый ответ.
MichaelChirico

1
Понижен, потому что это делает сильные заявления как, "КОГДА ДВОЙНОЙ КРОНШТЕЙН НИКОГДА НЕ ВОЗВРАЩАЕТ СПИСОК". Это не так - если у нас есть объект, представляющий собой список списков, двойная скобка вернет другой список.
Дабсингх

Тот факт, что []класс списка возвращается, даже если он представляет собой одну цифру, очень не интуитивно понятен. Им следовало создать другой синтаксис, подобный ([])списку, и [[]]для доступа к фактическому элементу все в порядке. Я предпочитаю воспринимать [[]]как необработанную ценность, как и в других языках.
Токио

13

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

(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
Как новичок, я нашел полезным в 3 назначениях для x (используя «<-») заменить x = 1 на w = 1, чтобы избежать путаницы с x, который является целью «<-»
user36800

Хотя это очень просто, мне очень нравится это объяснение. Еще одна простая демонстрация: iris[[1]]возвращает вектор, тогда как iris[1]возвращает data.frame
stevec

11

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


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"

-1

Пожалуйста, обратитесь к подробному объяснению ниже.

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


-1

К тому же:

После LINK в ОТВЕТА здесь.

Вот небольшой пример, посвященный следующему пункту:

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])
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.