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

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

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

12 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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"

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

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

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

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

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

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

>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

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

(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

Для еще одного конкретного случая использования используйте двойные скобки, когда вы хотите выбрать фрейм данных, созданный 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"

К тому же:

Следуя ССЫЛКА ОТВЕТА здесь.

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

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

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

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

Другие вопросы по тегам