7.4 El signo de dolar $ y los corchetes dobles [[]]

Otra manera en la que podemos extraer subconjuntos usando nombres, es con el signo de dólar $.

Para usar este método, escribir el signo $ después del nombre de un objeto de la siguiente forma: objeto$nombre.

Este método permite extraer un sólo elemento a la vez, funciona para data frames y listas, y para el caso de los data frame, el elemento extraido siempre será una columna.

Por ejemplo, extraemos la columna nombre del data frame mi_df.

mi_df$nombre
## [1] Armando Elsa    Ignacio Olga   
## Levels: Armando Elsa Ignacio Olga

También podemos escribir el nombre del elemento que deseamos entre comillas, esto es útil si el nombre tiene espacios.

mi_df$"nombre"
## [1] Armando Elsa    Ignacio Olga   
## Levels: Armando Elsa Ignacio Olga

Si intentamos dar más de un nombre después del signo $, nos es devuelto un error.

mi_df$c("nombre", "edad")

El resultado de las operaciones anteriores no es un data frame, sino un vector.

class(mi_df$nombre)
## [1] "factor"

Esta es una característica distintiva de este método, al usar el signo $ para extraer un elemento de un data frame o una lista, obtenemos un objeto de la clase que ese elemento era originalmente.

Recuerda que un data frame está formado por vectores. Como vimos en el capítulo 6, una manera de generar data frames es combinar vectores. Estos vectores, aunque estén contenidos dentro de un data frame, conservan todas las características de un vector y es posible extraerlos como tales.

Cuando usamos el signo $, le pedimos a R que extraiga de un objeto un subconjunto con sus propiedades originales. Por esta razón, para los data frame, siempre son devueltos vectores, mientras que para las listas lo que obtenemos depende del tipo de objeto contenido en ellas.

Por ejemplo, si extraemos el elemento uno de mi_lista usando corchetes, obtenemos una lista.

class(mi_lista["uno"])
## [1] "list"

Pero si usamos el signo $, el resultado es un vector numérico, pues esta es su clase original.

class(mi_lista$uno)
## [1] "numeric"

Si intentamos extraer el elemento cuatro, obtendremos una matriz.

class(mi_lista$"cuatro")
## [1] "matrix"

De manera similar, podemos extraer elementos de un objeto, con su clase original, usando índices y corchetes dobles [[]].

La ventaja de usar corchetes dobles es no sólo podemos usar índices, sino que los podemos combinar con nombres, lo cual nos da acceso a una mayor flexibilidad para extraer subconjuntos y permite usarlos en estructuras de datos con elementos sin nombre.

Por ejemplo, para extraer la columna edad de mi_df con corchetes dobles, podemos usar su índice, 2, o su nombre.

# Usando un índice
mi_df[[2]]
## [1] 20 24 22 30
# Usando un nombre
mi_df[["edad"]]
## [1] 20 24 22 30

A diferencia de los corchetes sencillos, no podemos extraer más de una columna de un data frame usando corchetes dobles y un vector.

Si escribimos un vector numérico dentro de corchetes dobles, será interpretado como si cada número estuviera separado por una coma, indicando las dimensiones de las cuales se extraerán datos.

Por ejemplo, intentamos extraer las columnas uno y tres de mi_df.

mi_df[[c(1, 3)]]
## [1] 3

El resultado que obtenemos no es el esperado.

Lo que ocurre es que cuando escribimos lo siguiente.

mi_df[[c(1, 3)]]

R lo interpreta como:

mi_df[[1, 3]]

Es decir, R extraerá el dato en el renglón uno y la columna tres, en lugar de las columnas uno y tres.

mi_df[[1, 3]]
## [1] H
## Levels: H M

Por lo tanto si escribimos lo siguiente:

mi_df[[1:3]]

R lo interpretará como buscar el dato uno en la primera dimensión, el dato dos, en la segunda dimensión, y el dato tres en la tercera dimensión. Como un data frame solo tiene dos dimensiones, se nos devolverá un error

mi_df[[1:3]]
## Error in .subset2(x, i, exact = exact): falló indexación recursiva en nivel 2

Lo mismo ocurre con vectores que contienen nombres de columnas.

mi_df[[c("nombre", "edad")]]
## Error in .subset2(x, i, exact = exact): subíndice fuera de  los límites

Como las listas son unidimensionales, sólo podemos extraer un elemento a la vez usando corchetes dobles [[]]

mi_lista[[1]]
## [1] 1

Si damos más de un índice o nombre, siempre obtendremos un error.

# Más de un índice
mi_lista[[1:2]]
## Error in mi_lista[[1:2]]: subíndice fuera de  los límites
# Más de un nombre
mi_lista[["uno", "dos"]]
## Error in mi_lista[["uno", "dos"]]: número incorrecto de subíndices

7.4.1 Los data frames y listas son como cajas de manzanas

Para comprender mejor el comportamiento comportamiento del signo $ y los corchetes dobles, imagina que los data frames y listas son cajas que contienen manzanas. Los data frame contienen las manzanas en bolsas, y estas bolsas serían vectores, en los cuales cada manzana sería un elemento.

Por su parte, las listas pueden tener manzanas en diferentes presentaciones: bolsas, manzanas sueltas o incluso otras cajas de manzana.

Cuando usamos corchetes, estamos sacando manzanas de una caja, usando una caja más pequeña, otro data frame o lista. Así, con un data frame, obtenemos otra caja que contiene bolsas de manza, y con una lista obtenemos otra caja con manzanas en las presentaciones que se encuentren.

En contraste, al usar el signo $ o corchetes dobles, estamos sacando bolsas de manzana directamente en un data frame, y en las listas estamos sacando las manzanas en su presentación original, sin que haya de por medio otra caja en ninguno de estos casos.

Dado lo anterior, podemos extrar subconjuntos de subconjuntos combinando diferentes tipos de corchetes.

# Subconjunto de un subconjunto: Data frame.
mi_df[[2]][3]
## [1] 22
# Subconjunto de un subcojunto: Lista.
mi_lista[["cuatro"]][2]
## [1] 2

No te preocupes mucho si lo anterior te parece confuso, lo es. No profundizaremos sobre este tema específico de los subconjuntos en este libro, pero ten en cuenta que si te encuentras con código como el del ejemplo anterior, lo que está ocurriendo es la extracción de subconjuntos de subconjuntos.