3.6: Marcos de datos
- Page ID
- 55126
\( \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}\)En el capítulo 28, “Vectores”, presentamos brevemente los marcos de datos como tablas de almacenamiento de datos. Ahora que tenemos una comprensión clara tanto de los vectores como de las listas, podemos describir fácilmente los marcos de datos. (Si te saltaste apresuradamente los capítulos 28 y 30 para aprender sobre los marcos de datos, ¡ahora es el momento de volver a ellos!) Los marcos de datos son esencialmente listas nombradas, donde los elementos son vectores que representan columnas. Pero los marcos de datos proporcionan algunas características más que simples listas de vectores. Aseguran que los vectores de columna componente sean siempre de la misma longitud, y nos permiten trabajar con los datos por fila así como por columna. Los marcos de datos son algunos de los tipos de datos más útiles y ubicuos en R.
Si bien ya hemos cubierto el uso de la función read.table ()
para producir un marco de datos basado en el contenido de un archivo de texto, también es posible crear un marco de datos a partir de un conjunto de vectores.


Cuando se imprimen, el contenido de los vectores de columna se muestra cuidadosamente, con los nombres de las columnas a lo largo de la parte superior y los nombres de fila a lo largo del lado izquierdo
Al igual que con read.table ()
, la función data.frame ()
toma un argumento opcional StringSasFactors
, que especifica si los vectores de caracteres (como ids
) deben convertirse a tipos de factores (los cubriremos en detalle más adelante). Por ahora, deshabilitaremos esta conversión.
Ejecutar str (gene_info)
revela la naturaleza de lista del marco de datos:

Al igual que elementos de listas, las columnas de marcos de datos no tienen que tener nombres, pero no tenerlos es poco común. La mayoría de los marcos de datos obtienen nombres de columna cuando se crean (ya sea por read.table ()
o data.frame ()
), y si se desestablecen, generalmente son V1
, V2
, y así sucesivamente. Los nombres de las columnas se pueden acceder y establecer con la función names ()
, o con la función colnames ()
más apropiada.

Para resaltar la naturaleza tipo lista de los marcos de datos, podemos trabajar con marcos de datos por columna de manera muy similar a listas por elemento. Las tres líneas del siguiente ejemplo dan como resultado que sub_info
sea un marco de datos de dos columnas.

Por lo tanto, una expresión como gene_info [2]
no devolvería un vector numérico de longitudes, sino un marco de datos de una sola columna que contiene el vector numérico. Podemos usar la sintaxis [[]]
y la sintaxis $
para referirnos también a los vectores contenidos dentro de los marcos de datos (este último es mucho más común).

Incluso podemos eliminar columnas de un marco de datos estableciendo el elemento en NULL
, como en gene_info$length <- NULL
.
El verdadero encanto de los marcos de datos es que podemos extraer y de otra manera trabajar con ellos por fila. Así como los marcos de datos tienen nombres de columna, también tienen nombres de fila: un vector de caracteres de la misma longitud que cada columna. Desafortunadamente, por defecto, los nombres de fila son “1"
, “2"
, “3"
, y así sucesivamente, pero cuando se imprime el marco de datos, se dejan las comillas (ver el resultado de print (gene_info)
arriba). Los nombres de fila son accesibles a través de la función rownames ()
.
Los marcos de datos son indexables usando una sintaxis extendida []
: [,]
<row_selector><column_selector>, donde <row_selector>y
<column_selector>son vectores. Al igual que con los vectores y listas, estos vectores de indexación/selección pueden ser enteros (para seleccionar por índice), caracteres (para seleccionar por nombre) o lógicos (para seleccionar lógicamente). También al igual que con los vectores, al indexar por posición de índice o nombre, se respeta el orden solicitado.

Aquí está el resultado resultante, ilustrando que “3"
y “1"
eran los nombres de las filas, que ahora aparecen en la primera y segunda fila, respectivamente. [1]

