Corso
Nota della redazione: Jean-Nicholas Hould è un data scientist presso Intel Security a Montreal e sul suo blog insegna come iniziare con la data science.
L’analisi esplorativa dei dati (EDA) è un approccio statistico che mira a scoprire e riassumere un dataset. In questa fase del processo di data science, vuoi esplorare la struttura del tuo dataset, le variabili e le loro relazioni.
In questo post ti concentrerai su un aspetto dell’analisi esplorativa dei dati: il data profiling.
Il data profiling consiste nel riassumere il tuo dataset tramite statistiche descrittive. Vuoi usare una pletora di misurazioni per comprendere meglio il tuo dataset. Tipi di dato, valori mancanti, media, mediana e deviazione standard sono solo alcuni dei molti elementi che dovrai raccogliere quando fai il profiling di un dataset. L’obiettivo del data profiling è avere una solida comprensione dei dati per poter poi iniziare a interrogarli e visualizzarli in vari modi.
Sapere da dove provengono i tuoi dati
Prima di lanciarti in qualunque EDA, dovresti sapere il più possibile sulla provenienza dei dati che stai analizzando. Devi capire come sono stati raccolti e come sono stati elaborati. Ci sono trasformazioni pregresse sui dati che potrebbero influenzare la tua analisi?
Dovresti essere in grado di rispondere a queste domande sul tuo dataset:
- Come è stato raccolto?
- È un campione?
- È stato campionato correttamente?
- Il dataset è stato trasformato in qualche modo?
- Ci sono problemi noti sul dataset?
Se non capisci da dove provengono i dati, sarà difficile trarre conclusioni significative dal dataset. Inoltre rischi di commettere errori di analisi molto importanti.
Inoltre, dovresti assicurarti che il dataset sia strutturato in modo standardizzato. Il formato consigliato è la terza forma normale, anche chiamata tidy data. Un dataset “tidy” ha le seguenti caratteristiche:
- Ogni variabile costituisce una colonna e contiene valori
- Ogni osservazione costituisce una riga
- Ogni tipo di unità osservazionale costituisce una tabella
Rispettare questo formato standardizzato velocizzerà la tua analisi, poiché è compatibile con molti strumenti e librerie.
Data Profiling
In questo post userai un dataset di birre artigianali dal sito CraftCans. Questo dataset contiene solo dati su birre in lattina provenienti da birrifici degli Stati Uniti. Non è chiaro dal sito se questo dataset riporti ogni singola birra in lattina prodotta negli USA o meno. Per sicurezza, considererai questo dataset come un campione che può contenere bias.
Ecco la struttura dei dataset che userai:
Beers:
ID: Identificatore univoco della birra.Name: Nome della birra.ABV: Gradazione alcolica per volume della birra.IBU: International Bittering Units della birra.Style: Stile della birra.Ounces: Once di birra.
Breweries:
ID: Identificatore univoco del birrificio.Name: Nome del birrificio.City: Città in cui si trova il birrificio.State: Stato in cui si trova il birrificio.
Tipi di dato
Il primo passo è capire la composizione del tuo dataset.
Con quali variabili hai a che fare?
In genere puoi classificare i dati in una di queste categorie:
- Numerici
- Categoriali
- Testo
- Data
Per prima cosa importerai i dataset che puoi trovare in questo repository con la funzione from_csv di pandas. Inoltre unirai i dataset beers e breweries per facilitare l’analisi più avanti.
import pandas as pd
beers = pd.DataFrame.from_csv("https://raw.githubusercontent.com/nickhould/craft-beers-dataset/master/data/craftcans_beers.csv")
breweries = pd.DataFrame.from_csv("https://raw.githubusercontent.com/nickhould/craft-beers-dataset/master/data/craftcans_breweries.csv")
beers_and_breweries = pd.merge(beers,
breweries,
how='inner',
left_on="brewery_id",
right_on="id",
sort=True,
suffixes=('_beer', '_brewery'))
Con la libreria pandas puoi eseguire la funzione dtypes per elencare ogni colonna e i rispettivi tipi di dato.
beers.dtypes
Che ti restituisce il seguente risultato:
abv float64
ibu float64
id int64
name object
style object
brewery_id int64
ounces float64
dtype: object
Come puoi vedere sopra, questa funzione non raggruppa in modo pulito i diversi tipi di dato. I vari tipi numerici (float64 e int64) non sono raggruppati in un’unica categoria come vorremmo. Inoltre, alcune colonne sono indicate come object, il che non è molto utile.
Per aggirare questo problema, puoi costruire una tua funzione che determini la categoria di ciascuna colonna in un DataFrame.
def get_var_category(series):
unique_count = series.nunique(dropna=False)
total_count = len(series)
if pd.api.types.is_numeric_dtype(series):
return 'Numerical'
elif pd.api.types.is_datetime64_dtype(series):
return 'Date'
elif unique_count==total_count:
return 'Text (Unique)'
else:
return 'Categorical'
def print_categories(df):
for column_name in df.columns:
print(column_name, ": ", get_var_category(df[column_name]))
Variabili di Beers
print_categories(beers)
Il comando sopra ti restituisce il seguente risultato:
abv : Numerical
ibu : Numerical
id : Numerical
name : Categorical
style : Categorical
brewery_id : Numerical
ounces : Numerical
Variabili di Breweries
print_categories(breweries)
Che ti restituisce il seguente risultato:
name : Categorical
city : Categorical
state : Categorical
id : Numerical
Con queste informazioni puoi già comprendere meglio il nostro dataset. Sai che stai trattando solo dati categoriali e numerici. Le variabili numeriche possono essere usate per estrarre molte misurazioni diverse come media, deviazione standard, ecc. Le variabili categoriali sono in genere un modo interessante per segmentare e raggruppare i dati. Per esempio, potresti voler capire come l’IBU differisce tra i vari stili di birra.
Statistiche descrittive
In questa sezione passerai in rassegna varie statistiche descrittive che possono essere usate per comprendere meglio i nostri dati. Noterai che ciascuna di queste misure, presa isolatamente, non è molto utile. È dalla combinazione di misure diverse che puoi estrarre il maggior valore.
Ti concentrerai sulla variabile IBU perché è una variabile numerica. Questo tipo di variabile offre una gamma più ampia di misurazioni rispetto a quelle categoriali. Puoi comunque calcolare misure anche sulle variabili categoriali, ma sarai molto più limitato.
Lunghezza
La funzione len conta il numero di osservazioni in una Series. La funzione conterà tutte le osservazioni, indipendentemente dalla presenza di valori mancanti o nulli.
length = len(beers["ibu"])
print(length)
In questa Series abbiamo un totale di 2410 osservazioni.
Conteggio
La funzione count restituisce il numero di osservazioni non-NA/non-nulle in una Series.
count = beers["ibu"].count()
print(count)
Come puoi vedere, hai 1405 osservazioni non nulle nella Series.
Valori mancanti
Con la Length e il Count ora possiamo calcolare il numero di valori mancanti. Il numero di valori mancanti è la differenza tra Length e Count.
number_of_missing_values = length - count
pct_of_missing_values = float(number_of_missing_values / length)
pct_of_missing_values = "{0:.1f}%".format(pct_of_missing_values*100)
print(pct_of_missing_values)
Per ottenere i valori mancanti in percentuale, dividi semplicemente il numero di valori mancanti per il numero totale di osservazioni, cioè la lunghezza. La funzione float è usata per assicurarsi che i decimali vengano catturati nella divisione. La funzione format è usata per formattare in modo ordinato il numero come percentuale.
In questo caso, mancano quasi il 42% dei valori della variabile IBU. Questo è importante da sapere perché influenzerà la tua analisi. La maggior parte delle statistiche descrittive ignorerà questi valori mancanti e questo causerà certamente un bias.
Valore minimo/massimo
Il valore minimo e massimo di un dataset si possono ottenere facilmente con le funzioni min e max su una Series.
print("Minimum value: ", beers["ibu"].min())
print("Maximum value: ", beers["ibu"].max())
I valori min/max sono utili per capire l’intervallo di valori di una variabile. In questo caso, l’IBU va da 4 a 138.
Moda
La moda è il valore più frequente in un dataset. Si può ottenere usando la funzione mode su una Series.
print(beers["ibu"].mode())
In una distribuzione normale, la moda è uguale a mean e median.
In questo caso, la moda della variabile IBU è 20. È l’IBU più frequente nel nostro dataset.
Media
La media è una misura di tendenza centrale. Rappresenta la somma dei valori divisa per il numero di osservazioni non mancanti.
Si può ottenere con la funzione mean su una Series.
mean = beers["ibu"].mean()
La media è suscettibile all’influenza degli outlier. Alcuni valori estremi possono modificarla notevolmente, trascinandola verso l’alto o verso il basso.
Mediana
La mediana è anch’essa una misura di tendenza centrale. È il numero esattamente al centro di un elenco ordinato di valori numerici.
median = beers["ibu"].median()
Nel caso di distribuzioni asimmetriche, la mediana è una misura di tendenza centrale molto migliore della media.
Nel caso della distribuzione dell’IBU, media e mediana sono dello stesso ordine di grandezza.
Deviazione standard
La deviazione standard è una misura di dispersione. Un’elevata deviazione standard indica che i punti dati sono distribuiti su un ampio intervallo di valori. La deviazione standard è espressa nella stessa unità dei valori.
standarddev = beers["ibu"].std()
In questo caso, la deviazione standard è ~26. Precisamente 25.954065911259324. Se la distribuzione dell’IBU fosse normale, sapresti che ~68% delle osservazioni è entro una deviazione standard dalla media.
Statistiche di quantile
I quantili sono punti di taglio che dividono una distribuzione in parti di uguale dimensione. Molti quantili hanno un proprio nome. Se dividi una distribuzione in quattro gruppi uguali, il quantile creato si chiama quartile. Puoi creare facilmente quantili usando la funzione quantile su una Series. Puoi passare a quella funzione un array con i diversi quantili da calcolare. Nel caso sotto, vogliamo dividere la nostra distribuzione in quattro gruppi uguali.
quantile = beers["ibu"].quantile([.25, .5, .75])
| 0.25 | 21.0 |
| 0.50 | 35.0 |
| 0.75 | 64.0 |
| Name: ibu, | dtype: float64 |
Come puoi vedere sopra, il quantile al 50% è uguale alla mediana. È il valore che divide il dataset a metà. Puoi anche notare che il 75% delle osservazioni è minore o uguale a 64 IBU. Inoltre, il 50% della distribuzione si trova tra 21 e 64 IBU. È importante notare che i valori mancanti non sono considerati in queste metriche.
Grafici di distribuzione
Le visualizzazioni sono molto utili nell’analisi esplorativa dei dati. In questo post non entreremo nel dettaglio delle visualizzazioni. Tuttavia, non si può parlare di data profiling senza menzionare l’importanza di un grafico di distribuzione delle frequenze. È una delle visualizzazioni più semplici ma anche più potenti. Mostra la frequenza di ciascun valore nel nostro dataset.
Per creare questa visualizzazione usiamo la libreria seaborn`` con ladisplotfunction. Questa funzione si aspetta unaSeries` senza valori mancanti.
import seaborn as sns
sns.set(color_codes=True)
sns.set_palette(sns.color_palette("muted"))
sns.distplot(beers["ibu"].dropna());

In questo grafico di distribuzione puoi vedere chiaramente alcuni dei valori calcolati in precedenza. Il valore minimo è vicino a 0 IBU e il valore massimo è vicino a 140 IBU. Vedi chiaramente che il valore più frequente è vicino a 20 IBU. In aggiunta a queste informazioni, ora vediamo un picco vicino a 60 IBU.
Perché ci sono due picchi in questa distribuzione?
Cosa può spiegarlo? Questo è un aspetto che possiamo esplorare nella seconda fase dell’analisi esplorativa dei dati.
Correlazioni
Le correlazioni sono un ottimo modo per scoprire relazioni tra variabili numeriche. Esistono vari modi per calcolare la correlazione. Il coefficiente di correlazione di Pearson è un approccio ampiamente usato che misura la dipendenza lineare tra due variabili. Il coefficiente di correlazione varia da -1 a 1. Una correlazione di 1 è una correlazione positiva totale, -1 è una correlazione negativa totale e 0 indica assenza di correlazione lineare. Possiamo eseguire quel calcolo usando la funzione corr su una Series. Per impostazione predefinita, questa funzione usa il coefficiente di Pearson. È possibile usare metodi di calcolo diversi con questa funzione.
beers[["abv", "ibu", "ounces"]].corr()
| abv | ibu | ounces | |
| abv | 1 | 0.670621 | 0.172529 |
| ibu | 0.670621 | 1 | 0.054691 |
| ounces | 0.172529 | 0.054691 | 1 |
Come puoi vedere sopra, la correlazione tra l’IBU e se stessa è 1. Ovviamente, le variabili numeriche sono perfettamente correlate con se stesse. Più interessante, puoi vedere che la correlazione tra ABV e IBU è pari a 0.670621. Pur non essendo una correlazione positiva totale, è comunque elevata. Questo è un aspetto interessante che possiamo esplorare più avanti.
Qualche nota sulle variabili non numeriche
Le metriche estratte in precedenza sono applicabili principalmente ai valori numerici. Se stai lavorando con altri tipi di dati, come quelli categoriali, puoi comunque ricavare alcune misure interessanti. Potresti calcolare la frequenza di ciascun valore nel dataset.
I DataFrame hanno una funzione chiamata describe che riassume il dataset. Se il tuo DataFrame contiene solo valori categoriali o di testo, il riepilogo sarà adattato specificamente a questo tipo di dati.
beers[["name", "style"]].describe()
| name | style | |
| count | 2410 | 2405 |
| unique | 2305 | 99 |
| top | Nonstop Hef Hop | American IPA |
| freq | 12 | 424 |
Librerie per il profiling
Come hai visto sopra, raccogliere statistiche descrittive può essere un processo noioso. Per fortuna esistono librerie che fanno tutto il lavoro pesante per te. Producono un profilo molto chiaro dei tuoi dati. pandas-profiling è una di queste. Questa libreria offre un profiling statistico pronto all’uso del tuo dataset. Poiché il dataset che stiamo usando è tidy e standardizzato, possiamo usare la libreria subito sul nostro dataset.
import pandas_profiling
pandas_profiling.ProfileReport(beers_and_breweries)
Altre domande
In genere, una volta profilato il tuo dataset, hai molte più domande rispetto all’inizio. Questo è ottimo perché queste nuove domande alimenteranno la tua analisi esplorativa dei dati.
Ecco alcune domande che abbiamo raccolto durante questo profiling:
- Il 41,7% dei valori di
IBUè mancante. Perché? Come può influenzare la nostra analisi? - Ci sono due picchi nella distribuzione dell’
IBU. Cosa lo spiega? - Cosa spiega la correlazione tra
IBUeABV? Qual è l’influenza dello stile di birra in questa correlazione? - Ci sono differenze in
IBU,ABVoStyletra regioni geografiche? E tra East Coast e West Coast?
Il data profiling non è un processo lineare. Man mano che filtri e segmenti il tuo dataset, tornerai a raccogliere statistiche descrittive su sottogruppi dei tuoi dati.
Prossimi passi
In questo post hai visto come profilare un dataset. Ora sai come attribuire le variabili a specifici gruppi di tipi di dato. Hai anche calcolato diverse statistiche descrittive sul dataset e capisci come interpretare queste misure. Infine, hai visto che esistono librerie che possono aiutarti nel lavoro di calcolo per profilare il tuo dataset. Ancora più importante, hai generato più domande che mai per alimentare la tua analisi esplorativa dei dati.
Dai un’occhiata al Tutorial di Analisi Esplorativa dei Dati in Python di DataCamp.

