import os
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
import arviz as az
from pathlib import Path
from statsmodels.graphics.mosaicplot import mosaic
18 Esplorare i dati qualitativi
Prerequisiti
- Leggere il capitolo Exploring categorical data di Introduction to Modern Statistics (2e) di Mine Çetinkaya-Rundel e Johanna Hardin.
Concetti e competenze chiave
- Tabelle di contingenza.
- Grafico a barre.
Preparazione del Notebook
= sum(map(ord, "frequency_distributions"))
seed = np.random.default_rng(seed=seed)
rng ="colorblind")
sns.set_theme(palette"arviz-darkgrid")
az.style.use(%config InlineBackend.figure_format = "retina"
Introduzione
In questo capitolo ci concentreremo sull’analisi dei dati qualitativi.
18.1 Il dataset penguins
Per fornire esempi pratici, in questo capitolo utilizzeremo il dataset palmerpenguins, messo a disposizione da Allison Horst. I dati sono stati raccolti e resi disponibili da Dr. Kristen Gorman e dalla Palmer Station, parte del programma di ricerca ecologica a lungo termine Long Term Ecological Research Network. Il dataset contiene informazioni su 344 pinguini, appartenenti a 3 diverse specie, raccolte su 3 isole dell’arcipelago di Palmer, in Antartide. Per semplicità, i dati sono organizzati nel file penguins.csv
.
18.2 Importare i Dati
Supponimo di essere interessati alla distribuzione di una singola variabili quantitativa. Per fare un esempio, considereremo i dati palmerpenguins.
Come illustrato nel capitolo precedente, dopo aver definito project_directory
come la directory di root utilizzando:
# Get the home directory
= os.path.expanduser("~")
home_directory # Construct the path to the Quarto project directory
= os.path.join(home_directory, "_repositories", "psicometria") project_directory
e impostato il percorso del file penguins.csv
in modo relativo rispetto alla root:
= os.path.join(project_directory, "data", "penguins.csv") file_path
possiamo caricare i dati grezzi dal file penguins.csv
in un DataFrame di pandas con il seguente comando:
= pd.read_csv(file_path) dat
Esaminiamo i dati.
dat.dtypes
species object
island object
bill_length_mm float64
bill_depth_mm float64
flipper_length_mm float64
body_mass_g float64
sex object
year int64
dtype: object
dat.shape
(344, 8)
dat.head()
species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year | |
---|---|---|---|---|---|---|---|---|
0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | male | 2007 |
1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | female | 2007 |
2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | female | 2007 |
3 | Adelie | Torgersen | NaN | NaN | NaN | NaN | NaN | 2007 |
4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | female | 2007 |
Per semplicità, rimuoviamo le righe con valori mancanti con la seguente istruzione:
= dat.dropna()
df df.shape
(333, 8)
df.head()
species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year | |
---|---|---|---|---|---|---|---|---|
0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | male | 2007 |
1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | female | 2007 |
2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | female | 2007 |
4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | female | 2007 |
5 | Adelie | Torgersen | 39.3 | 20.6 | 190.0 | 3650.0 | male | 2007 |
18.3 Tabelle di Contingenza
Una tabella di contingenza è uno strumento utilizzato per riassumere i dati di due variabili categoriali, ovvero variabili qualitative che assumono valori all’interno di un insieme finito di categorie. In una tabella di contingenza, ogni cella mostra quante volte si è verificata una combinazione specifica di categorie per le due variabili considerate.
Per esempio, se prendiamo in esame due variabili categoriali come “island” e “species” all’interno di un DataFrame df
, ciascuna delle quali rappresenta rispettivamente l’isola di provenienza e la specie dei pinguini, possiamo costruire una tabella che mostra quante volte ciascuna combinazione di “island” e “species” appare nel nostro campione. In altre parole, la tabella di contingenza ci permette di vedere quante osservazioni ci sono per ogni combinazione di categorie tra queste due variabili.
= pd.crosstab(df["island"], df["species"])
contingency_table
# Mostra la tabella di contingenza
contingency_table
species | Adelie | Chinstrap | Gentoo |
---|---|---|---|
island | |||
Biscoe | 44 | 0 | 119 |
Dream | 55 | 68 | 0 |
Torgersen | 47 | 0 | 0 |
Questa tabella di contingenza mostra la distribuzione di tre specie di pinguini (Adelie, Chinstrap, Gentoo) rispetto a tre isole (Biscoe, Dream, Torgersen). Ogni cella rappresenta il numero di pinguini di una determinata specie presenti su ciascuna isola. Ecco un’interpretazione dettagliata:
- Isola Biscoe: Qui troviamo 44 pinguini della specie Adelie e 119 pinguini della specie Gentoo, mentre non sono presenti pinguini Chinstrap.
- Isola Dream: Questa isola ospita 55 pinguini Adelie e 68 pinguini Chinstrap, ma nessun pinguino della specie Gentoo.
- Isola Torgersen: Su quest’isola sono presenti solo 47 pinguini della specie Adelie, e nessun pinguino delle specie Chinstrap o Gentoo.
Possiamo dunque commentare dicendo:
- La specie Adelie è distribuita su tutte e tre le isole, con numeri notevoli sia su Biscoe (44), Dream (55), che Torgersen (47).
- La specie Chinstrap si trova solo sull’isola Dream (68 esemplari) e non è presente sulle altre due isole.
- La specie Gentoo si trova esclusivamente sull’isola Biscoe (119 esemplari), non essendo presente su Dream e Torgersen.
Questo suggerisce una distribuzione geografica specifica delle diverse specie di pinguini, con alcune specie limitate a determinate isole e altre distribuite più ampiamente.
18.4 Grafico a barre
18.4.1 Grafico a Barre con una Singola Variabile
Un grafico a barre è uno strumento comunemente utilizzato per rappresentare visivamente una singola variabile categoriale. Questo tipo di grafico mostra le diverse categorie su uno degli assi (solitamente l’asse orizzontale) e utilizza barre di altezza proporzionale per rappresentare la frequenza o il conteggio di ciascuna categoria sull’altro asse (solitamente l’asse verticale).
Ad esempio, in un dataset che contiene informazioni su diverse specie di pinguini, un grafico a barre potrebbe mostrare il numero di pinguini per ciascuna specie. Le specie vengono visualizzate come etichette lungo l’asse delle ascisse, mentre l’altezza delle barre rappresenta il numero di pinguini osservati per ciascuna specie.
Il grafico a barre consente di confrontare le dimensioni delle categorie in modo semplice e intuitivo.
Per i dati in esame, creiamo un grafico a barre che rappresenta il numero totale di pinguini per isola.
= contingency_table.sum(axis=1).reset_index()
contingency_table_sum "island"], contingency_table_sum[0], alpha=0.5)
plt.bar(contingency_table_sum["Numero totale di pinguini per isola")
plt.title("Isola")
plt.xlabel("Numero di pinguini")
plt.ylabel( plt.show()
Un secondo grafico a barre mostra il numero totale di pinguini per specie.
= contingency_table.sum(axis=0).reset_index()
contingency_table_species_sum
plt.bar("species"],
contingency_table_species_sum[0],
contingency_table_species_sum[=0.5,
alpha
)"Numero totale di pinguini per specie")
plt.title("Specie")
plt.xlabel("Numero di pinguini")
plt.ylabel( plt.show()
18.4.2 Grafico a Barre con Due Variabili
È possibile visualizzare contemporaneamente le distribuzioni di due variabili categoriali utilizzando un grafico a barre. Questo tipo di grafico è particolarmente utile per esaminare la relazione tra due variabili categoriali.
In un grafico a barre con due variabili, una delle variabili viene rappresentata sull’asse orizzontale come categoria principale, mentre la seconda variabile è distinta tramite colori diversi o barre impilate. In questo modo, possiamo confrontare facilmente le frequenze o le proporzioni delle categorie della prima variabile, osservando allo stesso tempo come sono distribuite le categorie della seconda variabile all’interno di ciascuna categoria principale.
Ad esempio, visualizziamo il numero di pinguini per specie e isola. A qusto fine possiamo creare un grafico a barre dove le isole sono rappresentate sull’asse delle ascisse e i diversi colori delle barre mostrano la distribuzione delle specie su ciascuna isola. Questo approccio consente di esplorare come le due variabili categoriali (specie e isola) interagiscono visivamente.
="bar", stacked=True, figsize=(10, 6), colormap="viridis")
contingency_table.plot(kind"Numero di pinguini per specie e isola")
plt.title("Isola")
plt.xlabel("Numero di pinguini")
plt.ylabel(="Specie")
plt.legend(title plt.show()
In alternativa, è possibile creare un grafico a barre dove le specie sono rappresentate sull’asse delle ascisse e i diversi colori delle barre mostrano la distribuzione delle isole per ciascuna specie.
="bar", stacked=True, figsize=(10, 6), colormap="viridis")
contingency_table.T.plot(kind"Numero di pinguini per isola e specie")
plt.title("Specie")
plt.xlabel("Numero di pinguini")
plt.ylabel(="Isola")
plt.legend(title plt.show()
In alternativa all’uso delle frequenze assolute, possiamo rappresentare i dati utilizzando le frequenze relative. Questo approccio permette di confrontare meglio le categorie indipendentemente dal numero totale di osservazioni. Nella figura seguente, ad esempio, viene mostrata la proporzione di pinguini di ciascuna specie per ogni isola, evidenziando la distribuzione relativa delle specie su ogni isola, anziché il conteggio assoluto. Questa rappresentazione aiuta a visualizzare le differenze nella composizione delle specie, anche se il numero complessivo di pinguini varia tra le isole.
# Trasformazione della tabella di contingenza in proporzioni per ogni riga (isola)
= contingency_table.div(contingency_table.sum(axis=1), axis=0)
contingency_table_prop
# Rappresentazione visiva in termini proporzionali
= contingency_table_prop.plot(
ax ="bar", stacked=True, figsize=(10, 6), colormap="viridis"
kind
)
# Modifichiamo la leggenda per posizionarla al di fuori del grafico
"Proporzione di pinguini per specie e isola")
plt.title("Isola")
plt.xlabel("Proporzione")
plt.ylabel(
# Posizioniamo la leggenda al di fuori del grafico per migliorare la leggibilità
="Specie", bbox_to_anchor=(1.05, 1), loc="upper left")
plt.legend(title
plt.tight_layout() plt.show()
/var/folders/s7/z86r4t9j6yx376cm120nln6w0000gn/T/ipykernel_47978/957400763.py:17: UserWarning: The figure layout has changed to tight
plt.tight_layout()
18.5 Mosaic plots
Il Mosaic plot è una tecnica di visualizzazione particolarmente adatta per rappresentare tabelle di contingenza. Questo tipo di grafico somiglia a un grafico a barre impilate standard, ma con un vantaggio importante: oltre a visualizzare la suddivisione interna delle categorie, permette di vedere anche le dimensioni relative dei gruppi della variabile principale.
In altre parole, il Mosaic plot non solo mostra come si distribuiscono le categorie di una variabile secondaria all’interno di ogni gruppo della variabile principale, ma fornisce anche un’idea visiva della grandezza complessiva dei gruppi. Questo lo rende uno strumento utile per analizzare e interpretare le relazioni tra due variabili categoriali, evidenziando sia la proporzione all’interno di ciascun gruppo, sia la grandezza relativa tra i gruppi stessi.
# Funzione per personalizzare le etichette (evita di mostrare etichette troppo piccole)
def labelizer(key):
= key
species, island = df[(df["species"] == species) & (df["island"] == island)].shape[0]
count if count > 1: # Definisci una soglia per visualizzare le etichette
return f"{species}\n{count}"
else:
return ""
"species", "island"], labelizer=labelizer)
mosaic(df, ["Mosaic Plot of Species and Island")
plt.title( plt.show()
18.6 Proporzioni di Riga e Colonna
Nelle sezioni precedenti abbiamo esaminato la visualizzazione di due variabili categoriali utilizzando grafici a barre e Mosaic plot. Tuttavia, non abbiamo ancora discusso come vengono calcolate le proporzioni mostrate in questi grafici. In questa sezione ci concentreremo sulla suddivisione frazionaria di una variabile rispetto a un’altra, esplorando come possiamo modificare la nostra tabella di contingenza per ottenere una visione più dettagliata delle proporzioni.
Questo ci permetterà di comprendere meglio le relazioni tra le due variabili, visualizzando non solo i conteggi assoluti, ma anche le proporzioni relative per riga o per colonna. Le proporzioni di riga mostrano la distribuzione di una variabile all’interno delle categorie di un’altra, mentre le proporzioni di colonna evidenziano la distribuzione inversa.
Calcoliamo le proporzioni di specie per isola.
= contingency_table.div(contingency_table.sum(axis=1), axis=0)
row_proportions
# Aggiungiamo una colonna "Totale" che rappresenta il totale di ciascuna riga
= row_proportions.copy()
row_proportions_with_total "Totale"] = row_proportions.sum(axis=1)
row_proportions_with_total[
# Mostra la tabella con le proporzioni di riga e il totale
print(row_proportions_with_total)
species Adelie Chinstrap Gentoo Totale
island
Biscoe 0.269939 0.000000 0.730061 1.0
Dream 0.447154 0.552846 0.000000 1.0
Torgersen 1.000000 0.000000 0.000000 1.0
Calcoliamo nuovamente le proporzioni, ma questa volta in funzione delle colonne (per isola).
= contingency_table.div(contingency_table.sum(axis=0), axis=1)
column_proportions
# Aggiungiamo una riga "Totale" che rappresenta il totale di ciascuna colonna
= column_proportions.copy()
column_proportions_with_total "Totale"] = column_proportions.sum(axis=0)
column_proportions_with_total.loc[
# Mostra la tabella con le proporzioni di colonna e il totale
print(column_proportions_with_total)
species Adelie Chinstrap Gentoo
island
Biscoe 0.301370 0.0 1.0
Dream 0.376712 1.0 0.0
Torgersen 0.321918 0.0 0.0
Totale 1.000000 1.0 1.0
18.7 Confronto tra Gruppi
Alcune delle analisi più interessanti emergono confrontando i dati numerici tra diversi gruppi. In questa sezione approfondiremo alcune delle tecniche che abbiamo già esplorato per visualizzare i dati numerici di più gruppi su uno stesso grafico e introdurremo nuovi metodi per confrontare i dati numerici tra gruppi. Queste tecniche ci permetteranno di osservare meglio le differenze e le somiglianze tra gruppi, mettendo in evidenza tendenze, variazioni e altre caratteristiche rilevanti.
Iniziamo considerando due variabili qualitative. Creiamo un grafico a barre per confrontare la distribuzione del genere per specie.
=df, x="species", hue="sex")
sns.countplot(data"Distribuzione del genere per specie")
plt.title("Specie")
plt.xlabel("Conteggio")
plt.ylabel( plt.show()
Molto spesso i confronti più interessanti sono quelli che riguardano la distribuzione di una variabile numerica in funzione di una o più variabili qualitative.
Per fare un esempio, generiamo un grafico che confronta la distribuzione del peso corporeo (body_mass_g
) in funzione della specie e del genere. Le diverse aree colorate rappresentano la distribuzione del peso per maschi e femmine all’interno di ciascuna specie.
sns.violinplot(=df,
data="body_mass_g",
x="species",
y="sex",
hue=True,
split="quart",
inner="viridis",
palette
)
"Distribuzione di Body Mass in funzione di specie e genere")
plt.title("Body Mass (g)")
plt.xlabel("Specie")
plt.ylabel(
="Genere", bbox_to_anchor=(1.05, 1), loc="upper left")
plt.legend(title
plt.tight_layout() plt.show()
/var/folders/s7/z86r4t9j6yx376cm120nln6w0000gn/T/ipykernel_47978/2495003544.py:16: UserWarning: The figure layout has changed to tight
plt.tight_layout()
18.8 Codice R
Puoi accedere alla versione in R del codice di questo notebook qui.
Informazioni sull’Ambiente di Sviluppo
%load_ext watermark
%watermark -n -u -v -iv -w -m
Last updated: Thu Sep 12 2024
Python implementation: CPython
Python version : 3.12.4
IPython version : 8.26.0
Compiler : Clang 16.0.6
OS : Darwin
Release : 23.6.0
Machine : arm64
Processor : arm
CPU cores : 8
Architecture: 64bit
matplotlib: 3.9.1
seaborn : 0.13.2
arviz : 0.18.0
numpy : 1.26.4
pandas : 2.2.2
Watermark: 2.4.3