La differenza tra parentesi [] e doppia parentesi [[]] per accedere agli elementi di un elenco o di un frame di dati


521

R fornisce due diversi metodi per accedere agli elementi di un elenco o data.frame: []e [[]].

Qual è la differenza tra i due e in quali situazioni dovrei usare l'uno sull'altro?

Risposte:


327

La R Language Definition è utile per rispondere a questi tipi di domande:

R ha tre operatori di indicizzazione di base, con la sintassi visualizzata dai seguenti esempi

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

Per i vettori e le matrici le [[forme vengono utilizzate raramente, sebbene presentino alcune lievi differenze semantiche rispetto alla [forma (ad esempio, rilascia qualsiasi attributo di nomi o dimnomi e tale corrispondenza parziale viene utilizzata per gli indici dei caratteri). Quando si indicizzano strutture multidimensionali con un singolo indice, x[[i]]o x[i]si restituisce l' ielemento th sequenziale di x.

Per gli elenchi, si usa generalmente [[per selezionare un singolo elemento, mentre [restituisce un elenco degli elementi selezionati.

Il [[modulo consente di selezionare solo un singolo elemento utilizzando indici interi o di caratteri, mentre [consente l'indicizzazione per vettori. Si noti tuttavia che per un elenco, l'indice può essere un vettore e ogni elemento del vettore viene applicato a sua volta all'elenco, al componente selezionato, al componente selezionato di quel componente e così via. Il risultato è ancora un singolo elemento.


6
Qual è il ragionamento alla base dell'utilizzo di [[vs [per indicizzare con un singolo numero vs vettore? Perché non usare solo [per entrambi? Immagino che tu possa usare [[per tornare indietro di una singola voce, e [con un indice restituisce un elenco di lunghezza 1 ... ma perché non fare semplicemente [restituire una singola voce con un indice invece di un elenco? Perché potresti mai voler restituire un elenco di lunghezza 1?
parole per il

4
@wordsforthewise, durante la programmazione è possibile avere un vettore di lunghezza non definita che si desidera utilizzare per l'indicizzazione. Avere [sempre restituire un mezzo della lista che si ottiene la stessa classe di uscita per x[v]indipendentemente dalla lunghezza di v. Ad esempio, si potrebbe desiderare di lapplypiù di un sottoinsieme di una lista: lapply(x[v], fun). Se [eliminasse l'elenco per i vettori di lunghezza uno, ciò restituirebbe un errore ogni volta che ne vha uno.
Axeman,

1
Penso che questo spieghi più chiaramente, adv-r.had.co.nz/Subsetting.html
The Red Pea,

171

Le differenze significative tra i due metodi sono la classe degli oggetti che restituiscono quando utilizzati per l'estrazione e se possono accettare un intervallo di valori o solo un singolo valore durante l'assegnazione.

Si consideri il caso dell'estrazione dei dati nell'elenco seguente:

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

Supponiamo che vorremmo estrarre il valore memorizzato da bool da foo e utilizzarlo all'interno di if()un'istruzione. Ciò illustrerà le differenze tra i valori di ritorno di []e [[]]quando vengono utilizzati per l'estrazione dei dati. Il []metodo restituisce oggetti dell'elenco di classi (o data.frame se foo era un data.frame) mentre il [[]]metodo restituisce oggetti la cui classe è determinata dal tipo dei loro valori.

Quindi, usando il []metodo si ottiene quanto segue:

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

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

Questo perché il []metodo ha restituito un elenco e un elenco non è un oggetto valido da passare direttamente in if()un'istruzione. In questo caso dobbiamo usarlo [[]]perché restituirà l'oggetto "nudo" archiviato in "bool" che avrà la classe appropriata:

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

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

La seconda differenza è che l' []operatore può essere utilizzato per accedere a un intervallo di slot in un elenco o colonne in un frame di dati mentre l' [[]]operatore si limita ad accedere a un singolo slot o colonna. Si consideri il caso dell'assegnazione di valore utilizzando un secondo elenco bar():

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

Supponiamo di voler sovrascrivere gli ultimi due slot di foo con i dati contenuti nella barra. Se proviamo a usare l' [[]]operatore, ecco cosa succede:

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

Questo perché [[]]è limitato all'accesso a un singolo elemento. Dobbiamo usare []:

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

$str
[1] "R"

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

$bool
[1] -0.6291121

Notare che mentre il compito è andato a buon fine, le slot in foo hanno mantenuto i loro nomi originali.


111

Le parentesi doppie accedono a un elemento elenco , mentre una parentesi singola restituisce un elenco con un singolo elemento.

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

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

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


48

[]estrae un elenco, [[]]estrae elementi all'interno dell'elenco

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

Basta aggiungere qui che [[è anche attrezzato per l' indicizzazione ricorsiva .

Questo è stato accennato nella risposta di @JijoMatthew ma non esplorato.

Come notato in ?"[[", la sintassi like x[[y]], where length(y) > 1, viene interpretata come:

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

Si noti che ciò non cambia ciò che dovrebbe essere il principale fattore da asporto sulla differenza tra [e [[- vale a dire, che il primo viene utilizzato per il subsetting e il secondo viene utilizzato per estrarre singoli elementi dell'elenco.

Per esempio,

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

Per ottenere il valore 3, possiamo fare:

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

Tornando alla risposta di @JijoMatthew sopra, ricorda r:

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

In particolare, ciò spiega gli errori che tendiamo a riscontrare in caso di uso improprio [[, vale a dire:

r[[1:3]]

Errore in r[[1:3]]: indicizzazione ricorsiva non riuscita a livello 2

Dato che questo codice ha effettivamente cercato di valutare r[[1]][[2]][[3]]e l'annidamento degli rarresti al primo livello, il tentativo di estrarre attraverso l'indicizzazione ricorsiva è fallito [[2]], ad esempio al livello 2.

Errore in r[[c("foo", "far")]]: sottoscrizione fuori limite

Qui, R stava cercando r[["foo"]][["far"]], che non esiste, quindi otteniamo l'errore di limite.

Probabilmente sarebbe un po 'più utile / coerente se entrambi questi errori fornissero lo stesso messaggio.


Ciao Micheal, signore, possiamo usare [[]] per l'indicizzazione multipla ??
Therii,

14

Entrambi sono modi di sottoimpostazione. La parentesi singola restituirà un sottoinsieme dell'elenco, che di per sé sarà un elenco. vale a dire: può contenere o meno più di un elemento. D'altra parte una doppia parentesi restituirà solo un singolo elemento dall'elenco.

-La parentesi singola ci fornirà un elenco. Possiamo anche usare una parentesi singola se vogliamo restituire più elementi dall'elenco. considerare il seguente elenco: -

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

Ora si noti il ​​modo in cui l'elenco viene restituito quando provo a visualizzarlo. Digito r e premo invio

>r

#the result is:-

[[1]]

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

$foo

[1] 1

$far

[1] 2

Ora vedremo la magia della parentesi singola: -

>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

che è esattamente lo stesso di quando abbiamo provato a visualizzare il valore di r sullo schermo, il che significa che l'uso della parentesi singola ha restituito un elenco, dove all'indice 1 abbiamo un vettore di 10 elementi, quindi abbiamo altri due elementi con nomi foo e lontano. Possiamo anche scegliere di assegnare un singolo indice o nome di elemento come input per la parentesi singola. per esempio:

> r[1]

[[1]]

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

In questo esempio abbiamo dato un indice "1" e in cambio abbiamo ottenuto un elenco con un elemento (che è un array di 10 numeri)

> r[2]

$foo

[1] 1

Nell'esempio sopra abbiamo dato un indice "2" e in cambio abbiamo ottenuto un elenco con un elemento

> r["foo"];

$foo

[1] 1

In questo esempio abbiamo passato il nome di un elemento e in cambio è stato restituito un elenco con un elemento.

Puoi anche passare un vettore di nomi di elementi come: -

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

> r[x];

$foo

[1] 1

$far
[1] 2

In questo esempio abbiamo passato un vettore con due nomi di elementi "pippo" e "lontano"

In cambio abbiamo ottenuto un elenco con due elementi.

In breve la parentesi singola restituirà sempre un altro elenco con un numero di elementi uguale al numero di elementi o al numero di indici passati nella parentesi singola.

Al contrario, una doppia parentesi restituirà sempre solo un elemento. Prima di passare alla parentesi doppia, tenere presente una nota. 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.

Farò alcuni esempi. Si prega di tenere una nota delle parole in grassetto e tornarci dopo aver finito con gli esempi seguenti:

La doppia parentesi restituirà il valore effettivo all'indice ( NON restituirà un elenco)

  > r[[1]]

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


  >r[["foo"]]

    [1] 1

per parentesi doppie se proviamo a visualizzare più di un elemento passando un vettore, si verificherà un errore solo perché non è stato creato per soddisfare tale esigenza, ma solo per restituire un singolo elemento.

Considera quanto segue

> 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
Sottovalutato perché "il passaggio di un vettore ... comporterà un errore solo perché non è stato creato per soddisfare tale esigenza" non è corretto; vedi la mia nuova risposta.
MichaelChirico,

1
Sottovalutato perché fa affermazioni forti come "MENTRE UNA DOPPIA STAFFA NON INVIERÀ MAI UNA LISTA". Non è vero - se abbiamo un oggetto che è un elenco di elenchi, la doppia parentesi restituirà un altro elenco.
dabsingh,

Il fatto che []restituisca una classe di elenco anche se è una singola cifra non è molto intuitivo. Avrebbero dovuto creare un'altra sintassi come ([])per l'elenco e l' [[]]accesso per accedere all'elemento reale va bene. Preferisco pensare [[]]al valore grezzo come in altre lingue.
TokyoToo

13

Per aiutare i neofiti a navigare attraverso la nebbia manuale, potrebbe essere utile vedere la [[ ... ]]notazione come una funzione collassante - in altre parole, è quando vuoi semplicemente "ottenere i dati" da un vettore, elenco o frame di dati. È utile farlo se si desidera utilizzare i dati di questi oggetti per i calcoli. Questi semplici esempi illustreranno.

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

Quindi dal terzo esempio:

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

1
Come principiante, ho trovato utile nelle 3 assegnazioni a x (usando "<-") per sostituire x = 1 con w = 1 per evitare confusione con la x che è la destinazione di "<-"
user36800

Anche se molto semplice, mi piace molto questa spiegazione. Un'altra semplice dimostrazione: iris[[1]]restituisce un vettore, mentre iris[1]restituisce un data.frame
stevec il

11

Essendo terminologico, l' [[operatore estrae l'elemento da un elenco mentre l' [operatore accetta un sottoinsieme di un elenco.


7

Per l'ennesimo caso d'uso concreto, utilizzare doppie parentesi quando si desidera selezionare un frame di dati creato dalla split()funzione. Se non lo sai, split()raggruppa un elenco / frame di dati in sottoinsiemi in base a un campo chiave. È utile se si desidera operare su più gruppi, tracciarli, ecc.

> 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

Si prega di fare riferimento alla spiegazione dettagliata di seguito.

Ho usato il frame di dati incorporato in R, chiamato 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 ... 
           ............

La riga superiore della tabella si chiama intestazione che contiene i nomi delle colonne. Ogni linea orizzontale in seguito indica una riga di dati, che inizia con il nome della riga, quindi seguita dai dati effettivi. Ogni membro di dati di una riga è chiamato cella.

operatore "[]" a parentesi quadra singola

Per recuperare i dati in una cella, immettiamo le coordinate di riga e colonna nell'operatore "[]" a parentesi quadra singola. Le due coordinate sono separate da una virgola. In altre parole, le coordinate iniziano con la posizione della riga, quindi seguite da una virgola e terminano con la posizione della colonna. L'ordine è importante

Ad esempio 1: - Ecco il valore della cella dalla prima riga, seconda colonna di mtcar.

> mtcars[1, 2] 
[1] 6

Ad esempio 2: - Inoltre, possiamo usare i nomi di riga e colonna anziché le coordinate numeriche.

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

Operatore "[[]]" con parentesi quadre doppie

Facciamo riferimento a una colonna di frame di dati con l'operatore "[[]]" con parentesi quadre doppie.

Ad esempio 1: - Per recuperare il nono vettore di colonna del set di dati incorporato mtcars, scriviamo mtcars [[9]].

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

Ad esempio 2: - Possiamo recuperare lo stesso vettore di colonna con il suo nome.

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


-1

Inoltre:

Di seguito il LINK della RISPOSTA qui.

Ecco un piccolo esempio che affronta il punto seguente:

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])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.