venerdì 13 giugno 2025

Grafici a barre (barplot) con ggplot

grafici a barre (bar plot o barplot) si possono realizzare impiegando le funzioni base di R [1]. Tuttavia con il pacchetto ggplot2 si possono ottenere risultati molto interessanti, qui illustrati con tre script che si aggiungono ad altri già riportati [2] e che, quando si è agli inizi, possono aiutare a familiarizzare con le funzioni di questo pacchetto.

Come trovate riportato nel manuale di riferimento del pacchetto [3] "... Esistono due tipi di grafici a barre: geom_bar() e geom_col() ... geom_bar() rende l'altezza della barra proporzionale al numero di casi in ogni gruppo ... Se volete che le altezze delle barre rappresentino i valori nei dati, usate geom_col() al suo posto".

Oltre al pacchetto ggplot2 [4] dovete accertarvi di avere installato – o dovete scaricare anche – il pacchetto DAAG [5] che contiene il set di dati ais impiegati nell'esempio, il pacchetto RColorBrewer che contiene una serie di palette (raccolte di colori) aggiuntive e il pacchetto gridExtra che consente di combinare i grafici in una sola figura.

Le altezze delle barre rappresentano i valori numerici dei dati

Per questo tipo di rappresentazione si impiega la funzione geom_col().  Copiate e incollate nella Console di R questo script e premete ↵ Invio.

# BARPLOT con ggplot 
# barplot semplici
#
library(ggplot2) # carica il pacchetto per la grafica
library(RColorBrewer) # carica il pacchetto con le palette aggiuntive
library(gridExtra) # carica il pacchetto per combinare i grafici in una sola figura
#
# genera la tabella con i dati da rappresentare
mydata <- data.frame(loc=c("Milano", "Catania", "Roma"), temp=c(18.3, 26.8, 21.2))
#
# barplot di base
plot1 <- ggplot(data=mydata, aes(x=loc, y=temp)) + geom_col()
#
# scambia tra loro gli assi
plot2 <- ggplot(data=mydata, aes(x=loc, y=temp, fill=as.factor(temp))) + geom_col(width=0.6) + ylim(0, 30) + labs(x="Località", y="Temperatura in gradi Celsius") + theme(legend.position="bottom") + scale_fill_discrete(name="°C") + coord_flip()
#
# riporta i valori numerici in testa alle barre
plot3 <- ggplot(data=mydata, aes(x=loc, y=temp)) + geom_col(width=0.6, fill="steelblue3") + ylim(0, 30) + labs(x="Località", y="Temperatura in gradi Celsius") + scale_x_discrete(limits=c("Milano", "Roma", "Catania")) + geom_text(aes(label=temp), vjust=-0.6, color="black", size=3.2)
#
# applica alle barre un gradiente di colori
plot4 <- ggplot(data=mydata, aes(x=loc, y=temp, fill=as.factor(temp))) + geom_col(width=0.6) + ylim(0, 30) + labs(x="Località", y="Temperatura in gradi Celsius") + scale_x_discrete(limits=c("Milano", "Roma", "Catania")) + theme_classic() + scale_fill_brewer(palette="Blues") + labs(fill="°C")
#
# combina i grafici in una sola figura
grid.arrange(plot1, plot2, plot3, plot4, nrow=2, ncol=2)
#

Dopo avere caricato i pacchetti necessari, sono generati i dati impiegati come esempio (mydata):

      loc temp
1  Milano 18.3
2 Catania 26.8
3    Roma 21.2