Si encuentra esto confuso, considere qué pasaría si primero asignáramos los nombres de fila del marco de datos original a algo más razonable, antes de la extracción.

Ahora, cuando se imprimen, se revela la naturaleza de carácter de los nombres de las filas.

Finalmente, si uno de <row_selector>o <column_selector>no
se especifica, entonces se incluyen todas las filas o columnas. Como ejemplo,
gene_info [c (3,1),]
devuelve un marco de datos con la tercera y primera filas y las tres columnas, mientras que gene_info [, c (“longitudes”, “ids”)]
devuelve una con solo las columnas “longitudes”
e “ids”
, pero todas las filas.
Operaciones de marco de datos
Debido a que los marcos de datos tienen mucho en común con las listas y filas, y las columnas se pueden indexar por número de índice, nombre o vector lógico, hay muchas formas poderosas de manipularlos. Supongamos que queríamos extraer solo aquellas filas donde la columna de longitudes
es menor que 200
, o la columna gcs
es menor que 0.3
.

Esta sintaxis es concisa pero sofisticada. Mientras que gene_info$length
se refiere al vector numérico llamado “longitudes”
en el marco de datos, el operador lógico <
se vectoriza, reciclándose el elemento único 200
según sea necesario. El mismo proceso ocurre para gene_info$gcs < 0.3
, y el operador lógico-or |
es vectorizado, produciendo un vector lógico usado posteriormente para seleccionar las filas de interés. Se seleccionaría una versión aún más corta de estas dos líneas <- gene_info [gene_info$longitudes < 200 | gene_info$gcs < 0.3,]
. La salida impresa:

Si quisiéramos extraer el vector gcs
de este resultado, podríamos usar algo como selected_gcs <- selected$gcs
. A veces se utiliza una sintaxis más compacta, donde el $
y el nombre de la columna se anexan directamente a la sintaxis []
.

Alternativamente, y quizás más claramente, primero podemos usar la notación $
para extraer la columna de interés, y luego usar []
indexación lógica en el vector resultante.

Debido a que subestablecer filas de marcos de datos por condición lógica es tan común, hay una función especializada para esta tarea: subset ()
. El primer parámetro es el marco de datos desde el que seleccionar, y los parámetros posteriores son expresiones lógicas basadas en nombres de columna dentro de ese marco de datos (se dejan comillas). Por ejemplo, seleccionado <- subconjunto (gene_info, longitudes < 200 | gcs < 0.3)
. Si se da más de una expresión lógica, se combinan con &
(y). Así subconjunto (gene_info, longitudes < 200, gcs < 0.3)
es equivalente a gene_info [gene_info$longitudes < 200 & gene_info$gcs < 0.3,]
.
Si bien la función subset ()
es conveniente para extracciones simples, conocer los entresijos de []
la selección de marcos de datos en lo que se refiere a listas y vectores es una herramienta poderosa. Considere la función order ()
, que, dado un vector, devuelve un vector índice que se puede utilizar para ordenar. Al igual que con el uso de order ()
para ordenar un vector, podemos usar order ()
para ordenar un marco de datos basado en una columna en particular.

El resultado es un marco de datos ordenado por la columna de longitudes:

Debido a que los marcos de datos obligan a todos los vectores de columna a tener la misma longitud, podemos crear nuevas columnas asignándolas por su nombre y confiando en el reciclaje vectorial para llenar la columna según sea necesario. Vamos a crear una nueva columna llamada gc_categories
, que inicialmente se llena con valores NA
, y luego usar el reemplazo selectivo para asignar valores “low”
o “high”
dependiendo del contenido de la columna gcs
.

Si bien existen enfoques más automatizados para categorizar datos numéricos, el ejemplo anterior ilustra la flexibilidad y potencia del marco de datos y la sintaxis vectorial cubiertos hasta ahora.

