5.2 Trattamento dei dati con dplyr
Il pacchetto dplyr
include sei funzioni base: filter()
, select()
, mutate()
, arrange()
, group_by()
e summarise()
. Queste sei funzioni costituiscono i verbi del linguaggio di manipolazione dei dati. A questi sei verbi si aggiunge il pipe %>%
che serve a concatenare più operazioni. In particolare, considerando una matrice osservazioni per variabili, select()
e mutate()
si occupano di organizzare le variabili, filter()
e arrange()
i casi, e group_by()
e summarise()
i gruppi.
Per introdurre le funzionalità di dplyr
, utilizzeremo i dati msleep
forniti dal pacchetto ggplot2
. Tali dati descrivono le ore di sonno medie di 83 specie di mammiferi (Savage et al. 2007). Carichiamo il boundle tidyverse
(che contiene ggplot2
) e leggiamo nella memoria di lavoro l’oggetto msleep
:
5.2.1 Operatore pipe
Prima di presentare le funzionalità di dplyr
, introduciamo l’operatore pipe %>%
del pacchetto magrittr
– ma ora presente anche in base R
nella versione |>
. L’operatore pipe, %>%
o |>
, serve a concatenare varie funzioni insieme, in modo da inserire un’operazione dietro l’altra. Una spiegazione intuitiva dell’operatore pipe è stata fornita in un tweet di @andrewheiss
. Consideriamo la seguente istruzione in pseudo-codice R
:
leave_house(
get_dressed(
get_out_of_bed(
wake_up(me, time = "8:00"),
side = "correct"
),
pants = TRUE,
shirt = TRUE
),
car = TRUE,
bike = FALSE
)
Il listato precedente descrive una serie di (pseudo) funzioni concatenate, le quali costituiscono gli argomenti di altre funzioni. Scritto così, il codice è molto difficile da capire. Possiamo però ottenere lo stesso risultato utilizzando l’operatore pipe che facilita enormememnte la leggibilità del codice:
me %>%
wake_up(time = "8:00") %>%
get_out_of_bed(side = "correct") %>%
get_dressed(pants = TRUE, shirt = TRUE) %>%
leave_house(car = TRUE, bike = FALSE)
In questa seconda versione del (pseudo) codice R
si capisce molto meglio ciò che vogliamo fare. Il tibble
me
viene passato alla funzione wake_up()
. La funzione wake_up()
ha come argomento l’ora del giorno: time = "8:00"
. Una volta “svegliati” (wake up) dobbiamo scendere dal letto. Quindi l’output di wake_up()
viene passato alla funzione get_out_of_bed()
la quale ha come argomento side = "correct"
perché vogliamo scendere dal letto dalla parte giusta. E così via.
Questo pseudo-codice chiarisce il significato dell’operatore pipe. L’operatore %>%
è “syntactic sugar” per una serie di chiamate di funzioni concatenate, ovvero, detto in altre parole, consente di definire la relazione tra una serie di funzioni nelle quali il risultato (output) di una funzione viene utilizzato come l’input di una funzione successiva.
5.2.2 Estrarre una singola colonna con pull()
Ritorniamo ora all’esempio precedente. Iniziamo a trasformare il data frame msleep
in un tibble
(che è identico ad un data frame ma viene stampato sulla console in un modo diverso):
Estraiamo da msleep
la variabile sleep_total
usando il verbo pull()
:
msleep %>%
pull(sleep_total)
#> [1] 12.1 17.0 14.4 14.9 4.0 14.4 8.7 7.0 10.1 3.0 5.3 9.4 10.0 12.5 10.3
#> [16] 8.3 9.1 17.4 5.3 18.0 3.9 19.7 2.9 3.1 10.1 10.9 14.9 12.5 9.8 1.9
#> [31] 2.7 6.2 6.3 8.0 9.5 3.3 19.4 10.1 14.2 14.3 12.8 12.5 19.9 14.6 11.0
#> [46] 7.7 14.5 8.4 3.8 9.7 15.8 10.4 13.5 9.4 10.3 11.0 11.5 13.7 3.5 5.6
#> [61] 11.1 18.1 5.4 13.0 8.7 9.6 8.4 11.3 10.6 16.6 13.8 15.9 12.8 9.1 8.6
#> [76] 15.8 4.4 15.6 8.9 5.2 6.3 12.5 9.8
5.2.3 Selezionare più colonne con select()
Se vogliamo selezionare da msleep
un insieme di variabili, ad esempio name
, vore
e sleep_total
, possiamo usare il verbo select()
:
dt <- msleep %>%
dplyr::select(name, vore, sleep_total)
dt
#> # A tibble: 83 × 3
#> name vore sleep_total
#> <chr> <chr> <dbl>
#> 1 Cheetah carni 12.1
#> 2 Owl monkey omni 17
#> 3 Mountain beaver herbi 14.4
#> 4 Greater short-tailed shrew omni 14.9
#> 5 Cow herbi 4
#> 6 Three-toed sloth herbi 14.4
#> 7 Northern fur seal carni 8.7
#> 8 Vesper mouse <NA> 7
#> # ℹ 75 more rows
laddove la sequenza di istruzioni precedenti significa che abbiamo passato msleep
alla funzione select()
contenuta nel pacchetto dplyr
e l’output di select()
è stato salvato (usando l’operatore di assegnazione, <-
) nell’oggetto dt
. Alla funzione select()
abbiamo passato gli argomenti name
, vore
e sleep_total
.
5.2.4 Filtrare le osservazioni (righe) con filter()
Il verbo filter()
consente di selezionare da un tibble
un sottoinsieme di righe (osservazioni). Per esempio, possiamo selezionare tutte le osservazioni nella variabile vore
contrassegnate come carni
(ovvero, tutti i carnivori):
dt %>%
dplyr::filter(vore == "carni")
#> # A tibble: 19 × 3
#> name vore sleep_total
#> <chr> <chr> <dbl>
#> 1 Cheetah carni 12.1
#> 2 Northern fur seal carni 8.7
#> 3 Dog carni 10.1
#> 4 Long-nosed armadillo carni 17.4
#> 5 Domestic cat carni 12.5
#> 6 Pilot whale carni 2.7
#> 7 Gray seal carni 6.2
#> 8 Thick-tailed opposum carni 19.4
#> # ℹ 11 more rows
Per utilizzare il verbo filter()
in modo efficace è neccessario usare gli operatori relazionali (Tabella ??) e gli operatori logici (Tabella ??) di R
. Per un approfondimento, si veda il Capitolo Comparisons di R for Data Science.
5.2.5 Creare una nuova variabile con mutate()
Talvolta vogliamo creare una nuova variabile, per esempio, sommando o dividendo due variabili, oppure calcolandone la media. A questo scopo si usa il verbo mutate()
. Per esempio, se vogliamo esprimere i valori di sleep_total
in minuti, moltiplichiamo per 60:
5.2.6 Ordinare i dati con arrange()
Il verbo arrange()
ordina i dati in base ai valori di una o più variabili. Per esempio, possiamo ordinare la variabile sleep_total
dal valore più alto al più basso in questo modo:
dt %>%
arrange(
desc(sleep_total)
)
#> # A tibble: 83 × 3
#> name vore sleep_total
#> <chr> <chr> <dbl>
#> 1 Little brown bat insecti 19.9
#> 2 Big brown bat insecti 19.7
#> 3 Thick-tailed opposum carni 19.4
#> 4 Giant armadillo insecti 18.1
#> 5 North American Opossum omni 18
#> 6 Long-nosed armadillo carni 17.4
#> 7 Owl monkey omni 17
#> 8 Arctic ground squirrel herbi 16.6
#> # ℹ 75 more rows
5.2.7 Raggruppare i dati con group_by()
Il verbo group_by()
raggruppa insieme i valori in base a una o più variabili. Lo vedremo in uso in seguito insieme a summarise()
.
Nota: con dplyr()
, le operazioni raggruppate vengono iniziate con la funzione group_by()
. È una buona norma utilizzare ungroup()
alla fine di una serie di operazioni raggruppate, altrimenti i raggruppamenti verranno mantenuti nelle analisi successiva, il che non è sempre auspicabile.
5.2.8 Sommario dei dati con summarise()
Il verbo summarise()
collassa il dataset in una singola riga dove viene riportato il risultato della statistica richiesta. Per esempio, la media del tempo totale del sonno è
5.2.9 Operazioni raggruppate
Sopra abbiamo visto come i mammiferi considerati dormano, in media, 10.4 ore al giorno. Troviamo ora il sonno medio in funzione di vore
:
dt %>%
group_by(vore) %>%
summarise(
m_sleep = mean(sleep_total, na.rm = TRUE),
n = n()
)
#> # A tibble: 5 × 3
#> vore m_sleep n
#> <chr> <dbl> <int>
#> 1 carni 10.4 19
#> 2 herbi 9.51 32
#> 3 insecti 14.9 5
#> 4 omni 10.9 20
#> 5 <NA> 10.2 7
Si noti che, nel caso di 7 osservazioni, il valore di vore
non era
specificato. Per tali osservazioni, dunque, la classe di appartenenza è
NA
.
5.2.10 Applicare una funzione su più colonne: across()
È spesso utile eseguire la stessa operazione su più colonne, ma copiare e incollare è sia noioso che soggetto a errori:
In tali circostanze è possibile usare la funzione across()
che consente di riscrivere il codice precedente in modo più succinto:
Per i dati presenti, ad esempio, possiamo avere:
msleep %>%
group_by(vore) %>%
summarise(across(starts_with("sleep"), ~ mean(.x, na.rm = TRUE)))
#> # A tibble: 5 × 4
#> vore sleep_total sleep_rem sleep_cycle
#> <chr> <dbl> <dbl> <dbl>
#> 1 carni 10.4 2.29 0.373
#> 2 herbi 9.51 1.37 0.418
#> 3 insecti 14.9 3.52 0.161
#> 4 omni 10.9 1.96 0.592
#> 5 <NA> 10.2 1.88 0.183