3.5: Listas y Atributos
- Page ID
- 55108
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)El siguiente tipo de datos importante en nuestro recorrido por R es la lista. Las listas son bastante similares a los vectores: son colecciones ordenadas de datos, indexables por número de índice, vector lógico y nombre (si se nombra la lista). Sin embargo, las listas pueden contener varios tipos diferentes de datos (incluidas otras listas). Supongamos que tenemos tres vectores diferentes que representan alguna información sobre la planta Arabidopsis thaliana.

Entonces podemos usar la función list ()
para reunir estos vectores en una sola unidad con la clase “list”
.

Gráficamente, podríamos representar esta lista así:

Aquí, la sintaxis [1]
indica que los elementos de la lista son vectores (como en cuando se imprimen vectores). Al igual que los vectores, las listas se pueden indexar por vector índice y vector lógico.

Ambos de lo anterior asignan a la sublista
de variables una lista parecida a:

Esto parece bastante sencillo: al subestablecer una lista con un vector de indexación se devuelve una lista más pequeña con los elementos solicitados. Pero esta regla puede ser engañosa si olvidamos que un vector es el elemento más básico de los datos. Debido a que 2
es el vector de longitud uno c (2)
, athal [2]
devuelve no el segundo elemento de la lista athal
, sino más bien una lista de longitud uno con un solo elemento (el vector de ecotipos).

Una representación gráfica de esta lista:

Por lo tanto, necesitaremos una sintaxis diferente si queremos extraer un elemento individual de una lista. Esta sintaxis alternativa es athal [[2]]
.

Si quisiéramos extraer el segundo ecotipo directamente, necesitaríamos usar el second_ecotype relativamente torpe <- athal [[2]] [2]
, que accede al segundo elemento del vector (accedido por [2]
) dentro del del segundo elemento de la lista (accedido por [[2]]
).

Cuando imprimimos una lista, esta estructura y la sintaxis de doble corchete se refleja en la salida.

Listas con nombre, listas dentro de listas
Al igual que los vectores, las listas se pueden nombrar, asociadas con un vector de caracteres de igual longitud, usando la función names ()
. Podemos usar un vector índice de nombres para extraer una sublista, y podemos usar la sintaxis [[]]
para extraer elementos individuales por nombre.

Incluso podemos extraer elementos de una lista si el nombre del elemento que queremos se almacena en otra variable, usando la sintaxis [[]]
.

Por muy divertida que sea esta sintaxis de doble corchete, debido a que extraer elementos de una lista por su nombre es una operación común, hay un atajo usando sintaxis $
.

De hecho, si el nombre no contiene ningún carácter especial (espacios, etc.), entonces se pueden dejar las comillas.

Este atajo es ampliamente utilizado y conveniente, pero, debido a que las comillas están implícitas, no podemos usar la sintaxis $
para extraer un elemento por nombre si ese nombre está almacenado en una variable intermediaria. Por ejemplo, si extract_name <- “ecotipos”
, entonces athal$extract_name
se expandirá a athal [["extract_name"]]
, y no obtendremos el vector de ecotipos. Este error común refleja un malentendido del azúcar sintáctico empleado por R. Del mismo modo, la sintaxis $
no funcionará para nombres como “# Cromosomas”
porque ese nombre contiene un espacio y un carácter especial (por esta razón, los nombres de los elementos de la lista suelen simplificarse).
Frecuentemente, la sintaxis $
se combina con la sintaxis vectorial si el elemento de la lista al que se hace referencia es un vector. Por ejemplo, podemos extraer directamente el tercer ecotipo, o establecer el tercer ecotipo.

Continuando con este ejemplo, supongamos que tenemos otra lista que describe información sobre cada cromosoma. Podemos comenzar con una lista vacía, y asignarle elementos por nombre.

Esta lista de dos elementos se relaciona con A. thaliana, por lo que tiene sentido incluirla de alguna manera en la lista athal
. Afortunadamente, las listas pueden contener otras listas, por lo que asignaremos esta lista de chrs
como elemento de la lista athal
.

Las listas son un excelente contenedor para colecciones generales de datos heterogéneos en un solo “objeto” organizado. (Estos difieren de los objetos Python en que no tienen métodos almacenados en ellos también, pero veremos cómo funciona R con métodos en capítulos posteriores). Si corriéramos print (athal)
en este punto, toda esta información sería impresa, pero desafortunadamente de una manera bastante antipática:

Este resultado sí ilustra algo de interés, sin embargo. Podemos encadenar la sintaxis $
para acceder a elementos de listas y listas contenidas por nombre. Por ejemplo, length <- athal$chrinfo$length
extrae el vector de longitudes contenido en la lista interna de ChrInfo
, e incluso podemos modificar elementos de estos vectores con sintaxis como athal$chrinfo$geneCounts [1] <- 7079
(quizás un nuevo gen fue descubierto recientemente en el primer cromosoma). Ampliando la sintaxis un poco para usar corchetes dobles en lugar de notación $
, estas son equivalentes a longitudes <- athal [["ChrInfo"]] [["Longitudes"]]
y athal [["ChrInfo"]] [["GeneCounts"]] [1] <- 7079
.
Atributos, Eliminación de Elementos, Estructura de Lista
Las listas son una excelente manera de organizar datos heterogéneos, especialmente cuando los datos se almacenan en una asociación Nombre → Valor, [1] facilitando el acceso a los datos por nombre de carácter. Pero, ¿y si queremos buscar alguna información asociada a un dato pero no representada en los propios datos? Esto sería un tipo de “metadatos”, y R nos permite asociar metadatos a cualquier dato usando lo que se llama atributos. Supongamos que tenemos un vector simple de datos normalmente distribuidos:

Posteriormente, podríamos querer saber qué tipo de datos son estos: ¿se distribuyen normalmente, o algo más? Podemos resolver este problema asignando el término “normal”
como atributo de los datos. El atributo también necesita un nombre, al que llamaremos “disttype”
. Los atributos se asignan de manera similar a los nombres.

Cuando se imprime, la salida muestra los atributos que también se han asignado.

Podemos extraer por separado un atributo dado de un elemento de datos, usando sintaxis como sample_dist <- attr (sample, “disttype”)
. Los atributos se utilizan ampliamente en R, aunque rara vez se modifican en el uso diario del idioma. [2]
Para ampliar nuestro ejemplo de A. thaliana, asignemos un atributo de “reino” al vector especie.

En este punto, hemos construido una estructura bastante sofisticada: una lista que contiene vectores (uno de los cuales tiene un atributo) y otra lista, en sí misma que contiene vectores, con los diversos elementos de la lista siendo nombrados. Si fuéramos a ejecutar print (athal)
, veríamos una salida bastante desordenada. Afortunadamente, R incluye una alternativa a print ()
llamada str (),
que imprime muy bien la estructura de una lista (u otro objeto de datos). Aquí está el resultado de llamar a str (athal)
en este punto.

Eliminar un elemento o atributo de una lista es tan simple como asignarle el valor especial NULL
.

La estructura impresa revela que esta información ha sido eliminada.

¿Cuál es el punto de toda esta lista detallada y asignación de atributos? Resulta ser bastante importante, porque muchas funciones R devuelven exactamente este tipo de listas complejas cargadas de atributos. Considere la función t.test ()
, que compara las medias de dos vectores para la igualdad estadística:

Cuando se imprime, el resultado es un resultado muy bien formateado y legible por humanos.

Si ejecutamos str (tresult)
, sin embargo, encontramos la verdadera naturaleza de tresult
: ¡es una lista!

Dado el conocimiento de esta estructura, podemos extraer fácilmente elementos específicos, como el valor p con pval <- tresult$p.value
o pval <- tresult [["p.value"]]
.
Una nota final sobre listas: vectores (y otros tipos) se pueden convertir en una lista con la función.list ()
. Esto será útil más adelante, porque las listas son uno de los tipos de datos más generales en R, y podemos utilizarlas para representaciones de datos intermediarios.

Ejercicios
El siguiente código genera primero una muestra aleatoria llamada a, y luego una muestra llamada
respuesta
, donde cada elemento derespuesta
es un elemento deun
veces 1.5 más algo de ruido aleatorio:A continuación, podemos crear fácilmente un modelo lineal que predice valores de
respuesta
dea
:Cuando se imprime, la salida describe muy bien los parámetros del modelo.
También podemos probar fácilmente la significancia de los parámetros con la función
anova ()
(para ejecutar una prueba de análisis de varianza en el modelo).La salida nuevamente muestra texto muy bien formateado:
Del
modelo
, extraer el coeficiente de a enuna
variable llamadaa_coeff
(que contendría solo el número1.533367
para esta muestra aleatoria) .Siguiente, devartest
extraer el valor p asociado con el coeficiente a en un vector llamado
a_pval
(para esta muestra aleatoria, el valor p es2.2e-16
).- Escribe una función llamada
simple_lm_pval ()
que automatiza el proceso anterior; debería tomar dos parámetros (dos vectores numéricos potencialmente dependientes linealmente) y devolver el valor p asociado al primer coeficiente (nonintercept). - Cree una lista que contenga tres muestras aleatorias de diferentes distribuciones (por ejemplo, de
rnorm ()
,runif ()
yrexp ()
), y agregue un atributo para“disttype”
a cada una. Useprint ()
ystr ()
en la lista para examinar los atributos que agregó. - Algunos nombres se pueden usar con notación
$
sin comillas; sil <- list (values = c (20, 30))
, entoncesprint (l$values)
imprimirá el vector interno. Por otro lado, sil <- list (“val-entries” = c (20, 30))
, entonces se requieren cotizaciones como en formaimpresa (l$"val-entries”)
. Por experimentación, determinar al menos cinco caracteres diferentes que requieren el uso de comillas al usar notación$
. - Experimenta con las funciones
is.list ()
yas.list ()
, probando cada una de ellas tanto en vectores como en listas.
- Las listas R se utilizan a menudo como diccionarios en Python y tablas hash en otros idiomas, debido a esta operación de búsqueda de nombre → valor fácil y efectiva. Cabe señalar que (al menos a partir de R 3.3), las búsquedas de nombres en las listas no son tan eficientes como las búsquedas de nombres en diccionarios Python u otras tablas hash verdaderas. Para una operación eficiente y más idiomática de tabla/diccionario hash, también existe el
hash
del paquete disponible para instalar coninstall.packages (“hash”)
. - Por ejemplo, los nombres de un vector se almacenan como un atributo llamado “nombres”
—los nombres de líneas (puntuaciones) <- c (“Estudiante A”, “Estudiante B”, “Estudiante C”)
yattr (puntuaciones, “nombres”) <- c (“Estudiante A”, “Estudiante B”, “Estudiante C”)
son (casi) equivalentes. Aún así, se recomienda usar funciones especializadas comonames ()
en lugar de establecerlas conattr ()
porque la funciónnames ()
incluye comprobaciones adicionales sobre la cordura del vector names.