Una nota final: mientras que la función head ()
devuelve los primeros elementos de un vector o lista, cuando se aplica a un marco de datos, devuelve un marco de datos similar con solo las primeras filas.
Matrices y matrices
Dependiendo del tipo de análisis, es posible que se encuentre trabajando con matrices en R, que son esencialmente vectores bidimensionales. Al igual que los vectores, todos los elementos de una matriz deben ser del mismo tipo, y los intentos de mezclar tipos darán como resultado la autoconversión. Al igual que los marcos de datos, son bidimensionales, y por lo tanto se pueden indexar con la <row_selector><column_selector>sintaxis [,]
. También tienen nombres rownames ()
y colnames ()
.


Hay una serie de funciones interesantes para trabajar con matrices, incluyendo det ()
(para calcular el determinante de una matriz) y t ()
(para transponer una matriz).
Las matrices generalizan el concepto de datos multidimensionales; donde las matrices solo tienen dos dimensiones, las matrices pueden tener dos, tres o más. La línea a <- array (c (1,1,1,1,2,2,2,2,3,3,3,3), dim = c (2,2,3))
, por ejemplo, crea una matriz tridimensional, compuesta por tres matrices de dos por dos. El elemento superior izquierdo se puede extraer como un [1,1,1]
.
Ejercicios
- Lea el archivo
states.txt
en un marco de datos, como se describe en el Capítulo 28, Vectores. Extraiga un nuevo marco de datos llamadostates_name_pop
que contenga solo las columnas paranombre
ypoblación
. - Usando
subset ()
, extraiga un marco de datos llamadostates_gradincome_high
que contenga todas las columnas, y todas las filas donde elingreso
sea mayor que la mediana delingreso
o dondehs_grad
sea mayor que la mediana dehs_grad
. (Hay 35 estados de este tipo enumerados en la tabla.) A continuación, haga la misma extracción con la <row_selector><column_selector>sintaxis[,]
. - Crear una tabla llamada
states_by_region_income
, donde las filas se ordenan primero por región y segundo por ingresos. (La funciónorder ()
puede tomar múltiples parámetros como enorden (vec1, vec2)
; se consideran a su vez para determinar el orden). - Utilice la <row_selector><column_selector>sintaxis
order ()
,colnames ()
y[,]
para crear un marco de datosstates_cols_ordered
, donde las columnas están ordenadas alfabéticamente (es decir,hs_grad
primero, luegoingreso
, luegoasesinato
, entoncesnombre
, y así sucesivamente). La función
Úselos para producirnrow ()
devuelve el número de filas en un marco de datos, mientras querev ()
invierte un vector yseq (a, b)
devuelve un vector de números enteros de a ab
(inclusive).states_by_income_rev
, que es el marco de datos de estados pero con filas que aparecen en orden inverso de ingresos (ingresos más altos en la parte superior).- Intente convertir el marco de datos de
estados
en una matriz ejecutándolo a través dela función.matrix (
)
. ¿Qué pasa con los datos, y por qué crees que es eso?
- Desafortunadamente, cuando se imprimen, las comillas se dejan fuera de los nombres de las filas, lo que suele llevar a los programadores a pensar que los índices de fila son lo que se muestra. Del mismo modo, las comillas se dejan fuera de las columnas que son vectores de caracteres (como la columna
ids
en este ejemplo), lo que puede causar confusión si la columna es un tipo de carácter con elementos como“1"
,“2"
, y así sucesivamente, potencialmente llevando al programador a pensar en la columna es numérico. En un giro final de confusión, el uso de la <row_selector><column_selector>sintaxis[,]
rompe un patrón común:[]
la indexación de un vector siempre devuelve un vector,[]
la indexación de una lista siempre devuelve una lista, y[,]
<row_selector><column_selector> la indexación de un marco de datos siempre devuelve un marco de datos, a menos que tenga una sola columna, en cuyo caso se devuelve la columna/vector. El remedio para ello es[,, drop = FALSO]
<row_selector><column_selector>, que instruye a R a no “soltar” una dimensión.