Saltar al contenido principal

# 7.11: Otras estructuras de datos útiles

$$\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}$$

Hasta este punto nos hemos encontrado con varios tipos diferentes de variables. En el nivel más simple, hemos visto datos numéricos, datos lógicos y datos de caracteres. Sin embargo, también hemos encontrado algunos tipos de variables más complicados, a saber, factores, fórmulas, marcos de datos y listas. Veremos algunas estructuras de datos más especializadas más adelante en este libro, pero hay algunas más genéricas de las que quiero hablar de pasada. Ninguno de ellos es central para el resto del libro (y de hecho, el único que incluso veremos en cualquier otro lugar es la matriz), pero sí surgen un poco en la vida real.

## Matrices

En varios lugares diferentes de este capítulo he hecho referencia a una estructura de datos R llamada matriz, y mencioné que hablaría un poco más sobre matrices más adelante. Ese tiempo ha llegado. Al igual que un marco de datos, una matriz es básicamente una gran tabla rectangular de datos, y de hecho hay bastantes similitudes entre las dos. Sin embargo, también hay algunas diferencias clave, por lo que es importante hablar de matrices con un poco de detalle. Comencemos usando rbind () para crear una matriz pequeña: 126

row.1 <- c( 2,3,1 )         # create data for row 1
row.2 <- c( 5,6,7 )         # create data for row 2
M <- rbind( row.1, row.2 )  # row bind them into a matrix
print( M )                  # and print it out...
##       [,1] [,2] [,3]
## row.1    2    3    1
## row.2    5    6    7

La variable M es una matriz, que podemos confirmar usando la función class (). Observe que, cuando unimos los dos vectores juntos, R retuvo los nombres de las variables originales como nombres de fila. Podríamos eliminarlos si quisiéramos escribiendo rownames (M) <-NULL, pero generalmente prefiero tener nombres significativos adjuntos a mis variables, así que los mantendré. De hecho, también agreguemos algunos nombres de columna muy poco imaginativos:

colnames(M) <- c( "col.1", "col.2", "col.3" )
print(M)
##       col.1 col.2 col.3
## row.1     2     3     1
## row.2     5     6     7

Puede usar corchetes para subdividir una matriz de la misma manera que puede para los marcos de datos, especificando nuevamente un índice de fila y luego un índice de columna. Por ejemplo, M [2,3] saca la entrada en la 2da fila y 3ra columna de la matriz (i.e., 7), mientras que M [2,] saca toda la 2da fila, y M [,3] saca toda la 3ra columna. No obstante, vale la pena señalar que al sacar una columna, R imprimirá los resultados horizontalmente, no verticalmente. La razón de esto se relaciona con cómo se implementan las matrices (y las matrices en general). La matriz original M se trata como un objeto bidimensional, que contiene 2 filas y 3 columnas. Sin embargo, cada vez que se saca una sola fila o una sola columna, el resultado se considera unidimensional. En lo que a R se refiere, no hay ninguna razón real para distinguir entre un objeto unidimensional impreso verticalmente (una columna) y un objeto unidimensional impreso horizontalmente (una fila), y los imprime todos horizontalmente. 127 También hay una manera de usar solo un único índice, pero debido a la estructura interna a cómo R define una matriz, funciona de manera muy diferente a lo que vimos anteriormente con los marcos de datos.

El enfoque de índice único se ilustra en la Tabla 7.5 pero realmente no quiero centrarme en él ya que nunca lo necesitaremos realmente para este libro, y las matrices no juegan un papel tan importante en este libro como lo hacen los marcos de datos. La razón de estas diferencias es que para ello es que, tanto para los marcos de datos como para las matrices, existe la versión de “fila y columna” para permitir al usuario humano interactuar con el objeto de una manera psicológicamente significativa: dado que tanto los marcos de datos como las matrices son básicamente solo tablas de datos, es lo mismo en cada caso. Sin embargo, la versión de índice único es realmente un método para que interactúes con el objeto en términos de su estructura interna, y los internos para marcos de datos y matrices son bastante diferentes.

Cuadro 7.5: La versión de fila y columna, que es idéntica al esquema de indexación correspondiente para un marco de datos del mismo tamaño.

