Saltar al contenido principal
LibreTexts Español

3.9: Remodelación y unión de marcos de datos

  • Page ID
    55138
  • \( \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}}\)

    Mirando hacia atrás en el análisis split-apply-combine para los datos de expresión génica, está claro que la organización de los datos funcionó a nuestro favor (después de dividir la columna de muestra en genodad pe, tratamiento y otras columnas, al menos). En particular, cada fila del marco de datos representaba una sola medición, y cada columna representaba una variable (valores que varían entre las mediciones). Esto nos permitió agrupar fácilmente las mediciones por la columna ID. También funcionó bien al construir el modelo lineal, ya que lm () utiliza una fórmula que describe la relación de vectores o columnas de igual longitud (por ejemplo, expr$expression ~ expr$treatment + expr$genotipo), que son directamente accesibles como columnas del marco de datos.

    Los datos a menudo no se organizan en este formato “ordenado”. Este conjunto de datos, por ejemplo, estaba originalmente en un archivo llamado expr_wide.txt con el siguiente formato:

    III.9_1_R_202_Datos

    Los formatos de datos como este suelen ser más convenientes para los lectores humanos que para las máquinas. Aquí, las columnas id y anotación (esta última no mostrada) son variables como antes, pero los nombres de las otras columnas codifican una variedad de variables, y cada fila representa un gran número de mediciones. Los datos en esta “forma” serían mucho más difíciles de analizar con las herramientas que hemos cubierto hasta ahora.

    Convertir entre este formato y el formato preferido (y viceversa) es el objetivo principal del paquete tidyr. Como es habitual, este paquete se puede instalar en el intérprete interactivo con install.packages (“tidyr”). El paquete más antiguo reshape2 también maneja este tipo de reorganización de datos, y aunque las funciones proporcionadas allí son más flexibles y potentes, tidyr cubre la mayoría de las necesidades a la vez que es más fácil de usar.

    Reunión para el orden

    La función de recopilación () en el paquete tidyr hace que la mayoría de los marcos de datos desordenados sean más ordenados. El primer parámetro que se toma es el marco de datos a fijar, y el segundo y el tercero son los nombres de “clave” y “valor” para las columnas recién creadas, respectivamente (sin comillas). Los parámetros restantes especifican los nombres de columna que deben ordenarse (nuevamente, sin comillas). Supongamos que tenemos un marco de datos pequeño y desordenado llamado expr_small, con columnas para id, anotación y columnas para expresión en los genotipos C6 y L4.

    alt

    En este caso, ejecutaríamos la función gather() de la siguiente manera, donde sample y expression son los nuevos nombres de columna a crear, y C6 y L4 son las columnas que necesitan ordenar. (Observe la falta de comillas en todos los nombres de columna; esto es común al azúcar sintáctico de los paquetes tidyr y dplyr.)

    III.9_3_R_203_Gather_small
    III.9_4_R_204_Gather_small_out

    Observe que los datos en las columnas no agrupadas, no ordenadas (id y anotación) se han repetido según sea necesario. Si no se han especificado columnas para ordenar (C6 y L4 en este caso), la recopilación () asume que todas las columnas necesitan ser reorganizadas, resultando en solo dos columnas de salida (muestra y expresión). Esto sería obviamente incorrecto en este caso.

    Enumerar todos los nombres de columna que necesitan ordenar presenta un desafío cuando se trabaja con marcos de datos amplios, como el conjunto completo de datos de expresión. Para reunir esta tabla, necesitaríamos ejecutar la recopilación (expr_wide, sample, expression, c6_chemical_a1, c6_chemical_a3, c6_chemical_b1, y así sucesivamente, enumerando cada uno de los 35 nombres de columna. Afortunadamente, se reúnen () acepta un poco adicional de azúcar sintáctico: usando - y especificando las columnas que no necesitan ser reunidas. Así podríamos recopilar el conjunto completo de datos con

    III.9_5_R_205_Gather_Full_Minus

    La función join () toma algunos otros parámetros opcionales, por ejemplo, para eliminar filas con valores NA. Ver ayuda (“reunir”) en el intérprete interactivo para más información.

    Desaglutinar con untar ()

    Si bien esta organización de los datos, con cada fila siendo una observación y cada columna siendo una variable, suele ser más conveniente para el análisis en R, compartir datos con otras personas a menudo no lo es. La gente tiene afinidad por leer tablas de datos donde muchas mediciones se enumeran en una sola fila, mientras que las versiones “ordenadas” de las tablas de datos suelen ser largas con muchas entradas repetidas. Incluso cuando se analizan programáticamente tablas de datos, a veces ayuda tener múltiples mediciones en una sola fila. Si quisiéramos calcular la diferencia entre las expresiones de genotipo C6 y L4 para cada condición de tratamiento, podríamos desear tener columnas C6_expression y L4_expression, para que luego podamos usar resta vectorizada para calcular un C6_L4_ columna de diferencia.

    La función spread () en el tidyr proporciona esto, y de hecho es el complemento de la función gather. Los tres parámetros importantes son (1) el marco de datos a propagar, (2) la columna a usar como la “clave” y (3) la columna para usar como los “valores”. Considere el marco de datos expr_gathered_small desde arriba.

    alt

    Convertir este marco de datos de nuevo a la versión “amplia” es tan simple como:

    III.9_7_R_206_Spread
    III.9_8_R_207_Spread_out

    Debido a que las entradas en el nombre de columna “clave” se convierten en nuevos nombres de columna, normalmente sería un error usar una columna numérica aquí. En particular, si tuviéramos que mezclar el orden y en su lugar ejecutar spread (expr_gathered_small, expression, sample), terminaríamos con una columna por cada valor único en la columna de expresión, que fácilmente podría numerar en los cientos de miles y probablemente fallaría al intérprete.

    En combinación con group_by (), do () y sumze () del paquete dplyr, se pueden usar gather_by () y spread () para agregar y analizar datos tabulares en un número casi ilimitado de formas. Tanto los paquetes dplyr como tidyr incluyen una serie de otras funciones para trabajar con marcos de datos, incluyendo filtrar filas o columnas por criterios de selección y organizar filas y columnas.

    Dividir columnas

    En el capítulo 32, “Carácter y datos categóricos”, aprendimos a trabajar con vectores de caracteres usando funciones como str_split_fixed () para dividirlos en pedazos basados en un patrón, y str_detect () para producir un vector lógico que indica qué elementos coincidían con un patrón. El paquete tidyr también incluye algunas funciones especializadas para este tipo de operaciones. Considere un marco de datos pequeño expr_sample con columnas para id, expression y sample, como el marco de datos prelimpiado considerado en capítulos anteriores.

    III.9_9_R_208_EXPR_Muestra

    La función tidyr separate () se puede utilizar para dividir rápidamente una columna (carácter o factor) en múltiples columnas basadas en un patrón. El primer parámetro es el marco de datos para trabajar, el segundo es la columna para dividir dentro de ese marco de datos, el tercero especifica un vector de caracteres de nombres de columna recién divididos, y el cuarto parámetro opcional sep = especifica el patrón (expresión regular) para dividir.

    III.9_10_R_209_EXPR_Sample_Separar
    III.9_11_R_210_Expr_Sample_Separate_out

    De manera similar, la función extract () divide una columna en múltiples columnas basadas en un patrón (expresión regular), pero el patrón es más general y requiere una comprensión de las expresiones regulares y la retroreferenciación usando () grupos de captura. Aquí, usaremos el patrón de expresión regular “([A-Z]) ([0-9])” para hacer coincidir cualquier letra mayúscula seguida de un solo dígito, cada uno de los cuales es capturado por un par de paréntesis. Estos valores se convertirán en las entradas para las columnas recién creadas.

    III.9_12_R_211_Expr_Sample_Extract
    III.9_13_R_212_EXPR_MUESTRA_EXTRACT_OUT

    Aunque cubrimos expresiones regulares en capítulos anteriores, para entradas como C6_control_b3 donde asumimos que la codificación está bien descrita, podríamos usar una expresión regular como “(C6|L4) _ (control|chemical) _ (A|B|C) (1|2|3)”.

    Si bien estas funciones son convenientes para trabajar con columnas de marcos de datos, una comprensión de str_split_fixed () y str_detect () es útil para trabajar con datos de caracteres en general.

    Unión/Fusión de Marcos de Datos, cbind () y rbind ()

    Incluso después de que los marcos de datos han sido remodelados y de otra manera masajeados para facilitar los análisis, ocasionalmente datos similares o relacionados están presentes en dos marcos de datos diferentes. Quizás las anotaciones para un conjunto de ID de genes están presentes en un marco de datos, mientras que los valores p de los resultados estadísticos están presentes en otro. Por lo general, tales tablas tienen una o más columnas en común, tal vez una columna id.

    En ocasiones, cada entrada en una de las tablas tiene una entrada correspondiente en la otra, y viceversa. Más a menudo, algunas entradas son compartidas, pero otras no. Aquí hay un ejemplo de dos pequeños marcos de datos donde este es el caso, llamados alturas y edades.

    alt

    La función merge () (que viene con la instalación básica de R) puede unir rápidamente estos dos marcos de datos en uno solo. Por defecto, encuentra todas las columnas que tienen nombres comunes y utiliza todas las entradas que coinciden en todas esas columnas. Aquí está el resultado de la fusión (alturas, edades):

    III.9_15_R_213_Fusiones_alturas_edades

    Esto es mucho más fácil de usar que el programa de unión de línea de comandos: puede unirse en múltiples columnas, y las filas no necesitan estar en ningún orden común. Si nos gusta, podemos especificar opcionalmente un parámetro by =, para especificar por qué nombres de columna unir como vector de caracteres de nombres de columna. Aquí está la fusión (alturas, edades, por = c (“primero”)):

    III.9_16_R_214_Merge_alturas_ages_por_primera

    Debido a que especificamos que la unión solo debería ocurrir por la primera columna, merge () asumió que las dos columnas nombradas last podrían contener datos diferentes y por lo tanto deberían estar representadas por dos columnas diferentes en la salida.

    Por defecto, merge () produce una “unión interna”, lo que significa que las filas están presentes en la salida solo si las entradas están presentes para las entradas izquierda (alturas) y derecha (edades). Podemos especificar all = VERDADERO para realizar una “unión externa” completa. Aquí está la fusión (alturas, edades, todo = VERDADERO).

    III.9_17_R_215_Merge_alturas_ages_todos

    En este ejemplo, se han colocado valores NA para entradas que no están especificadas. A partir de aquí, las filas con entradas NA en la columna de altura o edad se pueden eliminar con selección basada en filas e is.na (), o una “unión externa izquierda” o “unión externa derecha”, se puede realizar con all.x = TRUE o all.y = TRUE, respectivamente .

    En el capítulo 32, también observamos cbind () después de dividir vectores de caracteres en marcos de datos multicolumna. Esta función une dos tramas de datos en una sola columna. No funcionará si los dos marcos de datos no tienen el mismo número de filas, pero funcionará si los dos marcos de datos tienen nombres de columna que son idénticos, en cuyo caso el marco de datos de salida podría tener confusamente varias columnas del mismo nombre. Esta función también dejará las filas del marco de datos en su orden original, así que tenga cuidado de que el orden de las filas sea consistente antes de la vinculación. Generalmente, se prefiere usar merge () para unir marcos de datos por columna a cbind (), incluso si esto significa asegurar que alguna columna de identificador esté siempre presente para servir como columna de enlace.

    La función rbind () combina dos marcos de datos que pueden tener diferentes números de filas pero tienen el mismo número de columnas. Además, los nombres de columna de los dos marcos de datos deben ser idénticos. Si los tipos de datos son diferentes, entonces después de combinarse con rbind (), las columnas de diferentes tipos se convertirán al tipo más general usando las mismas reglas al mezclar tipos dentro de vectores.

    El uso de rbind () requiere que los datos de cada vector de entrada sean copiados para producir el marco de datos de salida, incluso si el nombre de la variable se va a reutilizar como en df <- rbind (df, df2). Siempre que sea posible, los marcos de datos deben generarse con una estrategia split-apply-combine (como con group_by () y do ()) o una técnica de remodelación, en lugar de con muchas aplicaciones repetidas de rbind ().

    Ejercicios

    1. Como se discutió en los ejercicios del Capítulo 33, Dividir, Aplicar, Combinar, el marco de datos de CO2 incorporado contiene mediciones de las tasas de absorción de CO2 para diferentes plantas en diferentes ubicaciones bajo diferentes concentraciones ambientales de CO2.

      Utilice la función spread () en la biblioteca tidyr para producir un marco de datos CO2_spread que se vea así:
      III.9_18_R_215_2_SPREAD_EJERCICIO A continuación, deshaga esta operación con una recopilación (), recreando el marco de datos de CO2 como CO2_recreado.

    2. Ocasionalmente, queremos “remodelar” un marco de datos mientras se computan simultáneamente los resúmenes de los datos. El paquete reshape2 proporciona algunas funciones sofisticadas para este tipo de cálculo (específicamente melt () y cast ()), pero también podemos realizar este tipo de tareas con group_by () y do () (o sumze ()) desde el dplyr paquete en combinación con gathere () y spread () de tidyr.

      A partir del marco de datos de CO2, genere un marco de datos como el siguiente, donde las dos últimas columnas reportan la absorción media para cada combinación Tipo/conc:
      III.9_19_R_215_3_Reshape_alternative Es probable que desee comenzar por calcular las medias grupales apropiadas a partir del CO2 original datos.

    3. Los marcos de datos incorporados castor1 y castero2 describen la temperatura corporal y las observaciones de actividad de dos castores. Fusiona estos dos marcos de datos en uno único que contiene todos los datos y se ve así:
      III.9_20_R_215_4_Reshape_alternative Observe la columna para el nombre —asegúrese de incluir esta columna para que quede claro a qué castor corresponde cada medición!

    This page titled 3.9: Remodelación y unión de marcos de datos is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .