Manejo de datos con R (II):
the tidyverse way

Módulo práctico de ITA
(Grupo M)

Data munging: the tidyverse way



Aprendimos a cargar datos y a hacerlos TIDY, pero es raro que los datos estén preparados para empezar nuestro análisis, así que hay que “arreglar/limpiar” los datos

DPLYR


  • el paquete dplyr es el paquete más importante a la hora de manipular datos: permite hacerlo de forma intuitiva

principales funciones de dplyr

Tiene 6-7 funciones (o verbos) principales

  • Cada función hace “una sola cosa”, así que para realizar transformaciones complejas hay que ir concatenando instrucciones sencillas con el operador pipe (%>%)
  • select() : selecciona columnas
  • filter() : filtra/selecciona filas (que cumplen una o varias condiciones)


  • mutate() : crea nuevas columnas (variables)
  • arrange(): reordena las filas
  • rename() : cambia los nombres de las columnas


  • summarise() : resume (colapsa) los valores de una columna a un solo valor. Por ejemplo, calcula la media, máximo, mínimo, etc…

hay una séptima función (que da mucha potencia a dplyr)

  • group_by() : “agrupa” filas (en función de una o varias condiciones)

sintaxis de dplyr

Todas las funciones tienen una estructura o comportamiento similar:

  • el primer argumento siempre es un df

  • los siguientes argumentos describen que hacer con los datos.

  • el resultado es siempre un nuevo df


Las siguientes 3 expresiones hacen exactamente lo mismo:

filter(df, X1 >= 10)

df %>% filter(. , X1 >= 10)

df %>% filter(X1 >= 10)

Vamos a aprender a usar dplyr con ejemplos


trabajaremos con datos del pkg gapminder

  • ¿Supongo que ya sabéis que hace el siguiente código?

    • Claro que sí: hacemos accesibles en memoria de R el fichero de datos gapminder que está en el paquete gapminder
gapminder <- gapminder::gapminder   

select() se utiliza para seleccionar variables

  • Seleccionar variables por nombre
aa <- gapminder %>% select(year, lifeExp) 
  • Eliminar variables por nombre
aa <- gapminder %>% select(-year, -lifeExp)


  • Seleccionar variables por posición
aa <- gapminder %>% select(1:3, 5)
  • Eliminar variables por posición
aa <- gapminder %>% select(-c(1:3, 5))

A veces queremos renombrar las columnas

  • Podemos hacerlo con rename()
gapminder %>% rename(life_exp = lifeExp)



la función names() de R-base es útil

aa <- gapminder

names(aa)
[1] "country"   "continent" "year"      "lifeExp"   "pop"       "gdpPercap"

A veces queremos CREAR nuevas columnas

  • Podemos hacerlo con mutate()

  • Por ejemplo, podemos crear la variable: GDP = pop * gdpPercap

aa <- gapminder %>% mutate(GDP = pop * gdpPercap)


Extensión:

Por defecto, la nueva variable creada se situará al final del df, a no ser que usemos los argumentos .after y .before

aa <- gapminder %>% mutate(GDP = pop * gdpPercap, .after = pop)

filter(): permite seleccionar filas

filas que cumplan unas determinadas condiciones o criterios lógicos

#- Observaciones de España (country == "Spain")
aa <- gapminder %>% filter(country == "Spain") 

#- filas con valores de "lifeExp" < 29
aa <- gapminder %>% filter(lifeExp < 29)       

#- filas con valores de "lifeExp" entre [29, 32]
aa <- gapminder %>% filter(lifeExp >=  29 &  lifeExp <= 32)

#- observaciones de países de África con lifeExp > 72
aa <- gapminder %>% filter(lifeExp > 72 &  continent == "Africa") 

#- observaciones de países de África o Asia con lifeExp > 72
aa <- gapminder %>% filter(lifeExp > 72 &  continent %in% c("Africa", "Asia") )  
aa <- gapminder %>% filter(lifeExp > 72 & (continent == "Africa" | continent == "Asia") ) 

