La differenza tra parentesi e doppia parentesi ] per accedere agli elementi di una lista o di un dataframe

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

Qual è la differenza tra i due? In quali situazioni dovrei usare l’una sull’altra?

La definizione del linguaggio R è utile per rispondere a questi tipi di domande:

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

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

Per i vettori e le matrici le [[ forms sono usate raramente, sebbene abbiano alcune leggere differenze semantiche dal [form (ad esempio, lascia qualsiasi attributo nomi o dimnames e quella corrispondenza parziale è usata per gli indici dei caratteri). Quando si indicizzano strutture multidimensionali con un singolo indice, x[[i]] o x[i] restituiranno il loro elemento sequenziale di x .

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

La [[ form consente di selezionare un solo elemento usando interi o indici di caratteri, mentre [ consente l’indicizzazione per vettori. Nota che per un elenco, l’indice può essere un vettore e ogni elemento del vettore viene applicato a turno all’elenco, il componente selezionato, il componente selezionato di quel componente e così via. Il risultato è ancora un singolo elemento.

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

Considerare il caso dell’estrazione dei dati nel seguente elenco:

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

Diciamo che vorremmo estrarre il valore memorizzato da bool da foo e usarlo all'interno di un'istruzione if() . Questo 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 class è determinata dal tipo dei loro valori.

Quindi, utilizzando il metodo [] 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 [] restituito un elenco e un elenco non è un object valido per passare direttamente a un'istruzione if() . In questo caso dobbiamo usare [[]] perché restituirà l'object "nuda" memorizzato in "bool" che avrà la class 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 [[]] è limitato all'accesso a un singolo slot o colonna. Considerare il caso dell'assegnazione del valore usando una seconda lista, bar() :

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

Diciamo che vogliamo 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 

Si noti che mentre l'assegnazione è andata a buon fine, gli slot di foo hanno mantenuto il loro nome originale.

Le doppie parentesi accedono a un elemento dell’elenco , mentre una singola parentesi ti 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" 

[] 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" 

Aggiungo qui che [[ è anche equipaggiato per l’ indicizzazione ricorsiva .

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

Come notato in ?"[[" , La syntax come x[[y]] , dove length(y) > 1 , è interpretata come:

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

Nota che questo non cambia quello che dovrebbe essere il tuo principale argomento da tenere in considerazione sulla differenza tra [ e [[ – cioè che il primo è usato per il subset , e quest’ultimo è usato per estrarre singoli elementi di lista.

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 qui sopra, richiama r :

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

In particolare, questo spiega gli errori che tendiamo ad ottenere quando si usa male [[ , cioè:

 r[[1:3]] 

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

Poiché questo codice ha effettivamente provato a valutare r[[1]][[2]][[3]] e l'annidamento di r stop al livello uno, il tentativo di estrarre attraverso l'indicizzazione ricorsiva non è riuscito a [[2]] , cioè , al livello 2.

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

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

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

Entrambi sono modi di susseguirsi. La singola parentesi restituirà un sottoinsieme della lista, che di per sé sarà una lista. cioè: può o non può contenere più di un elemento. D’altra parte una doppia parentesi restituirà solo un singolo elemento dalla lista.

-Single bracket ci darà una lista. Possiamo anche utilizzare una singola parentesi se desideriamo restituire più elementi dalla lista. considera il seguente elenco: –

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

Ora si prega di notare il modo in cui la lista viene restituita quando provo a visualizzarla. Digito r e premere 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 singola staffa: -

 >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'utilizzo della singola parentesi ha restituito una lista, dove nell'indice 1 abbiamo un vettore di 10 elementi, quindi abbiamo altri due elementi con nomi foo e lontano. Potremmo anche scegliere di fornire un singolo indice o nome di elemento come input per la singola parentesi. 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 ho ottenuto una lista con un elemento (che è una matrice di 10 numeri)

 > r[2] $foo [1] 1 

Nell'esempio sopra abbiamo dato un indice "2" e in cambio abbiamo ottenuto una lista 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 "foo" e "lontano"

In cambio abbiamo ottenuto una lista con due elementi.

In breve, la singola parentesi ti restituirà sempre un'altra lista con un numero di elementi uguale al numero di elementi o al numero di indici che passi nella singola parentesi.

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

Troverò alcuni esempi. Si prega di tenere una nota delle parole in grassetto e tornare ad esso dopo aver finito con gli esempi di seguito:

Doppia parentesi ti restituirà il valore reale all'indice ( NON restituirà una lista)

  > r[[1]] [1] 1 2 3 4 5 6 7 8 9 10 >r[["foo"]] [1] 1 

per le doppie parentesi se proviamo a visualizzare più di un elemento passando un vettore, si verificherà un errore solo perché non è stato creato per soddisfare quel bisogno, 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 

Per aiutare i neofiti a navigare attraverso la nebbia manuale, potrebbe essere utile vedere la [[ ... ]] notazione come una funzione collassabile – in altre parole, è quando vuoi semplicemente “ottenere i dati” da un vettore con nome, elenco o cornice dati. È opportuno 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 

Da Hadley Wickham:

Da Hadley Wickham

La mia modifica (dall’aspetto schifoso) da mostrare usando tidyverse / purrr:

inserisci la descrizione dell'immagine qui

Per un altro caso d’uso concreto, utilizzare le parentesi doppie quando si desidera selezionare un frame di dati creato dalla funzione split() . Se non lo sai, split() raggruppa un elenco / frame dati in sottoinsiemi basati su un campo chiave. È utile se vuoi 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" 

Essendo terminologico, [[ operatore estrae l’elemento da una lista mentre [ operatore prende un sottoinsieme di una lista.