Tabla de Contenidos
Introducción
En el post anterior hemos hablado sobre como manipular un data frame mediante al paquete dplyr
. Ahora haremos lo propio con el paquete tidyr
. No obstante, antes de ponernos a ver las funcionalidades que nos provee este, debemos conocer algunos conceptos sobre la forma en que debe estar organizada la información en un data frame de modo que pueda ser utilizada por diversos paquetes que iremos viendo en próximos post.
Los temas presentados en este post se basan en el trabajo Tidy Data, publicado en the Journal of Statistical Software: http://www.jstatsoft.org/v59/i10/paper.
Entonce, lo primero, instalamos el paquete y lo cargamos.
install.packages("tidyr") library(tidyr)
Qué significa que un data frame esté ordenado
La misma información puede ser representada de muchas maneras, es decir, diversos data frames pueden contener la misma información, cada uno ordenándola de una forma diferente. Para ejemplificar esto, he tomado algo de información demográfica de Argentina desde Wikipedia. Como se observa, tenemos tres data frames que contienen la misma información. Cada uno muestra los mismos valores para cada una de las variables que se representan (país, censo, población urbana, población total), pero cada uno los organiza de manera diferente.
df1 Pais Censo Población.urbana Población Argentina 1980 23.198.068 27.949.480 Argentina 1991 28.832.127 32.615.528 Argentina 2001 32.380.296 36.260.130 Argentina 2010 36.907.728 40.117.096 df2 Pais Censo Tipo Población Argentina 1980 urbana 23.198.068 Argentina 1980 total 27.949.480 Argentina 1991 urbana 28.832.127 Argentina 1991 total 32.615.528 Argentina 2001 urbana 32.380.296 Argentina 2001 total 36.260.130 Argentina 2010 urbana 36.907.728 Argentina 2010 total 40.117.096 df3 Pais Censo Tasa.de.población.urbana Argentina 1980 23.198.068 / 27.949.480 Argentina 1991 28.832.126 / 32.615.528 Argentina 2001 32.380.296 / 36.260.130 Argentina 2010 36.907.728 / 40.117.096
No obstante, no todos los casos presentan la misma facilidad para utilizar dicha información. En particular, el primer data frame se dice que está «ordenado», lo que hace que presente mayor facilidad para trabajar con él mediante diferentes herramientas que iremos viendo.
Hay 3 reglas que se deben cumplir para considerar que un data frame está ordenado:
- Cada variable debe tener su propia columna
- Cada observación debe tener su propia fila
- Cada valor deber tener su propia celda
Estas tres reglas están interrelacionadas debido a que es imposible satisfacer solo dos de las tres. En la siguiente imagen se observan visualmente estas reglas.
Las funcionalidades que iremos viendo del paquete tidyr
tienen como finalidad hacer transformaciones de un data frame de modo que podemos llevarlo de una a otra estructura. En particular, lo que buscaremos hacer es, cuando nos enfrentemos con información representada como en el caso de df2
o df3
, poder transformarla a la forma de df1
. Es decir, cada fila es una observación y cada columna una variable.
Ahora bien, ¿porqué estos paquetes que veremos más adelante necesitan que la información esté organizada con este criterio?
Por un lado está el hecho de que tener un único criterio para considerar a la información ordenada «correctamente» hace que el diseño de paquetes y nuevas funcionalidades se simplifique. Esto es debido a que las funciones siempre esperarán a la información de entrada con una estructura conocida, y a su vez entregaran los resultados con la misma estructura. Por otro lado, el hecho de tener que cada columna represente a una variable permite la utilización de funciones vectorizadas como hemos ido viendo a los largo del curso.
El paquete tidyr
El paquete tidyr
brinda muchas funcionalidades para realizar transformaciones en un data frame con el fin de poder transformarlo a la estructura que deseemos. El primer paso para esto es averiguar en el dataset cuales son las variables y cuales son las observaciones. Una vez que las hemos identificado, nos enfrentamos a tres tipos de problemas:
- Una variable está dividida en múltiples columnas.
- Una observación está dispersa en múltiples filas.
- Múltiples variables están metidas en una única celda.
Usualmente un dataset solo tendrá unos de estos problemas. Para remediar esto deberemos utilizar las funciones que veremos a continuacion.
gather
La función gather
toma múltiples columnas y las une en pares clave-valor. Esto permite resolver las situaciones en que tenemos columnas que realmente no representan variables, sino valores de una variable.
Supongamos que tenemos el siguiente data frame:
df
Provincia Censo_1991 Censo_2001 Censo_2010
Buenos Aires 10934727 11460575 13596320
Cordoba 1208554 1368301 1466823
Rosario 1118905 1161188 1236089
Las columnas Censo_1991, Censo_2001
y Censo_2010
representan a cada censo (o el año en que se realizó cada censo), es decir, el valor de una variable nominal que podría llamarse censo, y cada fila representa tres observaciones, y no una sola.
Para ordenar este dataset necesitamos emplear la función gather
.
gather(data = df, key = "censo", value = "poblacion", 2:4)
Provincia censo poblacion
Buenos Aires Censo_1991 10934727
Cordoba Censo_1991 1208554
Rosario Censo_1991 1118905
Buenos Aires Censo_2001 11460575
Cordoba Censo_2001 1368301
Rosario Censo_2001 1161188
Buenos Aires Censo_2010 13596320
Cordoba Censo_2010 1466823
Rosario Censo_2010 1236089
Lo que hemos hecho es decir a la función que tome el data frame df
, y una las columnas 2:4
(las que tienen los valores obtenidos en cada censo), metiendo el resultado en el par clave (censo) – valor (población).
spread
La función spread
es usada cuando tenemos una observación dispersa en múltiples filas. En el siguiente ejemplo tenemos un dataset donde una observación es el resultado de un censo, pero cada observación (Censo) está esparcido en dos filas (una fila para la población total, y otro para la población urbana).
df
Pais Censo Tipo Población
Argentina 1980 urbana 23.198.068
Argentina 1980 total 27.949.480
Argentina 1991 urbana 28.832.127
Argentina 1991 total 32.615.528
Argentina 2001 urbana 32.380.296
Argentina 2001 total 36.260.130
Argentina 2010 urbana 36.907.728
Argentina 2010 total 40.117.096
Para llevar este dataset a la forma de una observación por fila debemos usar la función spread
.
spread(data = df, key = Tipo, value = Población)
Pais Censo total urbana
Argentina 1980 27.949.480 23.198.068
Argentina 1991 32.615.528 28.832.127
Argentina 2001 36.260.130 32.380.296
Argentina 2010 40.117.096 36.907.728
La columna que contiene los nombre de las variables corresponde al argumento key
, mientras que la columna que contiene el valor de la variable es value
.
Como se observa, la función spread
hace lo opuesto a gather
. Estas son funciones complementarias, es decir, si al resultado de aplicar la función spread
le aplicamos la función gather
llegamos al dataset original. Otra observación interesante es que gather
alarga los data frames, mientras que spread
los hace más ancho.
separate
Ahora supongamos que tenemos el siguiente dataset.
df
Pais Censo Tasa.de.población.urbana
Argentina 1980 23198068 / 27949480
Argentina 1991 28832126 / 32615528
Argentina 2001 32380296 / 36260130
Argentina 2010 36907728 / 40117096
Lo que observamos es que tenemos una columna (Tasa.de.población.urbana
) que contiene más de un valor. Para solucionar esto tenemos que emplear la función separate
. Esta función lo que hace es dividir una columnas en múltiples columnas, tomando como separador algún símbolo.
separate(data = df,
col = Tasa.de.población.urbana,
into = c("urbana", " total"),
sep = "/")
Pais Censo urbana total
Argentina 1980 23198068 27949480
Argentina 1991 28832126 32615528
Argentina 2001 32380296 36260130
Argentina 2010 36907728 40117096
Por defecto, la función separete
usa como separador cualquier valor no alfa-numerico. En este ejemplo he indicado explícitamente el separador (sep
) solo para claridad.
Por otro lado, el tipo de dato que se asigna a las nuevas columnas es el mismo que el original.
str(df) 'data.frame': 4 obs. of 3 variables: $ Pais : chr "Argentina" "Argentina" "Argentina" "Argentina" $ Censo : chr "1980" "1991" "2001" "2010" $ Tasa.de.población.urbana: chr "23198068 / 27949480" "28832126 / 32615528" "32380296 / 36260130" "36907728 / 40117096" str(separate(data = df, col = Tasa.de.población.urbana, into = c("urbana", " total"), sep = "/")) 'data.frame': 4 obs. of 4 variables: $ Pais : chr "Argentina" "Argentina" "Argentina" "Argentina" $ Censo : chr "1980" "1991" "2001" "2010" $ urbana: chr "23198068 " "28832126 " "32380296 " "36907728 " $ total: chr " 27949480" " 32615528" " 36260130" " 40117096"
Como es de esperar, las columnas resultantes de la división son de tipo character
, pero dado su significado lo que deseamos es que sean tratadas como numeric
o integer
. Para indicar esto podemos hacer la conversion del tipo de dato manualmente, o se puede agregar la opcion convert = TRUE
a la función separate
.
str(separate(data = df,
col = Tasa.de.población.urbana,
into = c("urbana", " total"),
sep = "/",
convert = TRUE))
'data.frame': 4 obs. of 4 variables:
$ Pais : chr "Argentina" "Argentina" "Argentina" "Argentina"
$ Censo : chr "1980" "1991" "2001" "2010"
$ urbana: num 23198068 28832126 32380296 36907728
$ total: int 27949480 32615528 36260130 40117096
unite
Así como teníamos a las funciones complementarias gather
y spread
, tenemos a la función unite
que complementa a separate
. Esta función lo que hace es tomar múltiples columnas (...
) y las colapsa en una única columna (col
), separando los elementos mediante sep
.
df ID dia mes anio 1 25 9 2015 2 30 7 2015 3 7 3 2015 4 15 8 2015
unite(data = df, col = Fecha, sep = "-", dia,mes,anio) ID Fecha 1 25-9-2015 2 30-7-2015 3 7-3-2015 4 15-8-2015
Thanks for the up to date content. https://salocalbusiness.com/business/pinupca-2305-chem-rockland-montr%C3%A9al-qc
Muchas gracias, muy didactica la clase.
Buenisima tu explicacion! muy clara y simple! los 4 ejemplos son buenisimo! muchas gracias me ayudaste mucho a comprender esto!
Hola, espero te encuentres bien
tengo este código: coalcl <- gather(coalc,year,coal_carbon,-región)
me funciona bien, sin embargo, la variable "year" no me deja convertirla porque no encuentra el objeto. se puede convertir a entero?
como realizar la separacion de : rojo1986 donde la letra sea una columna y la numeracion otro. gracias
Hola, muchas gracias por dedicar tiempo a compartir tu conocimiento, muy útil, incluso para mi que hasta ahora estoy empezando con R 😉
Hola Mauricio, muchas gracias por compartir lo que sabes. Es muy útil y practico lo que enseñas. Muchas gracias!!!!
La transformación con spread no la ejecuta xq me dice que tengo filas duplicadas.
Hola Roger, ¿me puedes dejar un ejemplo del dataset con el que estas trabajando y te produce el error? Así lo miro y te comento que es lo que está sucediendo. Si se te complica compartirlo por acá envíamelo por mail. contacto@mauricioanderson.com
Hola mauricio he querido realizar un spread con cuatro diferentes valores en la columna pero me sale un erro de datos duplicadas , alguna solución o ayuda
Saludos.
Excelente post, me ha servido mucho para entender como puedo ordenar los datos de acuerdo a la logica del negocio. !! Gracias por compartir conocimiento.
Solo una sugerencia. Podrias incluir o crear un nuevo post donde puedas explicar la combinacion de varios de estas funciones en un solo comando?.
Gracias. Saludos.
Hola Ivan, Gracias!! Me alegro que te haya resultado de utilidad.
Sobre tu sugerencia, la tendré en cuenta para hacer un post especialmente de ese tema.
Saludos