slice() es una variante de filter()

permite seleccionar filas pero por posición

#- selecciona las observaciones de la décima a la decimoquinta
aa <- gapminder %>% slice(c(10:15)) 

#- QUITA las 5 primeras observaciones (con el signo menos)
aa <- gapminder %>% slice(-c(1:5))


variantes de slice()

  • slice_max() y slice_min(): seleccionan filas con valor máximo (o mínimo) de una variable:
#- selecciona las 3 filas con mayor valor de lifeExp
aa <- gapminder %>% slice_max(lifeExp, n = 3)

#- selecciona las 4 filas con MENOR valor de pop
aa <- gapminder %>% slice_min(pop, n = 4)

arrange(): permite reordenar las filas de un df


#- ordena las filas de MENOR a mayor según los valores de la v. lifeExp 
aa <- gapminder %>% arrange(lifeExp)

#- ordena las filas de MAYOR a menor según los valores de la v. lifeExp
aa <- gapminder %>% arrange(desc(lifeExp))  

#- ordena las filas de MENOR a mayor según los valores de la v. lifeExp. 
#- Si hubiese empates se resuelven con la variable "pop"
aa <- gapminder %>% arrange(lifeExp, pop) 

summarize() para “resumir” variables

  • summarize(): coge una variable como input y devuelve un solo valor; por ejemplo, calcula la media aritmética (o el mínimo, o el máximo …) de una columna/variable


  • Empezamos “resumiendo” una sola variable:


aa <- gapminder %>% summarise(maximo = max(pop))  #- el valor máximo de la variable "pop"

aa <- gapminder %>% summarise(NN = n())           #- el número de observaciones

aa <- gapminder %>% summarise(media = mean(lifeExp))  #- la media de la variable "lifeExp"
  • También podemos hacer varios resúmenes a la vez:
#- retornará 2 valores: las medias de "lifeExp" y "gdpPercap"
aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))  

#- retornará 2 valores: la media y máximo de la v. "lifeExp"
aa <- gapminder %>% summarise(mean(lifeExp), max(lifeExp))

group_by(): con esta función ya se puede ver la potencia de dplyr

En análisis de datos muchas operaciones queremos calcularlas para distintos grupos (p. ej. mujer/hombre, diferentes países, provincias …). group_by() permite hacerlo.

  • group_by() coge un df y lo convierte en un “df agrupado”. En ese nuevo “df agrupado”, las operaciones que hagamos, se harán por separado para cada uno de los grupos que hayamos definido. Ahora lo vemos.
  • Por ejemplo: queremos calcular el nº de observaciones en el df y el nº de observaciones de cada continente
# calculamos el nº de observaciones en el df
aa <- gapminder %>% summarise(NN = n()) 
gt::gt(aa)
NN
1704

Ahora, el nº de observaciones de cada continente:

bb <- gapminder %>% 
1  group_by(continent) %>%
2  summarise(NN = n())
1
Agrupamos por continente: habrá 5 grupos
2
summarise() se calcula para cada grupo: una fila por continente

 

gt::gt(bb)
continent NN
Africa 624
Americas 300
Asia 396
Europe 360
Oceania 24

Extensiones



  • Uniendo tablas

  • Otras funciones de dplyr

  • across() y where()

COMBINANDO tablas (joining df’s)

Hasta ahora hemos trabajado con un único df, pero …

… muchas veces tenemos que trabajar con datos que están en varias tablas


  • así que, a veces tendremos que juntar o fusionar tablas

joining df’s

  • Hay diversos tipos de uniones de tablas: un buen recurso para estos temas es el capítulo dedicado a JOINS en R4DS2ed

  • Las uniones más comunes son las “mutating joins”: todas añaden las columnas de df2 a df1; PERO …

  • … se diferencian en las filas que se seleccionan, que dependen del criterio para hacer el match. Lo vemos:

