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


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

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

11 422

11 ответов:

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

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

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

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

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

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

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

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

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

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

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"

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

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")]] : индекс находится вне границ

вот, 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 ...