Maneig de dades amb R (II):
the tidyverse way

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

Data munging: the tidyverse way



Vam aprendre a carregar dades ia fer-les TIDY, però és rar que les dades estiguin preparades per començar la nostra anàlisi, així que cal “arreglar/netejar” les dades

Arreglant les dades

  • Aprendrem a netejar i transformar dades en R. Prioritzarem la nova forma de fer les coses a R (o workflow) conegut com tidyverse.


  • El processat/neteja de les dades sol ocupar un 80% del temps d‟una anàlisi de dades; així que el workflow seria més aviat així:

DPLYR


  • el paquet dplyr és el paquet més important per manipular dades.

dplyr

  • dplyr és un paquet que permet manipular dades de forma intuïtiva


6-7 funcions (o verbs) principals

  • Cada funció fa “una sola cosa”, així que per fer transformacions complexes cal anar concatenant instruccions senzilles amb l’operador pipe (%>%)

sintaxi de dplyr

Totes les funcions tenen una estructura o comportament similar:

  • el primer argument sempre és un df

  • els arguments següents descriuen que fer amb les dades.

  • el resultat és sempre un nou df


Les següents 3 expressions fan exactament el mateix:

filter(df, X1 >= 10)

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

df %>% filter(X1 >= 10)

principals funcions de dplyr

  • select() : selecciona columnes
  • filter() : filtra/selecciona files (que compleixen una o diverses condicions)


  • mutate() :crea noves columnes (variables)
  • arrange(): reordena les files
  • rename() : canvia els noms de les columnes


  • summarise() : resumeix (col·lapsa) els valors d’una columna a un sol valor. Per exemple, calcula la mitjana, màxim, mínim, etc….

hi ha una setena funció (que dóna molta potència a dplyr)

  • group_by() : “agrupa” files (en funció d’una o diverses condicions)

Aprendrem a utilitzar dplyr amb exemples



  • Farem servir les dades del paquet gapminder

treballarem amb dades del pkg gapminder

  • Suposo que ja sabeu que fa el codi següent?

    • És clar que sí: fem accessibles en memòria de R el fitxer de dades gapminder que està al paquet gapminder
gapminder <- gapminder::gapminder   

select() s’utilitza per seleccionar variables

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


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

De vegades volem reordenar les columnes/variables

  • Podem fer-ho relocate()
aa <- gapminder %>% dplyr::relocate(country, .after = lifeExp)

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

De vegades volem reanomenar les columnes

  • Podem fer-ho amb rename()
gapminder %>% rename(life_exp = lifeExp)



la funció names() de R-base és útil

aa <- gapminder

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

De vegades volem CREAR noves columnes

  • Podem fer-ho amb mutate()

  • Per exemple, podem crear la variable: GDP = pop * gdpperCap

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


Extensió:

Per defecte, la nova variable creada se situarà al final del df, tret que usem els arguments .after i .before

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

filter(): permet seleccionar files

files que compleixen unes determinades condicions o criteris lògics

gapminder <- gapminder::gapminder  #- cargamos los datos

#- 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)  
aa <- gapminder %>% filter(between(lifeExp, 29, 32))       

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

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

slice() és una variant de filter()

permet seleccionar files però per posició


  • filter() y slice() ambdues seleccionen files, la primera per condicions i la segona per posició:


#- selecciona les observacions de la desena a la quinzena
aa <- gapminder %>% slice(c(10:15)) 

#- selecciona les 4 primeres observacions, de la 41 a la 43, i les 4 últimes 
aa <- gapminder %>%
  slice( c(1:4, 41:43, (n()-3):n() ) ) 

variants de slice()

  • slice_max() i slice_min(): seleccionen files amb valor màxim (o mínim) d’una variable:
#- selecciona les 3 files amb més valor de lifeExp
aa <- gapminder %>% slice_max(lifeExp, n = 3)

#- selecciona les 4 files amb MENOR valor de pop
aa <- gapminder %>% slice_min(pop, n = 4)

#- observacions en el primer décil quant a esperança de vida, 10% amb menys esperança de vida
aa <- gapminder %>% slice_min(lifeExp, prop = 0.1)

#- 1% d'observacions amb més població. Imagino que estaran la Xina i l'Índia
aa <- gapminder %>% slice_max(pop, prop = 0.01)

variants de slice()

  • slice_sample(): permet obtenir una mostra aleatòria de les dades


#- selecciona (aleatòriament) 100 files de les dades
aa <- gapminder %>% slice_sample(n = 100)

#- selecciona (aleatòriament) un 5% de les dades
aa <- gapminder %>% slice_sample(prop = 0.05)

arrange(): permet reordenar les files d’un df


#- ordena les files de MENOR a major segons els valors de la v. lifeExp 
aa <- gapminder %>% arrange(lifeExp)

#- ordena les files de MAJOR a menor segons els valors de la v. lifeExp
aa <- gapminder %>% arrange(desc(lifeExp))  

#- ordena les files de MENOR a major segons els valors de la v. lifeExp. 
#- Si haguessin empats es resol amb la variable "pop"
aa <- gapminder %>% arrange(lifeExp, pop) 

summarize() per “resumir” variables

  • summarize(): agafa una variable com a input i torna un sol valor; per exemple, calcula la mitjana aritmètica (o el mínim, o el màxim…) d’una columna/variable


  • Comencem “resumint” una sola variable:


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