Fila Col.1 Col.2 Col.3
Fila 1 [1,1] [1,2] [1,3]
Fila 2 [2,1] [2,2] [2,3]

Tabla 7.5: La versión de índice único, que es bastante diferente a lo que obtendríamos con un marco de datos.

Fila Col.1 Col.2 Col.3
Fila 1 1 3 5
Fila 2 2 4 6

La diferencia crítica entre un marco de datos y una matriz es que, en un marco de datos, tenemos esta noción de que cada una de las columnas corresponde a una variable diferente: como consecuencia, las columnas en un marco de datos pueden ser de diferentes tipos de datos. La primera columna podría ser numérica, y la segunda columna podría contener cadenas de caracteres, y la tercera columna podría ser datos lógicos. En ese sentido, existe una asimetría fundamental integrada en un marco de datos, debido a que las columnas representan variables (que pueden ser cualitativamente diferentes entre sí) y las filas representan casos (que no pueden). Las matrices están pensadas para ser pensadas de una manera diferente. A nivel fundamental, una matriz realmente es solo una variable: simplemente sucede que esta variable está formateada en filas y columnas. Si quieres una matriz de datos numéricos, cada elemento de la matriz debe ser un número. Si quieres una matriz de cadenas de caracteres, cada elemento de la matriz debe ser una cadena de caracteres. Si intenta mezclar datos de diferentes tipos juntos, entonces R escupirá un error o coaccionará silenciosamente los datos subyacentes a una lista. Si quieres saber qué clase R piensa secretamente que son los datos dentro de la matriz, necesitas hacer algo como esto:

class( M[1] )

## [1] "numeric"

No puedes escribir class (M), porque todo lo que va a pasar es R te dirá que M es una matriz: no nos interesa la clase de la matriz en sí, queremos saber qué clase se supone que son los datos subyacentes. De todos modos, para darte una idea de cómo R hace cumplir esto, intentemos cambiar uno de los elementos de nuestra matriz numérica en una cadena de caracteres:

M[1,2] <- "text"
M
##       col.1 col.2  col.3
## row.1 "2"   "text" "1"
## row.2 "5"   "6"    "7"

Parece que R ha coaccionado todos los datos de nuestra matriz en cadenas de caracteres. Y de hecho, si ahora escribiéramos en clase (M [1]) veríamos que esto es exactamente lo que ha pasado. Si modifica el contenido de un elemento en una matriz, R cambiará el tipo de datos subyacente según sea necesario.

Sólo hay una cosa más de la que quiero hablar respecto a las matrices. El concepto detrás de una matriz es en gran medida matemático, y en matemáticas una matriz es definitivamente un objeto bidimensional. Sin embargo, al hacer análisis de datos, a menudo tenemos razones para querer usar tablas de dimensiones superiores (por ejemplo, a veces es necesario tabular tres variables entre sí). No se puede hacer esto con matrices, pero puede hacerlo con matrices. Una matriz es igual que una matriz, excepto que puede tener más de dos dimensiones si es necesario. De hecho, en lo que a R se refiere, una matriz es solo un tipo especial de matriz, de la misma manera que un marco de datos es un tipo especial de lista. No quiero hablar demasiado de arreglos, pero te voy a mostrar muy brevemente un ejemplo de cómo se ve una matriz 3D. Para ello, vamos a tabular las variables speaker y utterance del archivo de datos NightGarden.rData, pero esta vez agregaremos una tercera variable a las pestañas cruzadas, una variable lógica que indica si todavía estaba despierto o no en este punto del programa:

dan.awake <- c( TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,FALSE,FALSE,FALSE,FALSE )

Ahora que tenemos las tres variables en el espacio de trabajo (suponiendo que cargó los datos NightGarden.rData anteriormente en el capítulo) podemos construir nuestra tabulación cruzada de tres vías, usando la función table ().

xtab.3d <- table( speaker, utterance, dan.awake )
xtab.3d
## , , dan.awake = FALSE
##
##              utterance
## speaker       ee onk oo pip
##   makka-pakka  0   2  0   2
##   tombliboo    0   0  1   0
##   upsy-daisy   0   0  0   0
##
## , , dan.awake = TRUE
##
##              utterance
## speaker       ee onk oo pip
##   makka-pakka  0   0  0   0
##   tombliboo    1   0  0   0
##   upsy-daisy   0   2  0   2

