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


521

R надає два різні методи доступу до елементів списку або фрейму даних: []і [[]].

Яка різниця між двома, в яких ситуаціях я повинен використовувати одну над іншою?

Відповіді:


327

Визначення мови R зручно відповідати на такі типи питань:

R має три основні оператори індексації, причому синтаксис відображається наступними прикладами

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

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

Для списків, як правило, використовується [[для вибору будь-якого одного елемента, тоді як [повертає список вибраних елементів.

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


6
Яке міркування використовувати [[vs [] для індексації одним числом проти вектора? Чому б просто не використовувати [для обох? Я думаю, ви можете використовувати [[, щоб повернути один запис, і [з одним індексом повертає список довжиною 1 ... але чому б просто не зробити [повернути один запис з одним індексом замість списку? Чому ви коли-небудь хочете повернути список довжиною-1?
словаззаду

4
@wordsforthewise, при програмуванні ви можете мати вектор невизначеної довжини, який ви хочете використовувати для індексації. Маючи [завжди повертати список означає , що ви отримаєте той же клас виведення для 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"


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[["foo"]][["far"]] , якого не існує, тож ми отримуємо підписник із помилки меж.

Напевно, було б трохи корисніше / послідовніше, якби обидві ці помилки дали одне і те ж повідомлення.


Привіт Мішель, сер, чи можемо ми використати [[] для множинної індексації ??
Терій

14

Обидва вони є способами підмножини. Одинарна дужка поверне підмножину списку, яка сама по собі буде списком. тобто: Він може містити більше, ніж один елемент. З іншого боку, подвійна дужка поверне лише один елемент зі списку.

-Единарна дужка надасть нам список. Ми також можемо використовувати одну дужку, якщо хочемо повернути декілька елементів зі списку. врахуйте наступний перелік: -

>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 і далеко. Ми також можемо вказати ім'я одного індексу або елемента як вхід до однієї дужки. наприклад:

> 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
Незважаючи на те, що вона висловлює сильні претензії на кшталт "КОЛИ ДВОЙНИЙ КРАНТ НІКОЛИ НЕ БУДЕ ВЗАЄМО СПИСОК". Це неправда - якщо у нас є об’єкт, який є списком списків, подвійна дужка поверне інший список.
dabsingh

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 від ANSWER тут.

Ось невеликий приклад щодо наступного моменту:

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.