janitor: un package per la fase di pulizia ed esplorazione dei dati
Il package janitor è un package di servizio che contiene alcune funzioni che si rivelano molto utili nella fase di pulizia ed esplorazione dei dati. Il package contiene diverse funzioni:
anche se in questa pagina facciamo riferimento solo alle funzioni clean_names, per uniformare i nomi delle colonne di una tabella, e tabyl, per tabulare i dati.
tabyl: una funzione avanzata per il conteggio
L’operazione di conteggio è la più semplice e comune operazione in qualunque analisi di dati. La funzione di base per il conteggio, table(), lascia però molto a desiderare per i seguenti aspetti:
non accetta in input oggetti di tipo data.frame o tibble, per cui diventa complesso sfruttare la sintesi basata sugli operatori pipe (|> oppure %>%)
non restituisce in output oggetti di tipo data.frame ma una classe particolare di oggetti table
i risultati in output sono difficili da formattare, questo ancor di più se si comparano con le opzioni di formattazione di una tabella pivot di Microsoft Excel.
tabyl() è un approccio per la tabulazione di variabili che cerca di rispondere a questi limiti. E’ contenuto nel package janitor: l’operazione di conteggio è una parte fondamentale della fase di pulizia ed esplorazione dei dati.
tabyl() è una funzione che risponde ai principi di tidyverse (prende una tabella in input e restituisce una tabella in output) ed è basata sui package dplyr e tidyr.
Dettagli tecnici
tabyl() produce apparentemente tabelle di frequenza ad 1, 2 o 3 variabili. In realtà dietro le quinte tabyl() inserisce una copia di questi conteggi come attributo al data.frame risultante. In altre parole, il risultato di tabyl sembra un data.frame di conteggi, ma è un oggetto di tipo tabyl che contiene queste informazioni come metadati, e questo consente di sfruttare le funzioni di tipo adorn_ (ornamento) per inserire informazioni aggiuntive ed ottenere una migliore formattazione della tabella di output.
Sebbene le funzioni adorn_ siano state progettate per lavorare su oggetti di tipo tabyl, sono disponibili anche versioni che lavorano allo stesso modo su oggetti di tipo data.frame.
Alcuni esempi
Per mostrare tabyl (e clean_names) in azione sfruttiamo la tabella titanic disponibile in formato csvsu questo link
# carico i package utilizzati ---------------------------------------------library(readxl) # per importare il file MS-Excellibrary(tidyverse) # per i verbi di pulizia e manipolazione dei dati {dplyr}library(janitor) # per sfruttare clean_names e tabyl# carico e pulisco i dati -------------------------------------------------# importo il foglio titanic dalla cartella MS-Exceltitanic <-read_excel(paste0(here::here(), "/data/", "titanic-excel.xlsx"), sheet ="titanic")# nomi delle colonne (variabili) della tabella datinames(titanic)
# ricodifico i dati usando descrittori testuali e rinomino la colonna pclasstitanic <- titanic |>mutate(survived =recode(survived, "1"="Sì", "0 "="No"),pclass =recode(pclass, "1"="prima", "2"="seconda", "3"="terza"),embarked =recode(pclass, "S"="Southampthon", "Q"="Queentown", "C"="Cherbourg")) |>rename(class = pclass)
Tabelle di frequenza univariata (one-way tabyl)
La tabulazione di una singola variabile è l’operazione più semplice che si può effettuare con la funzione tabyl:
# tabella di frequenza della variabile classtabyl(titanic, class)
class n percent
prima 216 0.2424242
seconda 184 0.2065095
terza 491 0.5510662
# assegno la tabella di frequenza ad un oggetto sul workspacet1 <- titanic |>tabyl(class)t1
class n percent
prima 216 0.2424242
seconda 184 0.2065095
terza 491 0.5510662
# si tratta di un particolare data.frameclass(t1)
[1] "tabyl" "data.frame"
# su cui sono definiti alcuni attributi utili alla successiva manipolazioneattributes(t1)
$names
[1] "class" "n" "percent"
$class
[1] "tabyl" "data.frame"
$row.names
[1] 1 2 3
$core
class n percent
1 prima 216 0.2424242
2 seconda 184 0.2065095
3 terza 491 0.5510662
$tabyl_type
[1] "one_way"
In caso di valori NA, tabyl() visualizza anche le percentuali “valide”, vale a dire le percentuali calcolate sui solo valori validi. Come accennato in precedenza, anche se tabyl() è progettato in maniera da considerare in input una tabella data (data.frame o tibble) e i corrispondenti nomi delle colonne da tabulare, è possibile anche passare come input un vettore per ottenere la corrispondenza tabella di frequenza univariata:
# creo un semplice vettore con un valore mancantex <-c("basso", "basso", "alto", "alto", "alto", NA)# e ne calcolo la tabella di frequenzatabyl(x)
x n percent valid_percent
alto 3 0.5000000 0.6
basso 2 0.3333333 0.4
<NA> 1 0.1666667 NA
# tabella di frequenza scegliendo di non visualizzare i valori mancanti# che comunque non sono considerati nel calcolo delle percentualitabyl(x, show_na =FALSE)
x n percent
alto 3 0.6
basso 2 0.4
Le funzioni ausiliare adorn_ sono progettate per tabyl a due vie, ma alcune possono essere utilizzate anche su tabelle univariate, ed in particolare adorn_totals() e adorn_pct_formatting():
# aggiungo una riga con i totalit1 |>adorn_totals()
class n percent
prima 216 0.2424242
seconda 184 0.2065095
terza 491 0.5510662
Total 891 1.0000000
# l'argomento per gestire come calcolare i totali è where# per default calcola i totali di colonna (aggiunge cioè una riga)t1 |>adorn_totals(where ="row")
class n percent
prima 216 0.2424242
seconda 184 0.2065095
terza 491 0.5510662
Total 891 1.0000000
# oltre ai totali formatto adeguatamente la colonna delle percentualit1 |>adorn_totals(where ="row") |>adorn_pct_formatting()
class n percent
prima 216 24.2%
seconda 184 20.7%
terza 491 55.1%
Total 891 100.0%
Tabelle di frequenza bivariate (two-way tabyl)
Le tabelle a doppia entrata, dette anche crosstab o tabelle di contingenza, si ottengono passando a tabyl un data.frame e le due colonne su cui effettuare la tabulazione:
# calcolo una tabella a doppia entratat2 <- titanic %>%tabyl(class, survived)# e la stampo a videot2
class No Sì
prima 80 136
seconda 97 87
terza 372 119
Dettaglio avanzato
Si ottiene lo stesso risultato combinando le funzioni dplyr::count() e tidyr::pivot_wider().
Per dettagli vedi le lezioni successive.
Poichè si tratta di un oggetto di tipo tabyl è possibile sfruttare le funzioni ausiliarie per arricchirlo in termini informativi:
# se utilizzo il valore di default della funzione adorn_totals # viene aggiunta una nuova riga con i totali di colonnat2 |>adorn_totals()
class No Sì
prima 80 136
seconda 97 87
terza 372 119
Total 549 342
# posso aggiungere una nuova colonna con i totali di rigat2 |>adorn_totals(where ="col")
class No Sì Total
prima 80 136 216
seconda 97 87 184
terza 372 119 491
# così come posso aggiungere i totali su entrambe le dimensionit2 |>adorn_totals(where =c("row", "col"))
class No Sì Total
prima 80 136 216
seconda 97 87 184
terza 372 119 491
Total 549 342 891
# posso calcolare le frequenze percentuali in base al gran totalet2 |>adorn_totals(where =c("row", "col")) |>adorn_percentages(denominator ="all")
class No Sì Total
prima 0.08978676 0.1526375 0.2424242
seconda 0.10886644 0.0976431 0.2065095
terza 0.41750842 0.1335578 0.5510662
Total 0.61616162 0.3838384 1.0000000
# posso calcolare le percentuali utilizzando come denominatori i totali di # riga: distribuzioni percentuali della variabile in colonna condizionate# sui vari livelli della variabile in riga (profili riga)t2 |>adorn_totals(where =c("row", "col")) |>adorn_percentages(denominator ="row")
class No Sì Total
prima 0.3703704 0.6296296 1
seconda 0.5271739 0.4728261 1
terza 0.7576375 0.2423625 1
Total 0.6161616 0.3838384 1
# oppure le percentuali utilizzando come denominatori i totali di colonna:# distribuzioni percentuali della variabile in riga condizionate# sui vari livelli della variabile in colonna (profili colonna)t2 |>adorn_totals(where =c("row", "col")) |>adorn_percentages(denominator ="col")
class No Sì Total
prima 0.1457195 0.3976608 0.2424242
seconda 0.1766849 0.2543860 0.2065095
terza 0.6775956 0.3479532 0.5510662
Total 1.0000000 1.0000000 1.0000000
# una volta colcolate le frequenze percentuali di interesse posso procedere# alla formattazionet2 |>adorn_totals(where =c("row", "col")) |>adorn_percentages(denominator ="row") |>adorn_pct_formatting(digits =2)
class No Sì Total
prima 37.04% 62.96% 100.00%
seconda 52.72% 47.28% 100.00%
terza 75.76% 24.24% 100.00%
Total 61.62% 38.38% 100.00%
# e posso arricchire la tabella aggiungendo in ogni cella anche le# le corrispondenti frequenze assolutet2 |>adorn_totals(where =c("row", "col")) |>adorn_percentages("row") |>adorn_pct_formatting(digits =2) |>adorn_ns()
class No Sì Total
prima 37.04% (80) 62.96% (136) 100.00% (216)
seconda 52.72% (97) 47.28% (87) 100.00% (184)
terza 75.76% (372) 24.24% (119) 100.00% (491)
Total 61.62% (549) 38.38% (342) 100.00% (891)
Le funzioni di ornamento hanno una serie di opzioni che permettono di controllare gli assi, le funzioni di arrotondamento ed altre scelte relative alla formattazione (vedi sotto per dettagli in merito).
Tabelle di frequenza trivariate (lista di two-way tabyl)
Così come la funzione table() lavora su t La funzione tabyl(), allo stesso modo della funzione table(), può lavorare anche con una terza variabile in input. In questo caso tabyl() restituisce una lista di tabyl:
# un esempio di tabella a tre viet3 <- titanic |>tabyl(class, survived, sex)# il risultato è una tabella doppia divisa in una lista sui # livelli della terza variabilet3
$female
class No Sì
prima 3 91
seconda 6 70
terza 72 72
$male
class No Sì
prima 77 45
seconda 91 17
terza 300 47
# si tratta di un oggetto di tipo listaclass(t3)
[1] "list"
# i cui elementi sono due tabyl bivariaticlass(t3$female)
[1] "tabyl" "data.frame"
class(t3$male)
[1] "tabyl" "data.frame"
Nel caso in cui le funzioni ausiliarie adorn_ sono chiamate su una lista di data.frame, come nel caso appunto di una tabyl a tre vie, sfruttando la funzione purrr::map() lavorano automaticamente come se fossero applicate (mapping) a ciacun data.frame che compone la lista:
t3 |>adorn_totals("row") |>adorn_percentages("all") |>adorn_pct_formatting(digits =1) |>adorn_ns() |>adorn_title()
$female
survived
class No Sì
prima 1.0% (3) 29.0% (91)
seconda 1.9% (6) 22.3% (70)
terza 22.9% (72) 22.9% (72)
Total 25.8% (81) 74.2% (233)
$male
survived
class No Sì
prima 13.3% (77) 7.8% (45)
seconda 15.8% (91) 2.9% (17)
terza 52.0% (300) 8.1% (47)
Total 81.1% (468) 18.9% (109)
Questa “mappatura” (mapping) automatica permette all’utente di riutilizzare il codice relativo alle funzioni di ornamento anche se cambia le variabili di cui richiede la tabulazione. Se ad esempio effettua la tabulazione di tre variabili tab_dati |> tabyl(var1, var2, var3) e a questa tabella aggiunge le chiamate delle funzioni adorn_, se modifica le variabili in tabulazione cambiando la parte iniziale del comando, ad esempio tab_dati |> tabyl(var1, var2, var4), non deve riscrivere le successive chiamate delle funzioni adorn_, che sfruttando la funzione map() si adattano automaticamente alla nuova tabella.
Nota tecnica (avanzata)
E’ possibile sfruttare anche la sintassi esplicita richiamando map() sulla lista di data.frame, map(t3, adorn_percentages), oppure utilizzare la funzione di base lapply(), lapply(t3, adorn_percentages).
Per dettagli sulle funzioni lapply e map() e sul package purr() si rimanda ad una lezione successiva.
Ulteriori dettagli tecnici su tabyl
Se utilizzato su una variabile di tipo factor, tabyl mostra tra i risultati anche i “missing levels” (livelli non presenti nei dati). Questa opzione di default può però essere cambiata:
# creo una variabile factor con tre livelli ma con soli due valori presentivar_factor <-factor(c("alto", "basso", "alto", "basso", "alto"), levels =c("basso", "medio", "alto"))# per default nella tabella in output vengono mostrati tutti i livelli del factorvar_factor |>tabyl()
var_factor n percent
basso 2 0.4
medio 0 0.0
alto 3 0.6
# posso però nascondere i livelli non presenti nei dativar_factor |>tabyl(show_missing_levels =FALSE)
var_factor n percent
basso 2 0.4
alto 3 0.6
lo stesso discorso vale per gli NA, che possono essere gestisti sfruttando l’argomento show_na
la funzione di stampa per tabyls non visualizza i numeri di riga
è possibile sfruttare le funzioni chisq.test() e fisher.test() su un oggetto tabyl a due vie per eseguire i corrispondenti test statistici, così come per un tradizionale oggetto table()
Le funzioni adorn_*
These modular functions build on a tabyl to approximate the functionality of a PivotTable in Microsoft Excel. They print elegant results for interactive analysis or for sharing in a report, e.g., with knitr::kable(). For example:
Le funzioni di ornamento sono funzioni modulari che sono progettate per ottenere funzionalità simili a quelle di una tabella pivot in Microsoft Excel. E’ naturalmente possibile sfruttare le funzioni di formattazione di una tabella per stamparla in un report in formato conveniente. Ecco un esempio sfruttando la funzione knitr::kable():
adorn_totals(): per aggiungere totali di riga, colonna o su entrambe le dimensioni
adorn_percentages(): per calcolare le percentuali lungo ciascuna dimensione o sull’intera tabella (gran totale)
adorn_pct_formatting(): per formattare le colonne contenenti le percentuali, controllando sia il numero di cifre decimali che la posizione del simbolo %
adorn_rounding(): per arrotondare un data.frame di numeri (di solito il risultato di adorn_percentages), utilizzando la funzione base R round() o utilizzando round_half_up() di janitor per arrotondare per eccesso tutti i valori con decimale 0.5, così come succede in MS-Excel
round(10.5) in R base restituisce 10, mentre round_half_up(10.5) restituisce 11.
adorn_rounding() restituisce colonne di classe numeric, che sono quindi utilizzabili ai fini di rappresentazioni grafiche e successive manipolazioni. Si tratta di un’alternativa meno aggressiva della funzione adorn_pct_formatting(). Queste due funzioni non dovrebbero pertanto essere utilizate insieme in una pipeline di codice.
adorn_ns(): per aggiungere i conteggi (frequenze assolute) ad una tabella. Questi possono essere ricavati dai conteggi sottostanti all’oggetto tabyl, che sono allegati al tabyl come metadati, oppure possono essere forniti in input dall’utente.
adorn_title(): per aggiungere un titolo ad un tabyl (o ad un altro tipo di data.frame). Le opzioni disponibili includono l’inserimento del nome di colonna in una nuova riga in testa al data.frame o combinando i nomi di riga e colonna nella prima cella del data.frame.
NOTA: queste funzioni di ornamento dovrebbero essere chiamate in ordine logico (ad esempio aggiungendo prima i totali e poi calcolando le percentuali) per ottenere risultati coerenti.