La diferencia entre el corchete y el corchete doble para acceder a los elementos de una lista o dataframe

R proporciona dos métodos diferentes para acceder a los elementos de una lista o de un data.frame: los operadores [] y [[]] .

¿Cuál es la diferencia entre los dos? ¿En qué situaciones debo usar una sobre la otra?

R Language Definition es útil para responder este tipo de preguntas:

R tiene tres operadores de indexación básicos, con la syntax mostrada por los siguientes ejemplos

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

Para vectores y matrices, las formas [[ rara vez se usan, aunque tienen algunas diferencias semánticas leves con respecto a [la forma (por ejemplo, deja caer cualquier nombre o atributo de dimnames, y esa coincidencia parcial se usa para índices de caracteres). Al indexar estructuras multidimensionales con un solo índice, x[[i]] x[i] devolverá el elemento i ésimo secuencial de x .

Para las listas, generalmente se usa [[ para seleccionar cualquier elemento individual, mientras que [ devuelve una lista de los elementos seleccionados.

El formulario [[ permite seleccionar solo un elemento usando índices enteros o de caracteres, mientras que [ permite la indexación por vectores. Sin embargo, tenga en cuenta que para una lista, el índice puede ser un vector y cada elemento del vector se aplica a su vez a la lista, el componente seleccionado, el componente seleccionado de ese componente, y así sucesivamente. El resultado es aún un solo elemento.

Las diferencias significativas entre los dos métodos son la clase de los objetos que devuelven cuando se utilizan para la extracción y si pueden aceptar un rango de valores o solo un valor único durante la asignación.

Considere el caso de extracción de datos en la siguiente lista:

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

Digamos que nos gustaría extraer el valor almacenado por bool de foo y usarlo dentro de una sentencia if() . Esto ilustrará las diferencias entre los valores de retorno de [] y [[]] cuando se utilizan para la extracción de datos. El método [] devuelve objetos de la lista de clases (o data.frame si foo era un data.frame) mientras que el método [[]] devuelve objetos cuya clase está determinada por el tipo de sus valores.

Entonces, usando el método [] resulta en lo siguiente:

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

Esto se debe a que el método [] devolvió una lista y una lista no es un objeto válido para pasar directamente a una instrucción if() . En este caso, necesitamos usar [[]] porque devolverá el objeto "vacío" almacenado en 'bool' que tendrá la clase apropiada:

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

La segunda diferencia es que el operador [] se puede usar para acceder a un rango de ranuras en una lista o columnas en un dataframe, mientras que el operador [[]] está limitado para acceder a un solo nicho o columna. Considere el caso de la asignación de valores usando una segunda lista, bar() :

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

Digamos que queremos sobrescribir las últimas dos ranuras de foo con los datos contenidos en la barra. Si tratamos de usar el operador [[]] , esto es lo que sucede:

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

Esto se debe a que [[]] está limitado al acceso a un solo elemento. Necesitamos usar [] :

 foo[ 2:3 ] < - bar print( foo ) $str [1] "R" $vec [,1] [,2] [1,] 0 0 [2,] 0 0 $bool [1] -0.6291121 

Tenga en cuenta que, aunque la asignación fue exitosa, las ranuras en foo mantuvieron sus nombres originales.

Los corchetes dobles acceden a un elemento de lista, mientras que un corchete simple le devuelve una lista con un solo elemento.

 lst < - list('one','two','three') a <- lst[1] class(a) ## returns "list" a <- lst[[1]] class(a) ## returns "character" 

[] extrae una lista, [[]] extrae elementos dentro de la lista

 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" 

Solo agregue aquí que [[ también está equipado para la indexación recursiva .

Esto fue insinuado en la respuesta de @JijoMatthew pero no explorado.

Como se señala en ?"[[" , Sintaxis como x[[y]] , donde length(y) > 1 , se interpreta como:

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

Tenga en cuenta que esto no cambia lo que debería ser su principal punto de partida en la diferencia entre [ y [[ – a saber, que el primero se utiliza para subconjunto , y el último se utiliza para extraer elementos de lista única.

Por ejemplo,

 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 

Para obtener el valor 3, podemos hacer:

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

Volviendo a la respuesta anterior de @ JijoMatthew, recuerde r :

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

En particular, esto explica los errores que tendemos a obtener al mal usar [[ , a saber:

 r[[1:3]] 

Error en r[[1:3]] : la indexación recursiva falló en el nivel 2

Como este código en realidad intentó evaluar r[[1]][[2]][[3]] , y la anidación de r detiene en el nivel uno, el bash de extraer a través de la indexación recursiva falló en [[2]] , es decir , en el nivel 2.

Error en r[[c("foo", "far")]] : subíndice fuera de límites

Aquí, R estaba buscando r[["foo"]][["far"]] , que no existe, por lo que obtenemos el error de subíndice fuera de límites.

Probablemente sería un poco más útil / consistente si ambos errores dieran el mismo mensaje.

Ambas son formas de subconjunto. El paréntesis único devolverá un subconjunto de la lista, que en sí mismo será una lista. es decir: puede contener o no más de un elemento. Por otro lado, un corchete doble devolverá solo un elemento de la lista.

-Solo el soporte nos dará una lista. También podemos usar un solo paréntesis si deseamos devolver múltiples elementos de la lista. considere la siguiente lista:

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

Ahora, tenga en cuenta la forma en que se devuelve la lista cuando bash mostrarla. Escribo r y presiono enter

 >r #the result is:- [[1]] [1] 1 2 3 4 5 6 7 8 9 10 $foo [1] 1 $far [1] 2 

Ahora veremos la magia del bracket simple:

 >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 

que es exactamente lo mismo que cuando tratamos de mostrar el valor de r en la pantalla, lo que significa que el uso de corchetes individuales ha devuelto una lista, donde en el índice 1 tenemos un vector de 10 elementos, entonces tenemos dos elementos más con nombres foo y lejos. También podemos optar por dar un único índice o nombre de elemento como entrada para el corchete simple. p.ej:

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

En este ejemplo, le dimos un índice "1" y, a cambio, obtuvimos una lista con un elemento (que es una matriz de 10 números)

 > r[2] $foo [1] 1 

En el ejemplo anterior dimos un índice "2" y a cambio obtuvimos una lista con un elemento

 > r["foo"]; $foo [1] 1 

En este ejemplo pasamos el nombre de un elemento y, a cambio, se devolvió una lista con un elemento.

También puede pasar un vector de nombres de elementos como:

 > x< -c("foo","far") > r[x]; $foo [1] 1 $far [1] 2 

En este ejemplo pasamos un vector con dos nombres de elementos "foo" y "lejos"

A cambio, tenemos una lista con dos elementos.

En pocas palabras, el corchete individual siempre le devolverá otra lista con el número de elementos igual al número de elementos o la cantidad de índices que pase al corchete individual.

Por el contrario, un soporte doble siempre devolverá solo un elemento. Antes de pasar al paréntesis doble, tenga en cuenta 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.

Voy a ubicar algunos ejemplos. Guarde una nota de las palabras en negrita y vuelva a ella una vez que haya terminado con los ejemplos a continuación:

El corchete doble le devolverá el valor real en el índice. ( NO devolverá una lista)

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

para corchetes dobles si tratamos de ver más de un elemento pasando un vector, se producirá un error solo porque no se creó para satisfacer esa necesidad, sino simplemente para devolver un solo elemento.

Considera lo siguiente

 > 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 

Para ayudar a los novatos a navegar a través de la niebla manual, podría ser útil ver la notación [[ ... ]] como una función colapsante ; en otras palabras, es cuando solo quieres ‘obtener los datos’ de un vector nombrado, lista o dataframe. Es bueno hacerlo si desea utilizar datos de estos objetos para realizar cálculos. Estos ejemplos simples ilustrarán.

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

Entonces desde el tercer ejemplo:

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

De Hadley Wickham:

De Hadley Wickham

Mi (aspecto cutre) modificación para mostrar usando tidyverse / purrr:

enter image description here

Para otro caso de uso concreto, use corchetes dobles cuando desee seleccionar un dataframe creado por la función split() . Si no sabe, split() agrupa una lista / dataframe en subconjuntos basados ​​en un campo clave. Es útil si desea operar en múltiples grupos, trazarlos, etc.

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

Siendo terminológico, [[ operador extrae el elemento de una lista mientras que [ operador toma un subconjunto de una lista.