15 marzo 2026

Grafici di dispersione (scatterplot) per fattore con ggplot

La statistica è l'arte sottile che consente di ricavare informazione dai dati. Vediamo come ciò sia possibile impiegando anche semplici grafici di dispersione ma in questo caso separati per fattore.

Il punto di partenza è un grafico xy della distribuzione dei valori di concentrazione nel sangue dei globuli rossi (in ascisse) e dell'emoglobina (in ordinate) misurati in 202 atleti australiani di entrambi i sessi che praticano dieci differenti sport e contenuti nel set di dati ais.
 
# SCATTERPLOT semplice con ggplot
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# scatterplot semplice
ggplot(ais, aes(x=rcc, y=hg, color=sport)) + geom_point()
#

Le prime due righe servono a caricare i due pacchetti necessari, che devono essere preventivamente scaricati dal CRAN, e il primo dei quali contiene il set di dati [1]. 

Per realizzare il grafico viene impiegata la funzione ggplot() che prevede come argomenti:
→ il nome della tabella che contiene i dati (ais);
→ la funzione aes() che specifica la variabile da riportare in ascisse (rcc) e la variabile da riportare in ordinate (hg), che sono rispettivamente la concentrazione degli eritrociti (o globuli rossi) nel sangue espressa in milioni per microlitro di sangue (10^6/µL) e la concentrazione di emoglobina espressa in grammi per decilitro di sangue (g/dL);
→ il colore dei punti (color) separato per ciascuno degli sport praticati e codificati nella variabile sport.

Alla funzione ggplot() viene quindi collegata con il segno più [+] la funzione geom_point() che realizza il grafico impiegando i valori previsti di default e individuando gli sport praticati con un colore.


Visto il grado di sovrapposizione dei dati, vogliamo provare a separarli generando dieci scatterplot, uno per ciascuno degli sport. Ecco lo script necessario e i grafici risultanti.
 
# SCATTERPLOT separati per fattore
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# scatterplot separati per il fattore sport
ggplot(ais, aes(x=rcc, y=hg, color=sport)) + geom_point() + facet_wrap(~sport) 
#


Quindi per scomporre i dati nei dieci sottoinsiemi rappresentativi di ciascuno sport è stato sufficiente, al codice necessario per il grafico di dispersione che riporta tutti i dati, aggiungere (+) la funzione facet_wrap() specificando che i dati devono essere suddivisi per sport (~sport).

Possiamo anche migliorare il grafico come segue.
 
