Der Unterschied zwischen Klammer [] und doppelter Klammer [[]] für den Zugriff auf die Elemente einer Liste oder eines Datenrahmens


521

R bietet zwei verschiedene Methoden für den Zugriff auf die Elemente einer Liste oder eines Datenrahmens: []und [[]].

Was ist der Unterschied zwischen den beiden und in welchen Situationen sollte ich eine über die andere verwenden?

Antworten:


327

Die R-Sprachdefinition ist praktisch, um folgende Fragen zu beantworten:

R verfügt über drei grundlegende Indizierungsoperatoren, deren Syntax in den folgenden Beispielen dargestellt wird

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

Für Vektoren und Matrizen werden die [[Formulare selten verwendet, obwohl sie einige geringfügige semantische Unterschiede zum [Formular aufweisen (z. B. werden Namen oder Dimnames-Attribute gelöscht, und diese teilweise Übereinstimmung wird für Zeichenindizes verwendet). Wenn Sie mehrdimensionale Strukturen mit einem einzelnen Index indizieren x[[i]]oder x[i]das idritte sequentielle Element von zurückgeben x.

Bei Listen wird im Allgemeinen [[ein einzelnes Element ausgewählt, während [eine Liste der ausgewählten Elemente zurückgegeben wird.

Das [[Formular ermöglicht die Auswahl nur eines einzelnen Elements mithilfe von Ganzzahl- oder Zeichenindizes, während [die Indizierung nach Vektoren möglich ist. Beachten Sie jedoch, dass für eine Liste der Index ein Vektor sein kann und jedes Element des Vektors der Reihe nach auf die Liste, die ausgewählte Komponente, die ausgewählte Komponente dieser Komponente usw. angewendet wird. Das Ergebnis ist immer noch ein einzelnes Element.


6
Was ist der Grund für die Verwendung von [[vs [zum Indizieren mit einer einzelnen Zahl vs Vektor? Warum nicht einfach [für beide verwenden? Ich denke, Sie können das [[verwenden, um einen einzelnen Eintrag zurückzugewinnen, und [mit einem Index gibt eine Liste der Länge 1 zurück ... aber warum nicht einfach [einen einzelnen Eintrag mit einem Index anstelle einer Liste zurückgeben? Warum möchten Sie vielleicht jemals eine Liste mit der Länge 1 zurückgeben?
Worte für den

4
@wordsforthewise, beim Programmieren können Sie einen Vektor undefinierter Länge haben, den Sie für die Indizierung verwenden möchten. Wenn Sie [immer eine Liste zurückgeben, erhalten Sie x[v]unabhängig von der Länge von dieselbe Ausgabeklasse v. Zum Beispiel könnte man lapplyüber eine Teilmenge einer Liste gehen wollen : lapply(x[v], fun). Wenn [die Liste für Vektoren der Länge eins gelöscht würde, würde dies einen Fehler zurückgeben, wenn vdie Länge eins ist.
Axeman

1
Ich denke, das erklärt klarer, adv-r.had.co.nz/Subsetting.html
Die rote Erbse

171

Die signifikanten Unterschiede zwischen den beiden Methoden sind die Klasse der Objekte, die sie bei der Extraktion zurückgeben, und ob sie einen Wertebereich oder nur einen einzelnen Wert während der Zuweisung akzeptieren.

Betrachten Sie den Fall der Datenextraktion in der folgenden Liste:

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

Angenommen, wir möchten den von bool gespeicherten Wert aus foo extrahieren und in einer if()Anweisung verwenden. Dies zeigt die Unterschiede zwischen den Rückgabewerten von []und [[]]wann sie für die Datenextraktion verwendet werden. Die []Methode gibt Objekte der Klassenliste (oder data.frame, wenn foo ein data.frame war) zurück, während die [[]]Methode Objekte zurückgibt, deren Klasse durch den Typ ihrer Werte bestimmt wird.

Die Verwendung der []Methode führt also zu folgenden Ergebnissen:

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

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

Dies liegt daran, dass die []Methode eine Liste zurückgegeben hat und eine Liste kein gültiges Objekt ist, das direkt an eine if()Anweisung übergeben werden kann. In diesem Fall müssen wir verwenden, [[]]da es das in 'bool' gespeicherte "nackte" Objekt zurückgibt, das die entsprechende Klasse hat:

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

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

Der zweite Unterschied besteht darin, dass der []Operator verwendet werden kann, um auf einen Bereich von Slots in einer Liste oder Spalten in einem Datenrahmen [[]]zuzugreifen, während der Operator auf den Zugriff auf einen einzelnen Slot oder eine einzelne Spalte beschränkt ist. Betrachten Sie den Fall der Wertzuweisung anhand einer zweiten Liste bar():

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

Angenommen, wir möchten die letzten beiden foo-Slots mit den in bar enthaltenen Daten überschreiben. Wenn wir versuchen, den [[]]Operator zu verwenden, geschieht Folgendes:

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

Dies liegt daran, dass [[]]nur auf ein einzelnes Element zugegriffen werden kann. Wir müssen verwenden []:

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

$str
[1] "R"

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

$bool
[1] -0.6291121

Beachten Sie, dass die Slots in foo während der erfolgreichen Zuweisung ihre ursprünglichen Namen beibehalten haben.


111

Doppelbügel greift auf eine Liste Element , während eine einzelne Konsole gibt Ihnen eine Liste mit einem einzelnen Element zurück.

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

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

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


48

[]extrahiert eine Liste, [[]]extrahiert Elemente innerhalb der Liste

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

Fügen Sie hier nur das hinzu, das [[auch für die rekursive Indizierung ausgestattet ist .

Dies wurde in der Antwort von @JijoMatthew angedeutet, aber nicht untersucht.

Wie in erwähnt ?"[[", wird Syntax wie x[[y]], wo length(y) > 1, interpretiert als:

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

Beachten Sie, dass dies nichts daran ändert, was Ihr Hauptgrund für den Unterschied zwischen [und sein sollte [[- nämlich, dass ersteres für die Teilmenge und letzteres für das Extrahieren einzelner Listenelemente verwendet wird.

Zum Beispiel,

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

Um den Wert 3 zu erhalten, können wir Folgendes tun:

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

Zurück zu @ JijoMatthews Antwort oben, erinnern Sie sich r:

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

Dies erklärt insbesondere die Fehler, die bei Missbrauch auftreten können [[, nämlich:

r[[1:3]]

Fehler in r[[1:3]]: Rekursive Indizierung auf Ebene 2 fehlgeschlagen

Da dieser Code tatsächlich versucht hat, zu bewerten r[[1]][[2]][[3]], und das Verschachteln von rStopps auf Ebene 1 fehlschlug, schlug der Versuch, durch rekursive Indizierung zu extrahieren [[2]], auf Ebene 2 fehl .

Fehler in r[[c("foo", "far")]]: Index außerhalb der Grenzen

Hier hat R gesucht r[["foo"]][["far"]], was nicht existiert, also bekommen wir den Index-Out-of-Bound-Fehler.

Es wäre wahrscheinlich etwas hilfreicher / konsistenter, wenn beide Fehler dieselbe Meldung liefern würden.


Hallo Micheal Sir, können wir [[]] für die Mehrfachindizierung verwenden?
Therii

14

Beide sind Teilmengen. Die einzelne Klammer gibt eine Teilmenge der Liste zurück, die an sich eine Liste ist. dh: Es kann mehr als ein Element enthalten oder nicht. Andererseits gibt eine doppelte Klammer nur ein einzelnes Element aus der Liste zurück.

- Eine einzelne Klammer gibt uns eine Liste. Wir können auch eine einzelne Klammer verwenden, wenn wir mehrere Elemente aus der Liste zurückgeben möchten. Betrachten Sie die folgende Liste: -

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

Bitte beachten Sie nun, wie die Liste zurückgegeben wird, wenn ich versuche, sie anzuzeigen. Ich tippe r und drücke die Eingabetaste

>r

#the result is:-

[[1]]

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

$foo

[1] 1

$far

[1] 2

Jetzt werden wir die Magie einer einzelnen Klammer sehen: -

>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

Dies ist genau das gleiche wie beim Versuch, den Wert von r auf dem Bildschirm anzuzeigen. Dies bedeutet, dass die Verwendung einer einzelnen Klammer eine Liste zurückgegeben hat, in der wir bei Index 1 einen Vektor mit 10 Elementen haben und dann zwei weitere Elemente mit den Namen foo und weit. Wir können auch einen einzelnen Index- oder Elementnamen als Eingabe für die einzelne Klammer angeben. z.B:

> r[1]

[[1]]

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

In diesem Beispiel haben wir einen Index mit "1" angegeben und im Gegenzug eine Liste mit einem Element (einem Array mit 10 Zahlen) erhalten.

> r[2]

$foo

[1] 1

Im obigen Beispiel haben wir einen Index "2" angegeben und im Gegenzug eine Liste mit einem Element erhalten

> r["foo"];

$foo

[1] 1

In diesem Beispiel haben wir den Namen eines Elements übergeben und im Gegenzug wurde eine Liste mit einem Element zurückgegeben.

Sie können auch einen Vektor mit Elementnamen übergeben wie: -

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

> r[x];

$foo

[1] 1

$far
[1] 2

In diesem Beispiel haben wir einen Vektor mit zwei Elementnamen "foo" und "far" übergeben.

Im Gegenzug haben wir eine Liste mit zwei Elementen erhalten.

Kurz gesagt, eine einzelne Klammer gibt Ihnen immer eine andere Liste mit der Anzahl der Elemente zurück, die der Anzahl der Elemente oder der Anzahl der Indizes entspricht, die Sie an die einzelne Klammer übergeben.

Im Gegensatz dazu gibt eine doppelte Klammer immer nur ein Element zurück. Bevor Sie zur doppelten Klammer wechseln, sollten Sie eine Notiz beachten. 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.

Ich werde einige Beispiele nennen. Bitte notieren Sie sich die fett gedruckten Wörter und kehren Sie zurück, nachdem Sie mit den folgenden Beispielen fertig sind:

Die doppelte Klammer gibt Ihnen den tatsächlichen Wert am Index zurück. (Es wird KEINE Liste zurückgegeben.)

  > r[[1]]

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


  >r[["foo"]]

    [1] 1

Wenn wir in doppelten Klammern versuchen, mehr als ein Element durch Übergeben eines Vektors anzuzeigen, führt dies zu einem Fehler, nur weil es nicht für diesen Bedarf erstellt wurde, sondern nur, um ein einzelnes Element zurückzugeben.

Folgendes berücksichtigen

> 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
Downvoted, weil "das Übergeben eines Vektors ... zu einem Fehler führt, nur weil er nicht für diesen Bedarf erstellt wurde" falsch ist; siehe meine neue Antwort.
MichaelChirico

1
Abgestuft, weil es starke Behauptungen wie "WÄHREND EINE DOPPELTE HALTERUNG NIEMALS EINE LISTE ZURÜCKGABE" erhebt. Es ist nicht wahr - wenn wir ein Objekt haben, das eine Liste von Listen ist, gibt die doppelte Klammer eine andere Liste zurück.
Dabsingh

Die Tatsache, dass []eine Listenklasse zurückgegeben wird, auch wenn es sich um eine einzelne Ziffer handelt, ist sehr unintuitiv. Sie hätten eine andere Syntax wie ([])für die Liste erstellen sollen und der [[]]Zugriff auf das eigentliche Element ist in Ordnung. Ich stelle mir [[]]den Rohwert lieber wie in anderen Sprachen vor.
TokyoToo vor

13

Um Neulingen die Navigation durch den manuellen Nebel zu erleichtern, kann es hilfreich sein, die [[ ... ]]Notation als kollabierende Funktion zu betrachten. Mit anderen Worten, wenn Sie nur die Daten von einem benannten Vektor, einer Liste oder einem Datenrahmen abrufen möchten. Dies ist sinnvoll, wenn Sie Daten aus diesen Objekten für Berechnungen verwenden möchten. Diese einfachen Beispiele werden veranschaulichen.

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

Also aus dem dritten Beispiel:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2

1
Als Neuling fand ich es hilfreich, in den 3 Zuweisungen zu x (mit "<-") x = 1 durch w = 1 zu ersetzen, um Verwechslungen mit dem x zu vermeiden, das das Ziel von "<-"
user36800 ist

Obwohl sehr einfach, mag ich diese Erklärung wirklich. Eine weitere einfache Demonstration: Gibt iris[[1]]einen Vektor zurück, während iris[1]
stevec zurückgegeben wird

11

Als Terminologie extrahiert der [[Operator das Element aus einer Liste, während der Operator eine Teilmenge einer Liste übernimmt .[


7

Verwenden Sie für einen weiteren konkreten Anwendungsfall doppelte Klammern, wenn Sie einen von der split()Funktion erstellten Datenrahmen auswählen möchten . Wenn Sie es nicht wissen, split()gruppieren Sie eine Liste / einen Datenrahmen basierend auf einem Schlüsselfeld in Teilmengen. Dies ist nützlich, wenn Sie mehrere Gruppen bearbeiten, zeichnen usw. möchten.

> 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

Bitte beachten Sie die unten stehende ausführliche Erklärung.

Ich habe den in R integrierten Datenrahmen namens mtcars verwendet.

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

Die oberste Zeile der Tabelle wird als Header bezeichnet, der die Spaltennamen enthält. Jede horizontale Linie danach bezeichnet eine Datenzeile, die mit dem Namen der Zeile beginnt, gefolgt von den tatsächlichen Daten. Jedes Datenelement einer Zeile wird als Zelle bezeichnet.

Operator mit einer eckigen Klammer "[]"

Um Daten in einer Zelle abzurufen, geben wir ihre Zeilen- und Spaltenkoordinaten in den Operator "[]" in eckigen Klammern ein. Die beiden Koordinaten sind durch ein Komma getrennt. Mit anderen Worten, die Koordinaten beginnen mit der Zeilenposition, gefolgt von einem Komma und enden mit der Spaltenposition. Die Reihenfolge ist wichtig.

ZB 1: - Hier ist der Zellenwert aus der ersten Zeile, zweiten Spalte von mtcars.

> mtcars[1, 2] 
[1] 6

Beispiel 2: - Außerdem können wir die Zeilen- und Spaltennamen anstelle der numerischen Koordinaten verwenden.

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

Operator mit doppelter eckiger Klammer "[[]]"

Wir verweisen auf eine Datenrahmenspalte mit dem Operator "[[]]" in doppelter eckiger Klammer.

Beispiel 1: - Um den neunten Spaltenvektor des eingebauten Datensatzes mtcars abzurufen, schreiben wir mtcars [[9]].

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

Beispiel 2: - Wir können denselben Spaltenvektor anhand seines Namens abrufen.

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


-1

In Ergänzung:

Folgen Sie dem Link der ANTWORT hier.

Hier ist ein kleines Beispiel, das den folgenden Punkt anspricht:

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])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.