Per realizzare il primo grafico (plot1) viene impiegata innanzitutto la funzione ggplot()  che inizializza l'oggetto al quale sono collegate con il segno più [+] le successive funzioni, che sviluppano la grafica  che prevede:
→ come primo argomento il nome della tabella che contiene i dati (mydata);
 come secondo argomento la funzione aes() che specifica di riportare in ascisse (x=) la variabile (fattore) loc che indica la località e in ordinate (y=) la variabile (numerica) temp che riporta la temperatura rilevata espressa in gradi Celsius (°C). Alla funzione ggplot() viene quindi collegata con il segno più [+la funzione geom_col() che realizza come detto il grafico a barre nel quale le altezze delle barre rappresentano i valori numerici dei dati.

Questa è la struttura base che rimane identica in tutti e quattro i grafici realizzati, che si differenziano tra loro per le personalizzazioni grafiche via via introdotte.

Il secondo grafico (plot2) aggiunge:
→ la colorazione automatica delle barre con un colore diverso per ogni località (fill=loc);
→ la riduzione dell'ampiezza della barre con width=0.6;
→ la personalizzazione della scala delle ordinate che ora va da 0 a 30;
→ la funzione labs() che specifica le etichette delle ascisse e delle ordinate;
→ la funzione theme() che specifica la posizione in basso ("bottom") per la legenda;
→ il titolo °C della legenda con la funzione scale_fill_discrete();
→ la funzione coord_flip() che scambia tra di loro la posizione degli assi.

Nel terzo grafico (plot3):
→ con fill="steelblue3" viene specificato manualmente il colore delle barre;
→ con scale_x_discrete() viene specificato manualmente l'ordine delle barre;
→ con geom_text() in testa a ciascuna barra viene riportata la temperatura corripondente (temp) specificando posizione (vjust), colore (color) e dimensione (size) dei caratteri.

Il quarto grafico (plot4) ricalca il terzo grafico e in aggiunta:
→ con theme_classic() ripristina la struttura classica impiegata nei grafici;
→ con scale_fill_brewer() applica il gradiente di colori della palette Blues;
→ con labs() riporta nella legenda il titolo °C.

Infine con la funzione grid.arrange() i quattro grafici sono combinati in un'unica figura.


Per vedere come lavorare su dati che contengono un fattore di classificazione copiate e incollate nella Console di R questo secondo script e premete ↵ Invio.

# BARPLOT con ggplot
# barplot con un fattore
library(DAAG) # carica il pacchetto DAAG che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
library(RColorBrewer) # carica il pacchetto con le palette aggiuntive
library(gridExtra) # carica il pacchetto per combinare i grafici in una sola figura
#
# calcola sui dati della tabella ais la mediana di hg per sport 
hgb <- aggregate(hg~sport, data=ais, median) 
hgb # mostra la tabella con i dati da rappresentare
#
# barplot di base
plot1 <- ggplot(data=hgb, aes(x=sport, y=hg)) + geom_col(fill="steelblue3") + ylim(0, 20) + theme(axis.text.x=element_text(angle=90, hjust=1)) 
#
# scambia tra loro gli assi
plot2 <- ggplot(data=hgb, aes(x=sport, y=hg)) + geom_col(width=0.8, fill="steelblue3") + ylim(0, 20) + coord_flip()
#
# riporta i valori numerici all'interno delle barre
plot3 <- ggplot(data=hgb, aes(x=reorder(sport, -hg), y=hg)) + geom_col(width=0.8, fill="steelblue3") + ylim(0, 20) + geom_text(aes(label=hg), vjust=1.6, color="white", size=2.8) + theme(axis.text.x=element_text(angle=90, hjust=1)) + theme(legend.position="none") + labs(title="Mediana dell'emoglobina \n per sport", x="Sport praticato", y="Emoglobina in g/dL")
#
# applica alle barre un gradiente di colori
mycolors <- colorRampPalette(brewer.pal(9, "Blues"))(40)[11:20] # porta da 9 a 40 i colori della palette e seleziona i 10 colori da 11 a 20
plot4 <- ggplot(data=hgb, aes(x=reorder(sport, hg), y=hg, fill=as.factor(hg))) + geom_col(width=0.8) + theme_classic() + ylim(0, 20) + geom_text(aes(label=hg), vjust=-0.6, color="black", size=3.2) + theme(axis.text.x=element_text(angle=90, hjust=1)) + theme(legend.position="none") + labs(title="Mediana dell'emoglobina \n per sport", x="Sport praticato", y="Emoglobina in g/dL") + theme(plot.title=element_text(hjust=0.5)) + scale_fill_manual(values=mycolors)
#
# combina i grafici in una sola figura
grid.arrange(plot1, plot2, plot3, plot4, nrow=2, ncol=2) 
#

Innanzitutto sui dati della tabella ais contenuta nel pacchetto DAAG è calcolata la mediana (median) dell'emoglobina per ciascuno sport (hg~sport), salvandola nella nuova tabella hgb che viene mostrata e che contiene ora i dati da rappresentare:

> hgb # mostra la tabella con i dati da rappresentare
     sport   hg
1   B_Ball 14.0
2    Field 15.5
3      Gym 13.7
4  Netball 12.7
5      Row 14.7
6     Swim 15.1
7   T_400m 14.8
8  T_Sprnt 15.2
9   Tennis 14.3
10  W_Polo 15.6

Nel primo grafico (plot1):
→ sono impiegati i dati della tabella hg riportando in ascisse sport e in ordinate la mediana hg dell'emoglobina;
→ le barre sono colorate tutte con lo stesso colore, specificato in fill;
→ con la funzione ylim() viene dimensionata la scala delle ordinate da 0 a 20;
→ con theme() le etichette dell'asse delle x sono riportate in verticale (angle=90).

Il secondo grafico (plot2) è identico al precedente a parte il fatto che le barre sono ridotte di ampiezza (width=0.8) e gli assi sono scambiati tra loro con coord_flip() per porre le barre in orizzontale.

Il terzo grafico (plot3) riprende nuovamente il primo con alcune aggiunte e modifiche:
→ le barre sono ordinate ponendo la mediana dell'emoglobina in ordine decrescente (-hg);
→ con geom_text() sono riportate all'interno delle barre (vjust=1.6) in colore chiaro (white), rimpiccolendo lievemente i caratteri (size=2.8), le etichette con i valori (label=hg) della mediana dell'emoglobina;
→ con theme() le etichette dell'asse delle x sono riportate in verticale (angle=90);
→ viene esclusa la rappresentazione della legenda ("none");
→ con labs() sono aggiunti il titolo e le etichette degli assi.

Il quarto grafico (plot4) ripresenta il terzo grafico con poche aggiunte e modifiche:
→ innanzitutto sono portati da 9 a 40 i colori della palette, sono selezionati i 10 colori da 11 a 20, e questi sono memorizzati nell'oggetto mycolors;
→ sono riportati colori diversi per ciascun valore dell'emoglobina con as.factor(hg);
→ viene tolto lo sfondo con theme_classic();
→ le etichette con i valori della mediana dell'emoglobina sono portate all'esterno delle barre (vjust=-0.6) aumentando lievemente la dimensione dei caratteri (size=3.2);
→ con hjust=0.5 il titolo viene centrato;
→ le barre sono riportate nel gradiente di colori memorizzati in mycolors.

Questi sono i quattro grafici risultanti.



Vediamo ora cosa accade quando si desidera realizzare i grafici a barre a partire da una tabella che contiene due fattorinei quali come nei due casi precedenti le altezze delle barre rappresentano i valori dei dati e quindi è necessario impiegare la funzione geom_col().

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

# BARPLOT con ggplot 
# barplot con due fattori
library(DAAG) # carica il pacchetto DAAG che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
library(plotrix) # carica il pacchetto per il calcolo dell'errore standard
library(gridExtra) # carica il pacchetto per combinare i grafici in una sola figura
#
# aggrega i dati calcolando media ed errore standard per sport e per sesso
hg <- aggregate(hg~sport+sex, data=ais, FUN=function(x) c(mean=mean(x), se=std.error(x))) 
hg # mostra i dati da rappresentare
media <- hg$hg[,1] # vettore contenente la media
es <- hg$hg[,2] # vettore contenente l'errore standard
#
# grafico a barre impilate per sport e sesso
plot1 <- ggplot(data=hg, aes(x=sport, y=media, fill=sex)) + geom_col() + theme(axis.text.x=element_text(angle=90, hjust=1)) + ylim(0, 31)
#
# grafico a barre affiancate per sport e sesso
plot2 <- ggplot(data=hg, aes(x=sport, y=media, fill=sex)) + geom_col(position=position_dodge()) + theme(axis.text.x=element_text(angle=90, hjust=1)) + ylim(0, 31)
#
plot3 <- ggplot(data=hg, aes(x=sport, y=media, fill=sex)) + geom_col(position=position_dodge()) + theme(axis.text.x=element_text(angle=90, hjust=1)) + ylim(0, 20) + geom_errorbar(aes(ymin=media-1.96*es, ymax=media+1.96*es), width=0.2, position=position_dodge(0.9)) + labs(title="Media dell'emoglobina ± 1.96 es") 
#
plot4 <- ggplot(data=hg, aes(x=sport, y=media, fill=sex)) + geom_point(aes(fill=sex), shape=21, size=3, position=position_dodge(0.9)) + theme_classic() + theme(axis.text.x=element_text(angle=90, hjust=1)) + ylim(0, 20) + geom_errorbar(aes(ymin=media-1.96*es, ymax=media+1.96*es), width=0.2, position=position_dodge(0.9)) + labs(title="Media dell'emoglobina ± 1.96 es", x="Sport praticato", y="Emoglobina in g/dL") + scale_fill_discrete(name = "Sesso", labels = c("Donne", "Uomini"))
#
# combina i grafici in una sola figura
grid.arrange(plot1, plot2, plot3, plot4, nrow=2, ncol=2) 
#

In questo caso dai dati della tabella ais contenuta nel pacchetto DAAG riportiamo la variabile emoglobina suddivisa per sport e per sesso (hg~sport+sex) e ne calcoliamo la media (mean) e l'errore standard (std.error) – quest'ultima funzione richiede il pacchetto plotrix – e questa è la tabella risultante della quale vogliamo rappresentare i grafici a barre:

> hg # mostra i dati da rappresentare
     sport sex    hg.mean      hg.se
1   B_Ball   f 13.1307692  0.2434750
2    Field   f 14.6285714  0.2579353
3      Gym   f 13.6000000  0.4301163
4  Netball   f 12.8173913  0.1182301
5      Row   f 14.0318182  0.1576871
6     Swim   f 13.5666667  0.1943651
7   T_400m   f 13.7727273  0.3122036
8  T_Sprnt   f 14.1750000  0.2780138
9   Tennis   f 13.5285714  0.4144499
10  B_Ball   m 15.1416667  0.2661335
11   Field   m 16.0250000  0.2322893
12     Row   m 15.3866667  0.1835799
13    Swim   m 15.5076923  0.1816807
14  T_400m   m 15.3055556  0.1941080
15 T_Sprnt   m 16.1909091  0.4503442
16  Tennis   m 15.6500000  0.7410578
17  W_Polo   m 15.5176471  0.1741017

Nel primo grafico (plot1) è riportata la struttura base che impieghiamo:
→ sono riportate le barre che rappresentano la media (media) dell'emoglobina con un colore separato per ciascun sesso (fill=sex);
→ con theme() le etichette dell'asse delle x sono riportate in verticale (angle=90); 
→ con la funzione ylim() viene dimensionata la scala delle ordinate da 0 a 31;
→ per tutti gli altri parametri grafici sono lasciati i valori di default per cui abbiamo un grafico a barre impilate che include automaticamente anche una legenda.

Il secondo grafico (plot2) differisce dal precedente solamente per il fatto che, con position=position_dodge(), realizziamo un grafico a barre affiancate.

Nel terzo grafico (plot3):
→ per una miglior visualizzazione viene dimensionata la scala delle ordinate da 0 a 20;
→ con geom_errorbar() si riportano in testa a ciascuna barra, la cui altezza è la media dell'emoglobina, i limiti ±1.96 volte l'errore standard (es) cioè i limiti di confidenza al 95% della media;
→ si aggiunge un titolo.

Il quarto grafico (plot4) ripresenta il precedente ma:
→ sostituisce le barre con un circolo (shape=21) di diametro size=3 posizionato in corrispondenza della media;
→ con theme_classic() toglie lo sfondo;
→ aggiunge agli assi le etichette con una descrizione delle grandezze rappresentate;
→ con scale_fill_discrete() personalizza la legenda.

Questi sono i quattro grafici così realizzati.


Due brevi considerazioni, basate sull'assunto che i grafici possono essere impiegati per fornire una rappresentazione sintetica dei dati, ma che questa non deve mai essere fuorviante:
→ per consentire un confronto dei valori in relazione a un fattore (qui il sesso) è necessario impiegare barre affiancate (in alto a destra), le barre impilate (in alto a sinistra) sono da evitare;
→ è da evitare anche la rappresentazione delle medie sotto forma di barre (in basso a sinistra), in quanto queste danno l'idea di un "continuum" di valori che parte da 0, modi adeguati possono essere (i) la rappresentazione puntiforme qui impiegata (in basso a  destra) o in alternativa anche (ii) un grafico a scatola con i baffi [6].  

Le altezze delle barre rappresentano i conteggi dei dati

Per questo tipo di rappresentazione si impiega la funzione geom_bar() che realizza grafici a barre nei quali le altezze delle barre rappresentano i conteggi dei dati raggruppati mediante uno o più fattori, di volta in volta specificati. Lo facciamo con tre esempi molto semplici.

Nel primo esempio:
→ viene riportato in ascisse (x=sport) lo sport praticato;
→ per ciascuno sport viene riportata con geom_bar() una barra la cui altezza rappresenta il numero di dati (atleti) conteggiati per lo specifico sport;
→ le barre sono riportate in colore grigio con fill="grey";
→ con theme() le etichette dell'asse delle x sono riportate in verticale (angle=90);

# BARPLOT con ggplot (barplot semplice)
library(DAAG) # carica il pacchetto DAAG che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# barplot di base
ggplot(data=ais, aes(x=sport)) + geom_bar(fill="grey") + theme(axis.text.x=element_text(angle=90, hjust=1)) 
#

Il secondo esempio riprende esattamente il primo ma aggiunge con aes(fil=sex) a ciascuna barra colori differenziati in base alla variabile/fattore sesso (sex): in questo modo viene conteggiato il numero di casi (numero di atleti) separatamente per ciascun sesso, lasciando le barre sovrapposte.

# BARPLOT con ggplot 
# barplot sovrapposti, con un fattore
library(DAAG) # carica il pacchetto DAAG che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# barplot sovrapposti differenziati per sesso
ggplot(data=ais, aes(x=sport)) + geom_bar(aes(fill=sex)) + theme(axis.text.x=element_text(angle=90, hjust=1)) 
#

Infine nel terzo esempio, per il resto identico al secondo, aggiungendo position=position_dodge() alla funzione geom_bar() le barre vengono affiancate.

# BARPLOT con ggplot 
# barplot affiancati, con un fattore
library(DAAG) # carica il pacchetto DAAG che include il set di dati ais
library(ggplot2) # carica il pacchetto per la grafica
#
# barplot affiancati differenziati per sesso
ggplot(data=ais, aes(x=sport)) + geom_bar(aes(fill=sex), position=position_dodge()) + theme(axis.text.x=element_text(angle=90, hjust=1)) 
#

I tre grafici ottenuti sono qui riportati affiancati, quello del primo script in alto a sinistra, quello del secondo al centro e quello del terzo a destra.


Potete aggiungere ulteriori approfondimenti consultando gli altri post nei quali il pacchetto ggplot2 è stato impiegato [2] o la sua documentazione ufficiale [4].


---------- 


[2] Fate click su ggplot2 nelle Parole chiave o digitate ggplot2 nella casella Cerca nel blog quindi fate click su Cerca.

[3] "There are two types of bar charts: geom_bar() and geom_col() [and] geom_bar() makes the height of the bar proportional to the number of cases in each group ... If you want the heights of the bars to represent values in the data, use geom_col() instead".

[4] Vedere la documentazione e il manuale di riferimento su:
https://cran.r-project.org/web/packages/ggplot2/index.html

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

[6] Fate click su grafici a scatola con i baffi nelle Parole chiave.

Nessun commento:

Posta un commento