La diferencia entre el corchete [] y el doble corchete [[]] para acceder a los elementos de una lista o marco de datos


521

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

¿Cuál es la diferencia entre los dos y en qué situaciones debo usar uno sobre el otro?

Respuestas:


327

La definición del lenguaje R es útil para responder a este tipo de preguntas:

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

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

Para los vectores y las matrices, las [[formas rara vez se usan, aunque tienen algunas diferencias semánticas leves de la [forma (por ejemplo, elimina cualquier atributo de nombres o nombres oscuros, y esa coincidencia parcial se usa para los índices de caracteres). Al indexar estructuras multidimensionales con un solo índice, x[[i]]o x[i]devolverá el ielemento 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 un solo elemento utilizando í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, etc. El resultado sigue siendo un solo elemento.


66
¿Cuál es el razonamiento detrás del uso de [[vs [para indexar con un solo número vs vector? ¿Por qué no solo usar [para ambos? Supongo que puede usar [[para recuperar una sola entrada, y [con un índice devuelve una lista de longitud 1 ... pero ¿por qué no simplemente hacer [devolver una sola entrada con un índice en lugar de una lista? ¿Por qué querrías que se devuelva una lista de longitud 1?
wordsforthewise

44
@wordsforthewise, al programar puede tener un vector de longitud indefinida que desea usar para indexar. Tener [siempre una lista significa que obtienes la misma clase de salida x[v]independientemente de la longitud de v. Por ejemplo, uno podría querer lapplymás de un subconjunto de una lista: lapply(x[v], fun). Si [descartara la lista de vectores de longitud uno, devolvería un error siempre que vtenga longitud uno.
Axeman

1
Creo que esto se explica más claramente, adv-r.had.co.nz/Subsetting.html
The Red Pea

171

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

Considere el caso de la 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 if()declaración. Esto ilustrará las diferencias entre los valores de retorno de []y [[]]cuándo se usan 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, usar el []método da como resultado 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 if()declaración. En este caso, debemos usarlo [[]]porque devolverá el objeto "desnudo" 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 puede usarse para acceder a un rango de ranuras en una lista o columnas en un marco de datos, mientras que el [[]]operador está limitado a acceder a una sola ranura o columna. Considere el caso de asignación de valor utilizando una segunda lista bar():

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

Digamos que queremos sobrescribir las dos últimas ranuras de foo con los datos contenidos en la barra. Si intentamos 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 a acceder 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 si bien la asignación fue exitosa, las máquinas tragamonedas en foo mantuvieron sus nombres originales.


111

Los paréntesis dobles acceden a un elemento de la lista , mientras que un paréntesis 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"


48

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

18

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

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

Como se señaló en ?"[[", la sintaxis like x[[y]], where 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 conclusión principal sobre la diferencia entre [y [[, a saber, que el primero se usa para subconjuntos , y el segundo se usa 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 de @ JijoMatthew anterior, recuerde r:

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

En particular, esto explica los errores que solemos tener cuando usamos mal [[, a saber:

r[[1:3]]

Error en r[[1:3]]: error de indexación recursiva en el nivel 2

Dado que este código realmente trató de evaluar r[[1]][[2]][[3]], y la anidación de las rparadas en el nivel uno, el intento de extraer a través de la indexación recursiva falló [[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"]], lo que no existe, por lo que obtenemos el error de subíndice fuera de los límites.

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


Hola Micheal señor, ¿podemos usar [[]] para indexación múltiple?
Therii

14

Ambos son formas de subconjuntos. El paréntesis simple 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 paréntesis doble devolverá solo un elemento de la lista.

- Un solo 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 intento 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 corchete 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 intentamos mostrar el valor de r en la pantalla, lo que significa que el uso de un solo parche ha devuelto una lista, donde en el índice 1 tenemos un vector de 10 elementos, luego tenemos dos elementos más con nombres foo y lejos También podemos optar por dar un solo índice o nombre de elemento como entrada al paréntesis único. 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 le 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 "far"

A cambio tenemos una lista con dos elementos.

En resumen, el paréntesis simple siempre le devolverá otra lista con una cantidad de elementos igual a la cantidad de elementos o la cantidad de índices que pasa en la horquilla individual.

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

Encontraré algunos ejemplos. Mantenga una nota de las palabras en negrita y vuelva a leerlas una vez que haya terminado con los ejemplos a continuación:

El paréntesis 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 intentamos ver más de un elemento pasando un vector, se producirá un error solo porque no se creó para satisfacer esa necesidad, sino solo 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

1
Votado en contra porque "pasar un vector ... dará como resultado un error solo porque no fue creado para satisfacer esa necesidad" es incorrecto; Mira mi nueva respuesta.
MichaelChirico

1
Votado en contra porque hace fuertes afirmaciones como "MIENTRAS QUE UN SOPORTE DOBLE NUNCA VOLVERÁ A UNA LISTA". No es cierto: si tenemos un objeto que es una lista de listas, el paréntesis doble devolverá otra lista.
dabsingh

El hecho de que []devuelva una clase de lista incluso si es un solo dígito es muy poco intuitivo. Deberían haber creado otra sintaxis como ([])para la lista y [[]]acceder al elemento real está bien. Prefiero pensar [[]]en el valor bruto como en otros idiomas.
TokyoToo

13

Para ayudar a los novatos a navegar a través de la niebla manual, puede ser útil ver la [[ ... ]]notación como una función de colapso ; en otras palabras, es cuando solo desea 'obtener los datos' de un vector, lista o marco de datos con nombre. Es bueno hacer esto si desea utilizar datos de estos objetos para los 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, del tercer ejemplo:

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

1
Como novato, me pareció útil en las 3 asignaciones a x (usando "<-") para reemplazar x = 1 con w = 1 para evitar confusiones con la x que es el objetivo de "<-"
user36800

Aunque es muy simple, me gusta mucho esta explicación. Otra demostración simple: iris[[1]]devuelve un vector, mientras que iris[1]devuelve un data.frame
stevec

11

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


7

Para otro caso de uso concreto, use corchetes dobles cuando desee seleccionar un marco de datos creado por la split()función. Si no sabe, split()agrupa un marco de lista / datos en subconjuntos basados ​​en un campo clave. Es útil si cuando 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"

-1

Consulte la explicación detallada a continuación.

He usado el marco de datos incorporado en R, llamado 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 línea superior de la tabla se llama encabezado que contiene los nombres de las columnas. Cada línea horizontal luego denota una fila de datos, que comienza con el nombre de la fila, y luego sigue con los datos reales. Cada miembro de datos de una fila se llama celda.

operador de corchete simple "[]"

Para recuperar datos en una celda, ingresaríamos sus coordenadas de fila y columna en el operador de corchete único "[]". Las dos coordenadas están separadas por una coma. En otras palabras, las coordenadas comienzan con la posición de la fila, seguidas de una coma y terminan con la posición de la columna. El orden es importante.

Por ejemplo, 1: - Aquí está el valor de celda de la primera fila, segunda columna de mtcars.

> mtcars[1, 2] 
[1] 6

Por ejemplo, 2: - Además, podemos usar los nombres de fila y columna en lugar de las coordenadas numéricas.

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

Operador de corchete doble "[[]]"

Hacemos referencia a una columna de marco de datos con el operador de corchete doble "[[]]".

Ejemplo 1: - Para recuperar el vector de la novena columna del conjunto de datos integrado mtcars, escribimos mtcars [[9]].

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

Ej. 2: - Podemos recuperar el mismo vector de columna por su nombre.

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


-1

Adicionalmente:

Siguiendo el ENLACE de la RESPUESTA aquí.

Aquí hay un pequeño ejemplo que aborda el siguiente punto:

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])
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.