# SCATTERPLOT riorganizzati per righe
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# scatterplot riorganizzati per righe
ggplot(ais, aes(x=rcc, y=hg, color=sport)) + geom_point(show.legend=FALSE) + facet_wrap(~sport, nrow=2
#

In questo caso, visto che la rappresentazione fornita contiene già le etichette che individuano i dati, è stata eliminata la legenda (show.legend=FALSE) e i grafici sono stati riorganizzati su due righe con nrow=2, con questo risultato.


Dei nostri dati abbiamo quindi realizzato una grafica sintetica (primo script) e poi abbiamo visto come realizzare una grafica analitica: ma che dire se potessimo visualizzarle contemporaneamente entrambe? Ecco come è possibile farlo con due brevi aggiunte allo script precedente.
 
# SCATTERPLOT sovrapposti ai dati globali
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# scatterplot separati sovrapposti alla distribuzione globale dei dati
ggplot(ais, aes(x=rcc, y=hg, color=sport)) + geom_point(data=transform(ais, sport=NULL), colour="grey82") + geom_point(show.legend = FALSE) + facet_wrap(~sport, nrow=2, strip.position="bottom")
#


Nota: i grafici mettono in rilievo l'informazione che la distribuzione dei valori non è omogenea in tutti gli sport – ad esempio gli atleti che praticano lo sport Netball hanno concentrazioni di eritrociti ed emoglobina relativamente basse e all'estremo opposto di quelle relativamente alte che si riscontrano negli atleti che praticano pallanuoto (W_Polo).

In questo caso viene aggiunta una nuova funzione geom_point() che elimina dai dati la variabile sport (sport=NULLmediante la funzione transform() per riportarli tutti  nel grafico – in colore grigio (colour="grey82") – prima che ad essi con le successive funzioni  geom_point() e facet_wrap() siano sovrapposti separatamente i dati dei singoli sport. 

Da notare che la funzione facet_wrap() prevede:
→  l'argomento strip.position che consente di personalizzare la posizione delle etichette, con il valore predefinito "top", che può essere cambiato in "bottom" (come in questo caso), "left" o "right";
→  l'argomento scales che consente di specificare il tipo di scale impiegate, con il valore predefinito "fixed" che assume scale degli assi x e y uguali in tutti i grafici, e con i valori "free", "free_x" o "free_y" da impiegare qualora siano previste scale differenti degli assi.

Completiamo l'analisi grafica dei dati per sport praticato ripetendola anche per il sesso degli atleti sostituendo sport con sex, il nome della variabile fattore che contiene l'indicazione del sesso dell'atleta: al bisogno una informazione completa sulla struttura del set di dati si può avere digitando str(ais).
 
# SCATTERPLOT sovrapposti ai dati globali
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# scatterplot separati sovrapposti alla distribuzione globale dei dati
ggplot(ais, aes(x=rcc, y=hg, color=sex)) + geom_point(data=transform(ais, sex=NULL), colour="grey82") + geom_point(show.legend=FALSE) + facet_wrap(~sex, nrow=2, strip.position="bottom")
#


In questo caso il grafico mette in evidenza che la distribuzione del valori non è omogenea nei due sessi, un fatto che rispecchia i differenti "valori normali" dei globuli rossi e dell'emoglobina nelle donne e negli uomini (valori che sono fisiologicamente mediamente inferiori nelle prime rispetto ai secondi).

Conclusione: opportunamente impiegati i grafici xy consentono di ricavare dai dati informazioni che vanno al di la della semplice relazione tra due variabili.


---------

[1] Vedere il post Il set di dati ais nel quale trovate anche come caricare i dati senza impiegare il pacchetto DAAG.

12 marzo 2026

Tracciare barre dell'errore e limiti di confidenza

Questa volta vediamo come con ggplot2 sia possibile, impiegando una sola riga di codice, ottenere rappresentazioni multiple di una statistica (come ad esempio la media) con le corrispondenti barre dell'errore, che tecnicamente definiscono i limiti di confidenza della statistica.

Limiti di confidenza di una distribuzione campionaria

In questo primo esempio sottraiamo e sommiamo alla media di una variabile 1.96 volte la sua deviazione standard ottenendo i limiti di confidenza al 95% della distribuzione campionaria cioè i limiti entro i quali si trova il 95% dei dati del campione in esame.

Copiate e incollate nella Console di R questo script e premete ↵ Invio.

# BARRE DELL'ERRORE E LIMITI DI CONFIDENZA della distribuzione campionaria
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(dplyr) # carica il pacchetto per i calcoli
library(ggplot2) # carica il pacchetto per la grafica
# calcola media e deviazione standard separatamente per sport e per sesso
ais_stat <- ais %>% group_by(sport, sex) %>% summarise(mean_hg=mean(hg), sd_hg=sd(hg)) %>% ungroup()
#
# crea il grafico ma alcuni dati risultano sovrapposti
#
ggplot(ais_stat, aes(x=sport, y=mean_hg, color=sex)) + geom_point(size=3) + geom_errorbar(aes(ymin=mean_hg-1.96*sd_hg, ymax=mean_hg+1.96*sd_hg), width=0.2) + labs(x="Sport praticato", y="Emoglobina in g/dL", color="Sesso dell'atleta") + theme_minimal() + theme(legend.position="top") 
#

Le prime tre righe servono a caricare i pacchetti necessari, che devono essere preventivamente scaricati dal CRAN, il primo dei quali contiene il set di dati ais impiegato come esempio [1]. 

La quarta riga di codice serve per preparare i dati impiegando l'operatore pipe (%>%) [2] che trasferisce i dati da una funzione alla successiva in questo modo:
→ i dati (ais) sono trasferiti (%>%) alla funzione group_by();
→ la funzione group_by() raggruppa i dati in sottoinsiemi per sport praticato (sport) e per sesso dell'atleta (sex) e il risultato di questa suddivisione viene trasferito (%>%) alla funzione summarise();
→ la funzione summarise() calcola la media mean() e deviazione standard sd() della variabile hg che è la concentrazione nel sangue dell'emoglobina (espressa in g/dL, grammi per decilitro di sangue) dei 202 atleti australiani [1] suddivisi per sport e per sesso, e il risultato viene salvato (<-) nella tabella ais_stat;
→ i risultati sono trasferiti (%>%) alla funzione ungroup() che rimuove dalla tabella ais_stat una serie di valori impiegati provvisoriamente per generarla.

L'ultima riga di codice realizza un grafico:
→ impiegando le statistiche salvate nella variabile ais_stat;
→ riportando in ascisse lo sport praticato (sport), in ordinate la media dell'emoglobina (mean_hg), assegnando colori diversi in base al sesso (sex);
→ riportando un grafico a punti (geom_point) dei dati;
→ aggiungendo con geom_errorbar() le barre che indicano l'errore che nel nostro caso è rappresentato da 1.96 volte la deviazione standard (sd_hg);
→ aggiungendo con labs() le opportune etichette; 
→ impiegando con theme_minimal() una rappresentazione semplice;
→ posizionando infine la legenda in alto (legend.position="top").

Questo è il grafico.


Poiché le barre dell'errore nei due sessi risultano sovrapposte, riducendo la chiarezza del grafico, riprendiamo lo stesso codice con la piccola modifica evidenziata in colore nel codice. Copiate e incollate nella Console di R questo script e premete ↵ Invio.

# BARRE DELL'ERRORE E LIMITI DI CONFIDENZA della distribuzione campionaria
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(dplyr) # carica il pacchetto per i calcoli
library(ggplot2) # carica il pacchetto per la grafica
# calcola media e deviazione standard separatamente per sport e per sesso
ais_stat <- ais %>% group_by(sport, sex) %>% summarise(mean_hg=mean(hg), sd_hg=sd(hg)) %>% ungroup()
#
# crea il grafico evitando la sovrapposizione dei dati
ggplot(ais_stat, aes(x=sport, y=mean_hg, color=sex)) + geom_point(size=3, position=position_dodge(width=0.5)) + geom_errorbar(aes(ymin=mean_hg-1.96*sd_hg, ymax=mean_hg+1.96*sd_hg), width=0.2, position=position_dodge(width=0.5)) + labs(x="Sport praticato", y="Emoglobina in g/dL", color="Sesso dell'atleta") + theme_minimal() + theme(legend.position="top") 
#

L'unica differenza è che aggiungiamo alla funzione geom_point() e alla funzione geom_errorbar() l'argomento position=position_dodge(width=0.5): questo consente di eliminare la sovrapposizione delle barre dell'errore. 


Se ora digitate ais_stat nella Console di R potete visualizzare i dati impiegati per realizzare i due grafici precedenti: le medie (mean_hg) e le deviazioni standard (sd_hg), suddivise per ciascuno sport (sport) e per ciascun sesso (sex).

> ais_stat
# A tibble: 17 × 4
   sport   sex   mean_hg sd_hg
   <fct>   <fct>   <dbl> <dbl>
 1 B_Ball  f        13.1 0.878
 2 B_Ball  m        15.1 0.922
 3 Field   f        14.6 0.682
 4 Field   m        16.0 0.805
 5 Gym     f        13.6 0.860
 6 Netball f        12.8 0.567
 7 Row     f        14.0 0.740
 8 Row     m        15.4 0.711
 9 Swim    f        13.6 0.583
10 Swim    m        15.5 0.655
11 T_400m  f        13.8 1.04 
12 T_400m  m        15.3 0.824
13 T_Sprnt f        14.2 0.556
14 T_Sprnt m        16.2 1.49 
15 Tennis  f        13.5 1.10 
16 Tennis  m        15.6 1.48 
17 W_Polo  m        15.5 0.718

Limiti di confidenza di una media campionaria

Ripetiamo l'analisi dei dati: ma questa volta sottraiamo e sommiamo alla media della variabile 1.96 volte il suo errore standard ottenendo così i limiti di confidenza al 95% della media campionaria, che forniscono una misura del grado di incertezza che caratterizza detta media.

Copiate e incollate nella Console di R questo script e premete ↵ Invio.

# BARRE DELL'ERRORE E LIMITI DI CONFIDENZA della media campionaria
#
library (DAAG) # carica il pacchetto che include il set di dati ais
library(dplyr) # carica il pacchetto per i calcoli
library(ggplot2) # carica il pacchetto per la grafica
library(plotrix) # carica il pacchetto per il calcolo dell'errore standard
# calcola media e deviazione standard separatamente per sport e per sesso
ais_stat <- ais %>% group_by(sport, sex) %>% summarise(mean_hg=mean(hg), es_hg=std.error(hg)) %>% ungroup()
#
# crea il grafico evitando la sovrapposizione dei dati
#
ggplot(ais_stat, aes(x=sport, y=mean_hg, color=sex)) + geom_point(size=3, position=position_dodge(width=0.5)) + geom_errorbar(aes(ymin=mean_hg-1.96*es_hg, ymax=mean_hg+1.96*es_hg), width=0.2, position=position_dodge(width=0.5)) + labs(x="Sport praticato", y="Emoglobina in g/dL", color="Sesso dell'atleta") + theme_minimal() + theme(legend.position="top") 
#

Da notare che deve essere installato anche il pacchetto plotrix che viene impiegato per calcolare l'errore standard della media. Per il resto viene impiegato esattamente lo stesso codice dello script precedente, semplicemente sostituendo la deviazione standard dell'emoglobina con l'errore standard della media (es_hg) calcolato con la funzione std.error(). Questo è il risultato. 


Come si rileva dal grafico, a parte il caso del tennis (Tennis), all'interno di ciascuno sport i limiti di confidenza al 95% delle medie dell'emoglobina negli atleti dei due sessi non si sovrappongono (la considerazione che se ne può trarre è riportata qui sotto nella Conclusione).

Se digitate ais_stat nella Console di R potete ora visualizzare la tabella che contiene media (mean_hg) ed errore standard (es_hg) calcolati per ciascuno sport (sport) e per ciascun sesso (sex).

> ais_stat
# A tibble: 17 × 4
   sport   sex   mean_hg es_hg
   <fct>   <fct>   <dbl> <dbl>
 1 B_Ball  f        13.1 0.243
 2 B_Ball  m        15.1 0.266
 3 Field   f        14.6 0.258
 4 Field   m        16.0 0.232
 5 Gym     f        13.6 0.430
 6 Netball f        12.8 0.118
 7 Row     f        14.0 0.158
 8 Row     m        15.4 0.184
 9 Swim    f        13.6 0.194
10 Swim    m        15.5 0.182
11 T_400m  f        13.8 0.312
12 T_400m  m        15.3 0.194
13 T_Sprnt f        14.2 0.278
14 T_Sprnt m        16.2 0.450
15 Tennis  f        13.5 0.414
16 Tennis  m        15.6 0.741
17 W_Polo  m        15.5 0.174


Conclusione
 il termine barra dell'errore o errorbar indica semplicemente l'espediente grafico impiegato per rappresentare attorno al valore di una statistica i suoi limiti di confidenza al livello di probabilità desiderato, che il più delle volte viene fissato al 95%;
 limiti di confidenza al 95% della distribuzione campionaria – calcolati come media±1.96∙ds (deviazione standard) – sono i limiti all'interno dei quali cade il 95% dei valori osservati nel campione esaminato, quindi la probabilità che un dato risulti al di fuori di questi limiti è del 5%;
 i limiti di confidenza al 95% della media campionaria – calcolati come media±1.96∙es (errore standard) – sono i limiti all'interno dei quali abbiamo il 95% di probabilità che cada la media, quindi la probabilità che la media che andiamo ricercando risulti al di fuori di questi limiti è del 5% (stante l'informazione contenuta nel nostro campione). Questi limiti forniscono un test di significatività della differenza tra le medie: quando i limiti di confidenza al 95% di due medie non si sovrappongono, possiamo affermare che tra le due medie esiste una differenza statisticamente significativa. Quindi nel nostro caso possiamo concludere che all'interno di ciascuno degli sport praticati da entrambi i sessi, con una sola eccezione, la concentrazione media dell'emoglobina nelle atlete è significativamente inferiore a quella degli atleti e che la probabilità che questa conclusione sia errata è del 5% o meno;
il risultato di una statistica dovrebbe essere sempre riportato con la sua incertezza, espressa in termini dei suoi limiti di confidenza opportunamente specificati: limiti di confidenza al 95%, limiti di confidenza al 99%, media±n∙ds, media±n∙es, ove n è l'opportuno moltiplicatore, eccetera.


----------

[1] Vedere il post Il set di dati ais nel quale trovate anche come caricare i dati senza impiegare il pacchetto DAAG.