Ojalá esta salida sea bastante sencilla: debido a que R no puede imprimir texto en tres dimensiones, lo que hace es mostrar una secuencia de cortes 2D a través de la tabla 3D. Es decir, la parte,, dan.wake = FALSO indica que la tabla 2D que sigue a continuación muestra la tabulación cruzada 2D de hablante contra enunciación solo para las instancias dan.despierto = FALSO, y así sucesivamente. 128

Un tema que olvidé mencionar al discutir factores anteriormente (Sección 4.7 es que en realidad hay dos tipos diferentes de factor en R, factores desordenados y factores ordenados. Un factor desordenado corresponde a una variable de escala nominal, y todos los factores que hemos discutido hasta ahora en este libro han sido desordenados (al igual que todos los factores utilizados en cualquier otro lugar excepto en esta sección). Sin embargo, a menudo es muy útil decirle explícitamente a R que tu variable es escala ordinal, y si es así necesitas declararla como un factor ordenado. Por ejemplo, anteriormente en este capítulo hicimos uso de una variable que consiste en datos de escala Likert, que representamos como la variable likert.raw:

likert.raw
##  [1] 1 7 3 4 4 4 2 6 5 5

Podemos declarar que esto es un factor ordenado usando la función factor (), y configurando ordered = TRUE. Para ilustrar cómo funciona esto, vamos a crear un factor ordenado llamado likert.ordinal y echemos un vistazo:

likert.ordinal <- factor( x = likert.raw,        # the raw data
levels = seq(7,1,-1),  # strongest agreement is 1, weakest is 7
ordered = TRUE )       # and it's ordered
print( likert.ordinal )
##  [1] 1 7 3 4 4 4 2 6 5 5
## Levels: 7 < 6 < 5 < 4 < 3 < 2 < 1

Observe que cuando imprimimos el factor ordenado, R nos dice explícitamente en qué orden entran los niveles. Debido a que quería ordenar mis niveles en términos de aumentar la fuerza de acuerdo, y porque una respuesta de 1 correspondía al acuerdo más fuerte y 7 al desacuerdo más fuerte, era importante que le dijera a R que codificara 7 como el valor más bajo y 1 como el más grande. Siempre comprueba esto al crear un factor ordenado: es muy fácil codificar accidentalmente tus datos “al revés” si no estás prestando atención. En cualquier caso, tenga en cuenta que podemos (y debemos) adjuntar nombres significativos a estos niveles de factores mediante el uso de la función levels (), así:

levels( likert.ordinal ) <- c( "strong.disagree", "disagree", "weak.disagree",
"neutral", "weak.agree", "agree", "strong.agree" )
print( likert.ordinal )
##  [1] strong.agree    strong.disagree weak.agree      neutral
##  [5] neutral         neutral         agree           disagree
##  [9] weak.disagree   weak.disagree
## 7 Levels: strong.disagree < disagree < weak.disagree < ... < strong.agree

Una cosa buena del uso de factores ordenados es que hay muchos análisis para los cuales R trata automáticamente los factores ordenados de manera diferente a los factores desordenados, y generalmente de una manera que es más apropiada para los datos ordinales. No obstante, como no lo discuto en este libro, no voy a entrar en detalles. Como tantas cosas en este capítulo, mi principal objetivo aquí es hacerte consciente de que R tiene esta capacidad incorporada; así que si alguna vez necesitas empezar a pensar en las variables de escala ordinal con más detalle, ¡tienes al menos alguna idea de por dónde empezar a buscar!

## Fechas y horarios

Los horarios y las fechas son tipos de datos muy molestos. A una primera aproximación podemos decir que hay 365 días en un año, 24 horas en un día, 60 minutos en una hora y 60 segundos en un minuto, pero eso no es del todo correcto. La duración del día solar no es exactamente 24 horas, y la duración del año solar no es exactamente 365 días, por lo que tenemos un complicado sistema de correcciones que hay que hacer para mantener funcionando el sistema de fecha y hora. Además de eso, la medición del tiempo generalmente se toma en relación con una zona horaria local, y la mayoría (pero no todas) las zonas horarias tienen tanto una hora estándar como una hora de verano, aunque la fecha en la que ocurre el cambio no está en absoluto estandarizada. Entonces, como forma de datos, los tiempos y las fechas apestan. Desafortunadamente, también son importantes. A veces es posible evitar tener que usar cualquier sistema complicado para tratar horarios y fechas. A menudo solo quieres saber en qué año sucedió algo, así que solo puedes usar datos numéricos: en bastantes situaciones algo tan simple como esto.año <- 2011 funciona bien. Si puedes salirte con la tuya para tu aplicación, esto es probablemente lo mejor que puedes hacer. No obstante, a veces realmente necesitas saber la fecha real. O, peor aún, el tiempo real. En esta sección, te voy a presentar muy brevemente los conceptos básicos de cómo R trata los datos de fecha y hora. Al igual que con muchas cosas en este capítulo, no voy a entrar en detalles porque no uso este tipo de datos en ningún otro lugar del libro. El objetivo aquí es mostrarte los conceptos básicos de lo que debes hacer si alguna vez encuentras este tipo de datos en la vida real. Y entonces todos estaremos de acuerdo en no volver a hablar de ello.

Para empezar, hablemos de la fecha. Como sucede, los sistemas operativos modernos son muy buenos para realizar un seguimiento de la hora y la fecha, e incluso pueden manejar todos esos molestos problemas de zona horaria y el ahorro de luz diurna bastante bien. Entonces R toma la opinión bastante sensata de que solo puede preguntarle al sistema operativo cuál es la fecha. Podemos sacar la fecha usando la función Sys.Date ():

today <- Sys.Date()  # ask the operating system for the date
print(today)         # display the date
## [1] "2018-12-30"

Bien, eso parece sencillo. Pero, más bien parece que hoy es solo una cadena de caracteres, ¿no es así? Eso sería un problema, porque las fechas realmente tienen un carácter numérico para ellas, y sería bueno poder hacerles suma y resta básicas. Bueno, no temas. Si escribes clase (hoy), R te dirá que la clase de la variable today es “Date”. Lo que esto significa es que, oculta debajo de esta cadena de texto que imprime una fecha real, R en realidad tiene una representación numérica. 129 Lo que eso significa es que en realidad puedes sumar y restar días. Por ejemplo, si agregamos 1 al día de hoy, R imprimirá la fecha para mañana:

today + 1
## [1] "2018-12-31"

Veamos qué pasa cuando sumamos 365 días:

today + 365
## [1] "2019-12-30"

Esto es particularmente útil si olvidas que un año es un año bisiesto ya que en ese caso probablemente te equivocarías es hacer esto en tu cabeza. R proporciona una serie de funciones para trabajar con fechas, pero no quiero hablar de ellas con ningún detalle. Sin embargo, voy a hacer una mención pasajera de la función días de la semana () que te dirá a qué día de la semana correspondió una fecha en particular, lo cual es sumamente conveniente en algunas situaciones:

weekdays( today )
## [1] "Sunday"

También señalaré que puede utilizar el AS.date () para convertir varios tipos diferentes de datos en fechas. Si los datos resultan ser cadenas formateadas exactamente de acuerdo con la notación estándar internacional (es decir, aaaa-mm-dd) entonces la conversión es sencilla, porque ese es el formato que R espera ver por defecto. También puedes convertir fechas de otros formatos, pero es un poco más complicado, y más allá del alcance de este libro.

¿Qué pasa con los tiempos? Bueno, los tiempos son aún más molestos, tanto es así que no pretendo hablar de ellos en absoluto en este libro, aparte de señalarte en la dirección de algunas cosas vagamente útiles. R sí mismo le proporciona algunas herramientas para manejar datos de tiempo, y de hecho hay dos clases separadas de datos que se utilizan para representar tiempos, conocidas por los nombres impares PosiXCT y PosiXLT. Puedes usarlos para trabajar con tiempos si quieres, pero para la mayoría de las aplicaciones probablemente sería mejor descargar el paquete chron, que proporciona algunas herramientas mucho más fáciles de usar para trabajar con horarios y fechas.

This page titled 7.11: Otras estructuras de datos útiles is shared under a CC BY-SA 4.0 license and was authored, remixed, and/or curated by Danielle Navarro via source content that was edited to the style and standards of the LibreTexts platform.