Różnica między nawiasami [] i podwójnymi nawiasami [[]] w celu uzyskania dostępu do elementów listy lub ramki danych

521

R zapewnia dwie różne metody dostępu do elementów listy lub data.frame: []i [[]].

Jaka jest różnica między nimi, w jakich sytuacjach powinienem używać jednego nad drugim?

Sharpie
źródło

Odpowiedzi:

327

Definicja języka R jest przydatna w przypadku odpowiedzi na tego rodzaju pytania:

R ma trzy podstawowe operatory indeksowania, a składnia jest wyświetlana w poniższych przykładach

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

W przypadku wektorów i macierzy [[formy są rzadko używane, chociaż mają one pewne nieznaczne różnice semantyczne w stosunku do [formy (np. Upuszcza wszelkie atrybuty nazw lub dimnames, a indeksowanie znaków stosuje się częściowe dopasowanie). Podczas indeksowania struktur wielowymiarowych za pomocą jednego indeksu x[[i]]lub x[i]zwróci ith kolejny element x.

W przypadku list zwykle używa się [[do wyboru dowolnego pojedynczego elementu, a [zwraca listę wybranych elementów.

[[Forma pozwala tylko jeden element, który jest zaznaczony za pomocą liczb całkowitych lub postaci wskaźników, przy czym [umożliwia indeksowanie wektorów. Należy jednak pamiętać, że dla listy indeksem może być wektor, a każdy element wektora jest stosowany kolejno do listy, wybranego komponentu, wybranego komponentu tego komponentu i tak dalej. Wynik jest nadal jednym elementem.

ars
źródło
6
Jakie jest uzasadnienie używania [[vs [do indeksowania pojedynczą liczbą vs wektorem? Dlaczego nie użyć po prostu [dla obu? Wydaje mi się, że można użyć [[, aby zwrócić pojedynczy wpis, a [z jednym indeksem zwraca listę o długości 1 ... ale dlaczego nie zrobić po prostu [zwrócić pojedynczy wpis z jednym indeksem zamiast listy? Dlaczego możesz chcieć zwrócić listę o długości 1?
wordsforthewise
4
@ słowem, podczas programowania możesz mieć wektor o nieokreślonej długości, którego chcesz użyć do indeksowania. Mając [zawsze zwróci listę środków, które można uzyskać tę samą klasę wyjściowy dla x[v]niezależnie od długości v. Na przykład, może chcieć lapplyponad podzbiór listy: lapply(x[v], fun). Gdyby [upuścić listę wektorów o długości 1, zwróciłoby to błąd, ilekroć vma długość 1.
Axeman
1
Myślę, że to wyjaśnia jaśniej, adv-r.had.co.nz/Subsetting.html
The Red Pea
171

Istotne różnice między dwiema metodami to klasa obiektów, które zwracają, gdy są używane do ekstrakcji, oraz to, czy mogą zaakceptować zakres wartości, czy tylko jedną wartość podczas przypisywania.

Rozważ przypadek ekstrakcji danych na poniższej liście:

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

Powiedzmy, że chcielibyśmy wyodrębnić wartość przechowywaną przez bool z foo i użyć jej w if()instrukcji. Zilustruje to różnice między wartościami zwracanymi []i [[]]kiedy są one używane do ekstrakcji danych. W []sposób powraca Przedmiotem liście klasy (lub data.frame jeśli foo był data.frame), natomiast[[]] powraca Sposób przedmioty, których klasa jest określana przez rodzaj ich wartości.

Tak więc użycie tej []metody powoduje:

if( foo[ 'bool' ] ){ print("Hi!") }
Error in if (foo["bool"]) { : argument is not interpretable as logical

class( foo[ 'bool' ] )
[1] "list"

Wynika to z tego, że []metoda zwróciła listę, a lista nie jest prawidłowym obiektem do przekazania bezpośrednio do if()instrukcji. W tym przypadku musimy użyć, [[]]ponieważ zwróci „nagi” obiekt przechowywany w 'bool', który będzie miał odpowiednią klasę:

if( foo[[ 'bool' ]] ){ print("Hi!") }
[1] "Hi!"

class( foo[[ 'bool' ]] )
[1] "logical"

Druga różnica polega na tym, że []operator może być użyty do uzyskania dostępu do zakresu szczelin na liście lub kolumnach w ramce danych, podczas gdy [[]]operator jest ograniczony do dostępu do pojedynczego slotu lub kolumny. Rozważ przypadek przypisania wartości za pomocą drugiej listy bar():

bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )

Powiedzmy, że chcemy zastąpić ostatnie dwa miejsca foo danymi zawartymi w takcie. Jeśli spróbujemy użyć [[]]operatora, dzieje się tak:

foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar : 
more elements supplied than there are to replace

Jest tak, ponieważ [[]]ogranicza się do uzyskania dostępu do pojedynczego elementu. Musimy użyć []:

foo[ 2:3 ] <- bar
print( foo )

$str
[1] "R"

$vec
     [,1] [,2]
[1,]    0    0
[2,]    0    0

$bool
[1] -0.6291121

Zwróć uwagę, że podczas gdy przypisanie zakończyło się powodzeniem, sloty w foo zachowały swoje oryginalne nazwy.

Sharpie
źródło
111

Podwójne nawiasy otwierają dostęp do elementu listy , a pojedynczy nawias zwraca listę z jednym elementem.

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

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

a <- lst[[1]]
class(a)
## returns "character"
medriscoll
źródło
66

Od Hadley Wickham:

Od Hadley Wickham

Moja (kiepsko wyglądająca) modyfikacja do wyświetlania za pomocą tidyverse / purrr:

wprowadź opis zdjęcia tutaj

jzadra
źródło
2
Fajne! Masz kilka pikosekund Grace Hopper !
Steve Pitchers
@ StevePitchers huh?
jzadra
Grace Hopper w Letterman, pokazując nanosekundę dailymotion.com/video/x35dsz7 .
Steve Pitchers
48

[]wyodrębnia listę, [[]]wyodrębnia elementy na liście

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"
Brad Gilbert
źródło
18

Wystarczy dodać tutaj, że [[jest on również przystosowany do indeksowania rekurencyjnego .

Wskazano na to w odpowiedzi @JijoMatthew, ale nie zostało to zbadane.

Jak zaznaczono w ?"[[", składnia typu jak x[[y]], gdzie length(y) > 1jest interpretowana jako:

x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]

Zauważ, że to nie zmienia tego, co powinno być twoim głównym wynosieniem różnicy między [i [[- mianowicie, że ten pierwszy służy do podzbioru , a drugi do wyodrębniania pojedynczych elementów listy.

Na przykład,

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

Aby uzyskać wartość 3, możemy:

x[[c(2, 1, 1, 1)]]
# [1] 3

Wracając do powyższej odpowiedzi @ JijoMatthew, przypomnij sobie r:

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

W szczególności wyjaśnia to błędy, które często popełniamy przy niewłaściwym użyciu [[, a mianowicie:

r[[1:3]]

Błąd w r[[1:3]]: indeksowanie rekurencyjne nie powiodło się na poziomie 2

Ponieważ ten kod faktycznie próbował ocenić r[[1]][[2]][[3]], a zagnieżdżanie rzatrzymań na poziomie pierwszym, próba wyodrębnienia za pomocą indeksowania rekurencyjnego zakończyła się niepowodzeniem [[2]], tj. Na poziomie 2.

Błąd w r[[c("foo", "far")]]: indeks dolny poza granicami

Tutaj R szukał r[["foo"]][["far"]], który nie istnieje, więc uzyskujemy błąd indeksu poza granicami.

Prawdopodobnie byłoby trochę bardziej pomocne / spójne, gdyby oba te błędy dawały ten sam komunikat.

MichaelChirico
źródło
Witaj, panie Micheal, czy możemy użyć [[]] do wielokrotnego indeksowania?
Therii
14

Oba są sposobami podzbioru. Pojedynczy nawias zwróci podzbiór listy, który sam w sobie będzie listą. tzn .: może zawierać więcej niż jeden element. Z drugiej strony podwójny nawias zwróci tylko jeden element z listy.

-Jeden wspornik da nam listę. Możemy również użyć pojedynczego nawiasu, jeśli chcemy zwrócić wiele elementów z listy. rozważ następującą listę:

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

Teraz zwróć uwagę na sposób zwracania listy, gdy próbuję ją wyświetlić. Wpisuję r i naciskam Enter

>r

#the result is:-

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1

$far

[1] 2

Teraz zobaczymy magię pojedynczego nawiasu: -

>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

co jest dokładnie takie samo, jak gdy próbowaliśmy wyświetlić wartość r na ekranie, co oznacza, że ​​użycie pojedynczego nawiasu zwróciło listę, gdzie w indeksie 1 mamy wektor 10 elementów, a następnie mamy dwa kolejne elementy o nazwach foo i daleko. Możemy również podać pojedynczy indeks lub nazwę elementu jako dane wejściowe do pojedynczego nawiasu. na przykład:

> r[1]

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

W tym przykładzie podaliśmy jeden indeks „1”, aw zamian otrzymaliśmy listę z jednym elementem (którym jest tablica 10 liczb)

> r[2]

$foo

[1] 1

W powyższym przykładzie podaliśmy jeden indeks „2”, aw zamian otrzymaliśmy listę z jednym elementem

> r["foo"];

$foo

[1] 1

W tym przykładzie przekazaliśmy nazwę jednego elementu, aw zamian zwrócono listę z jednym elementem.

Możesz także przekazać wektor nazw elementów, takich jak:

> x<-c("foo","far")

> r[x];

$foo

[1] 1

$far
[1] 2

W tym przykładzie przekazaliśmy wektor o dwóch nazwach elementów „foo” i „far”

W zamian otrzymaliśmy listę z dwoma elementami.

Krótko mówiąc, pojedynczy nawias zawsze zwraca kolejną listę z liczbą elementów równą liczbie elementów lub liczbie indeksów, które przekazujesz do pojedynczego nawiasu.

Natomiast podwójny nawias zawsze zwraca tylko jeden element. Przed przejściem do podwójnego nawiasu należy pamiętać o tym. 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.

Przedstawię kilka przykładów. Zapisz pogrubioną czcionkę i wróć do niej po zakończeniu poniższych przykładów:

Podwójny nawias zwróci rzeczywistą wartość w indeksie ( NIE zwróci listy)

  > r[[1]]

     [1]  1  2  3  4  5  6  7  8  9 10


  >r[["foo"]]

    [1] 1

w przypadku podwójnych nawiasów, jeśli spróbujemy wyświetlić więcej niż jeden element, przekazując wektor, spowoduje to błąd tylko dlatego, że nie został zbudowany, aby zaspokoić tę potrzebę, ale po prostu zwrócić pojedynczy element.

Rozważ następujące

> 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
Jijo Mathew
źródło
1
Przegłosowane, ponieważ „przekazanie wektora ... spowoduje błąd tylko dlatego, że nie został zbudowany w celu zaspokojenia tej potrzeby” jest niepoprawne; zobacz moją nową odpowiedź.
MichaelChirico
1
Odebrano, ponieważ zawiera silne stwierdzenia, takie jak: „PODCZAS PODWÓJNEGO UCHWYTU NIGDY NIE ZWRACA LISTY”. To nieprawda - jeśli mamy obiekt, którym jest lista list, podwójny nawias zwróci kolejną listę.
dabsingh
13

Aby pomóc początkującym w poruszaniu się po ręcznej mgle, pomocne może być postrzeganie [[ ... ]]notacji jako funkcji zwijania - innymi słowy, to wtedy, gdy chcesz po prostu „pobrać dane” z nazwanego wektora, listy lub ramki danych. Dobrze jest to zrobić, jeśli chcesz wykorzystać dane z tych obiektów do obliczeń. Te proste przykłady to zilustrują.

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

Tak więc z trzeciego przykładu:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2
Redfoot
źródło
1
Jako nowicjusz, znalazłem pomoc w 3 przypisaniach do x (używając „<-”), aby zastąpić x = 1 w = 1, aby uniknąć pomyłki z x, który jest celem „<-”
user36800
Chociaż bardzo proste, naprawdę podoba mi się to wyjaśnienie. Kolejna prosta demonstracja: iris[[1]]zwraca wektor, podczas gdy iris[1]zwraca data.frame
stevec
11

Będąc terminologicznym, [[operator wyodrębnia element z listy, podczas gdy [operator pobiera podzbiór listy.

submartingale
źródło
7

W jeszcze innym konkretnym przypadku użycia użyj podwójnych nawiasów, aby wybrać ramkę danych utworzoną przez split()funkcję. Jeśli nie wiesz, split()grupujesz ramkę listy / danych w podzbiory na podstawie pola klucza. Jest to przydatne, jeśli chcesz operować na wielu grupach, wykreślić je itp.

> 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"
Piotr
źródło
-1

Proszę zapoznać się ze szczegółowym wyjaśnieniem poniżej.

Użyłem wbudowanej ramki danych w języku R, zwanej 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 ... 
           ............

Górny wiersz tabeli nazywa się nagłówkiem, który zawiera nazwy kolumn. Każda linia pozioma następnie oznacza wiersz danych, który zaczyna się od nazwy wiersza, a następnie następuje rzeczywiste dane. Każdy element danych wiersza jest nazywany komórką.

operator „[]” z pojedynczym nawiasiem kwadratowym

Aby pobrać dane z komórki, wprowadzilibyśmy współrzędne jej wiersza i kolumny w operatorze „[]” z pojedynczym nawiasiem kwadratowym. Dwie współrzędne są oddzielone przecinkiem. Innymi słowy, współrzędne zaczynają się od pozycji wiersza, po której następuje przecinek, a kończy na pozycji kolumny. Kolejność jest ważna.

Np. 1: - Oto wartość komórki z pierwszego wiersza, drugiej kolumny mtcars.

> mtcars[1, 2] 
[1] 6

Np. 2: - Ponadto możemy użyć nazw wierszy i kolumn zamiast współrzędnych numerycznych.

> mtcars["Mazda RX4", "cyl"] 
[1] 6 

Podwójny nawias kwadratowy operator „[[]]”

Odwołujemy się do kolumny ramki danych za pomocą operatora podwójnego nawiasu kwadratowego „[[]]”.

Np. 1: - Aby pobrać dziewiąty wektor wbudowanego zestawu danych mtcars, piszemy mtcars [[9]].

mtcars [[9]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...

Np. 2: - Możemy pobrać ten sam wektor kolumny według jego nazwy.

mtcars [["am"]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...

Prasan Karunarathna
źródło
-1

Dodatkowo:

Po LINK z ODPOWIEDŹ tutaj.

Oto mały przykład odnoszący się do następującego punktu:

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])
Andre Elrico
źródło