3 tipos de mutating joins

  • inner_join(df1,df2): retorna las filas de df1 que también existen en df2
  • left_join(df1,df2): retorna TODAS las filas de df1
  • full_join(df1,df2): retorna TODAS las filas de df1 y de df2; (o sea, retorna TODAS las filas y TODAS las columnas de las 2 tablas)

mutating joins: un ejemplo

  • En el ejemplo usaremos estos 2 df’s:
df1 <- tibble(id = 1:3, x = paste0("x", 1:3))
df2 <- tibble(id = 1:4, y = paste0("y", 1:4)) %>% slice(-3)
df1
id x
1 x1
2 x2
3 x3
df2
id y
1 y1
2 y2
4 y4

mutating joins: un ejemplo


#- inner_join() only includes observations that match in df1 and df2
df_inner <- inner_join(df1, df2)

#- full_join() includes all observations from df1 and df2
df_full_join <- full_join(df1, df2)

#- left_join() includes all observations in df1
df_left_join <- left_join(df1, df2)

A veces queremos reordenar las columnas/variables

  • Podemos hacerlo con relocate()
aa <- gapminder %>% dplyr::relocate(country, .after = lifeExp)

aa <- gapminder %>% dplyr::relocate(country, .before = lifeExp)

variantes de slice()

  • slice_sample(): permite obtener una muestra aleatoria de los datos


#- selecciona (aleatoriamente) 100 filas de los datos
aa <- gapminder %>% slice_sample(n = 100)

#- selecciona (aleatoriamente) un 5% de los datos
aa <- gapminder %>% slice_sample(prop = 0.05)

con dplyr 1.0.0, en 2020, aparecieron dos funciones importantes: across() y where()

  • across() y where(): estas funciones son un poco diferentes, solo se usan en combinación de otra función/verbo. Son 2 funciones que en la jerga del tidyverse no son verbos sino adverbios, cualifican/cambian lo que hacen otras funciones como select()

Ejemplo de uso: select() junto a la función where() [🌶🌶🌶🌶]

  • En gapminder las 2 primeras variables (country y continent) son factores y las 4 siguientes son variables numéricas.


  • Imagina que queremos seleccionar sólo las variables numéricas. Podemos hacerlo por nombre o por posición pero mejor hacerlo con select() y where()
aa <- gapminder %>% select(is.numeric)        #- funciona, pero ...

aa <- gapminder %>% select(where(is.numeric)) #- es "preferible" esta segunda expresión


  • Si quisiéramos seleccionar las variables que NO son numéricas haríamos:
aa <- gapminder %>% select(!where(is.numeric)) 

más ejemplos de across() y where()


  • Imagina que queremos calcular la media de todas las variables de gapminder. Tendríamos que repetir código 6 veces … o usar across()
#- media de todas (everything()) las variables. 
#- Devuelve 2 warnings porque las 2 primeras son textuales:
#- no se puede calcular la media de continent y country
gapminder %>% summarise(across(everything(), mean) )


  • Ahora calculamos la media de la tercera a la sexta variable:
gapminder %>% summarise(across(3:6, mean) )

across() y where() con summarise()       😱


  • Dentro de across() se puede utilizar where()

  • Con esto conseguimos poder aplicar criterios lógicos en la selección de variables: [🌶] [ 🌟 ]


Si quisiéramos calcular la media de las variables numéricas

gapminder %>% summarise(across(where(is.numeric), mean))

Resumen


  • Lo que hemos visto del paquete dplyr

Resumen: las funciones de dplyr

  • Para trabajar con columnas: select(), rename() y mutate()

  • Para trabajar con filas: filter(), slice() y arrange()

  • Para resumir: summarise()

  • Para hacer los cálculos por grupos: group_by()

cada función hace una sola cosa: se encadenan con el operador pipe (%>%)