aa <- gapminder %>% summarise(NN = n())           #- el nombre d'observacions

aa <- gapminder %>% summarise(media = mean(lifeExp))  #- la mitjana de la variable "lifeExp"

aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp))  #- us ho explicaran a Estadística

summarize(): “resumim” dues variables


#- retornarà 2 valors: les mitjanes de "lifeExp" i "gdpPercap"
aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))  



summarize(): fem 2 resums d’una variable


#- retornarà 2 valors: la mitjana i màxim de la v. "lifeExp"
aa <- gapminder %>% summarise(mean(lifeExp), max(lifeExp))

group_by(): amb aquesta funció ja es pot veure la potència de dplyr

En anàlisi de dades moltes operacions volem calcular-les per a diferents grups (p. ex. dona/home, diferents països, províncies…). group_by() permet fer-ho

  • group_by() agafa un df i el converteix en un “df agrupat”. En aquest nou “df agrupat”, les operacions que fem, es faran per separat per a cadascun dels grups que hàgim definit. Ara ho veiem
  • Per exemple: volem calcular el nombre d’observacions al df i el nombre d’observacions de cada continent
# calculem el nombre d'observacions al df
aa <- gapminder %>% summarise(NN = n()) 
gt::gt(aa)
NN
1704
# ara volem calcular el nombre d'observacions de cada continent
# agafem df i ho (des)agrupem per grups definits per la variable "continent"
# o sigui, hi haurà 5 grups (5 continents)
# després amb summarise() calcularem el nombre d'observacions a cada grup;
# és a dir, ens retornarà un df amb una fila per cada continent
# amb el nombre d'observacions de cada continent

bb <- gapminder %>% group_by(continent) %>% summarise(NN = n())
gt::gt(bb)
continent NN
Africa 624
Americas 300
Asia 396
Europe 360
Oceania 24

COMBINANT taules (joining df’s)


Fins ara hem treballat amb un únic df, però …

… moltes vegades hem de treballar amb dades que estan a diverses taules


  • així que, de vegades haurem de ajuntar o fusionar taules

joining df’s

  • Hi ha diversos tipus d’unions de taules: un bon recurs per a aquests temes és el capítol dedicat a JOINS a R4DS2ed

  • Les unions més comunes són les “mutating joins”, que afegeixen columnes d’una taula a una altra. Ho veiem:


mutating joins (ajuntant df1 amd df2)

  • Les mutating joins el que fan és afegir les variables de df2 a df1
  • Hi ha 3/4 tipus de mutating joins. Totes afegeixen les columnes de df2 al df1; PERÒ …


  • es diferencien a les files que se seleccionen

  • Les files que se seleccionen depenen del criteri per fer el match. Ho veiem:

3/4 tipus de mutating joins

Recorda que totes elles afegeixen columnes (les columnes de df1 a df2) però que es diferencien a les files que se seleccionen

  • inner_join(df1,df2): retorna les files de df1 que també existeixen a df2
  • left_join(df1,df2): retorna TOTES les files de df1
  • full_join(df1,df2): retorna TOTES les files de df1 i de df2; (ès à dir, retorna TOTES les files i TOTES les columnes de les 2 taules)

mutating joins: un exemple

  • A l’exemple farem servir aquests 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 exemple


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

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

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

Extensions




amb dplyr 1.0.0, el 2020, van aparèixer dues funcions importants: across() i where()

  • across() i where(): aquestes funcions són una mica diferents, només es fan servir en combinació d’una altra funció/verb. Són 2 funcions que a l’argot del tidyverse no són verbs sinó adverbis

Extensió: select() al costat de la funció where() [🌶🌶🌶🌶]

  • select() i where() són dues funcions, sí, però a l’argot del tidyverse, select() és un verb i where() és un adverbi, qualifica/canvia el que fa select().

Exemple dús:

  • A gapminder les 2 primeres variables (country i continent) són factors i les 4 següents són variable numèriques

  • Imagina que volem seleccionar només les variables que són numèriques. Podem fer-ho per nom o per posició però millor amb select() i la funció auxiliar where()

aa <- gapminder %>% select(is.numeric)        #- funciona, però ...

aa <- gapminder %>% select(where(is.numeric)) #- és "preferible" aquesta segona expressió
  • Si volem seleccionar les variables que NO són numèriques faríem:
aa <- gapminder %>% select(!where(is.numeric)) 

summarise() amb across() : permet fer “resums” de moltes variables

  • Usar across() juntament amb summarise() permet calcular estadístics de totes les variables, o de subconjunts d’aquestes, de manera més còmoda:
#- mitjana de cadascuna de les 6 variables.
#- Retorna 2 warnings perquè les 2 primeres són textuals. No es pot calcular la mitjana de  continent y country
gapminder %>% summarise(across(everything(), mean) ) 

#- calculem la mitjana de tercera a la sisena variable
gapminder %>% summarise(across(3:6, mean) ) 


across() i where() amb summarise()

  • Dins de across() es pot utilitzar where() per aplicar criteris lògics a la selecció variables [🌶🌶🌶🌶🌶🌶] 🌟
gapminder %>% summarise(across(where(is.numeric), mean))

Pipe nativa ( |> )

  • El 2021, amb la versió 4.1.0 de R va suposar l’aparició d’una pipe nativa en R: |>

Millores a la pipe nativa

què passarà en el futur?