Fondamenti di ggplot2
Le statistiche riassuntive non bastano!
13 dataset con IDENTICHE statistiche:
Senza visualizzare, sembrano lo stesso dataset!
Con la visualizzazione, la verità emerge…
library(ggplot2)
library(datasauRus)
ggplot(datasaurus_dozen, aes(x = x, y = y, color = dataset)) +
geom_point() +
facet_wrap(~ dataset, ncol = 4) +
theme_minimal() +
theme(legend.position = "none")Pattern completamente diversi:
La Lezione
Mai fidarsi solo delle statistiche!
La visualizzazione rivela pattern, outlier, distribuzioni che i numeri nascondono.
Prima del Datasaurus, c’era Anscombe (1973)
4 dataset con statistiche IDENTICHE:
Ma visualizzati sono completamente diversi:
Il messaggio
Sempre visualizzare prima di analizzare statisticamente!
Perché le visualizzazioni contano nella scienza?
Principio fondamentale: Un grafico deve rispettare i dati e aiutare il lettore a comprenderli
Gerarchia percettiva (dal più al meno accurato):
Implicazione pratica: Usa posizione quando possibile, evita 3D e pie charts complessi
Riferimento: Cleveland & McGill (1984), Ware (2008)
The Principle of Proportional Ink (Wilke, 2019)
La quantità di “inchiostro” usata per rappresentare un valore deve essere proporzionale al valore stesso
Esempi comuni di violazione:
Best practice:
Errori comuni che distorcono la verità:
1. Assi manipolati - EVITA - asse y troncato senza indicazione chiara
2. Cherry-picking - Mostrare solo subset favorevole
3. Dual y-axes - Scale diverse creano correlazioni spurie → Evitare
4. Binning arbitrario - Bin width cambia la storia → mostra più versioni
Principi per l’uso del colore:
1. Accessibilità
- Color blindness ~8% uomini, ~0.5% donne hanno daltonismo
- Soluzione: palette colorblind-safe (viridis, ColorBrewer)
scale_color_viridis_d() # discreto
scale_color_viridis_c() # continuo
scale_color_brewer(palette = "Set2") # ColorBrewer2. Tipi di scale cromatiche
- Qualitativa: categorie non ordinate (Set1, Set2, Dark2)
- Sequenziale: valori ordinati (Blues, Greens, Viridis)
- Divergente: dati con punto medio (RdBu, BrBG) - es. correlazioni
3. Evita semaforo (rosso-giallo-verde) - non accessibile
Redundant Coding (Wilke, Ch. 20)
Codifica la stessa informazione in modi multipli per robustezza:
Vantaggi:
Best practices per figure composite:
1. Small multiples (faceting) - stessi assi
2. Composizione con patchwork
Regole:
Anatomia di una figura pubblicabile:
title = "Breve e informativo", # opzionale per paper
subtitle = "Dettagli aggiuntivi", # opzionale
x = "Variabile esplicativa [unità]", # SEMPRE con unità
y = "Variabile risposta [unità]", # SEMPRE con unità
color = "Gruppo", # etichetta legenda
caption = "Fonte: Studio XYZ (2024)" # fonte datiPer pubblicazioni scientifiche:
theme(text = element_text(size = 12)))Data-ink ratio (Tufte, 1983)
Massimizza la proporzione di “inchiostro” dedicato ai dati
Rimuovi elementi non essenziali: Ma non esagerare!
Obiettivo: Chiarezza, non minimalismo estremo
Soluzioni per punti sovrapposti:
1. Trasparenza (alpha)
2. Jittering
3. 2D density / Heatmap
4. Contour plots
Scegli in base a: numero punti, messaggio da comunicare, audience
“Use Larger Axis Labels” (Wilke, Ch. 24)
Problema comune: Font troppo piccoli nella versione finale
Test pratico:
Formati e parametri corretti:
Linee guida:
Prima di sottomettere, verifica:
✅ Accuratezza
✅ Chiarezza
✅ Professionalità
Libri essenziali:
Articoli scientifici:
Online:
Grammar of Graphics
Framework teorico di Leland Wilkinson (1999)
Decostruire i grafici in componenti riutilizzabili
Tutti i grafici (barre, linee, scatter) condividono elementi comuni:
Mix & Match: Combina i componenti per creare qualsiasi visualizzazione!

I layer si sovrappongono dal basso verso l’alto
Il dataset che vogliamo visualizzare
Importante
ggplot2 lavora meglio con dati “tidy”!

Dati in formato “wide” (NON funziona con ggplot2):
| country | 1999_cases | 1999_pop | 2000_cases | 2000_pop |
|---|---|---|---|---|
| Afghanistan | 745 | 19987071 | 2666 | 20595360 |
| Brazil | 37737 | 172006362 | 80488 | 174504898 |
| China | 212258 | 1272915272 | 213766 | 1280428583 |
Problemi:
Soluzione: Trasformare in formato “long”
Stessi dati in formato “long” (✅ funziona con ggplot2):
| country | year | cases | population |
|---|---|---|---|
| Afghanistan | 1999 | 745 | 19987071 |
| Afghanistan | 2000 | 2666 | 20595360 |
| Brazil | 1999 | 37737 | 172006362 |
| Brazil | 2000 | 80488 | 174504898 |
| China | 1999 | 212258 | 1272915272 |
| China | 2000 | 213766 | 1280428583 |
Vantaggi:
Definizione formale di “Aesthetic”:
Un’estetica è qualsiasi proprietà visuale degli oggetti in un plot che può essere controllata per trasmettere informazione
Il concetto chiave: Un grafico statistico è una mappatura (mapping) da variabili dei dati ad attributi estetici di oggetti geometrici
Aesthetics fondamentali:
x, y - Posizione nello spazio 2Dcolor - Colore di bordi/linee/puntifill - Colore di riempimentosize - Dimensione (area punti)shape - Forma dei puntialpha - Trasparenza/opacitàlinetype - Tipo di lineaLa dicotomia fondamentale di ggplot2:
MAPPING (dentro aes())
Proprietà visiva dipende dai dati
✅ Risultato:
Quando usare: Per visualizzare una variabile del dataset
Perché aes(color = "blue") NON produce punti blu?
# ❌ ERRORE COMUNE - Non produce punti blu!
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(aes(color = "blue"))La logica formale dietro questo comportamento:
aes() interpreta tutto come dati, non come istruzioni di stile"blue" diventa una variabile temporanea con valore costante "blue" per ogni riga"blue"scale_color_discrete() viene applicata automaticamente"blue""blue" → colore = rossoPrincipio chiave
aes() è uno spazio sacro riservato esclusivamente al mapping dati-visual. Qualsiasi valore al suo interno viene interpretato come parte dello spazio dati, non dello spazio stilistico.
| Aesthetic | Proprietà Controllata | Tipo Dati | Geoms Comuni | Note Importanti |
|---|---|---|---|---|
x, y |
Posizione sugli assi | Continuo, Discreto | Tutti | Le estetiche più fondamentali |
color |
Colore bordi/linee/punti | Continuo, Discreto | geom_point, geom_line |
Per shape 21-25: controlla il bordo |
fill |
Colore riempimento | Continuo, Discreto | geom_bar, geom_boxplot |
Solo per geom con area interna |
size |
Area punti, dimensione testo | Continuo | geom_point, geom_text |
Scala l’area, non il raggio. Evitare per variabili discrete |
linewidth |
Spessore linee | Continuo | geom_line, geom_smooth |
Introdotto in ggplot2 3.4.0 per controllo esplicito |
shape |
Forma punti | Discreto | geom_point |
Max 6 categorie. Shapes 0-20: solidi; 21-25: con bordo+fill |
alpha |
Trasparenza (0-1) | Continuo | Tutti | Strumento primario per overplotting |
linetype |
Pattern linea | Discreto | geom_line, geom_path |
Max 6 categorie (solid, dashed, dotted, etc.) |
group |
Raggruppamento dati | Discreto | geom_line, geom_smooth |
Cruciale per aggregazione corretta (es. time series) |
label |
Contenuto testo | Character | geom_text, geom_label |
Specifica il testo da visualizzare |
Percezione visiva
Posizione > Lunghezza > Angolo > Area > Colore. Usa x/y per variabili più importanti, color/size per secondarie.
Due aesthetic per il colore:
color - Bordi, linee, punti (contorni):
geom_point(color = "red") # punti rossi
geom_line(color = "blue") # linea blu
geom_bar(color = "black") # bordo barre nerofill - Riempimento interno (aree):
geom_bar(fill = "steelblue") # barre riempite
geom_boxplot(fill = "lightgreen") # box riempiti
geom_violin(fill = "orange") # violin riempitiEntrambi insieme:
Regola pratica: Geoms con area interna usano fill, geoms 1D usano color
Forme visive che rappresentano i dati:
1D (una variabile):
geom_histogram(), geom_density(), geom_dotplot()2D (due variabili):
geom_point() - Scatter plotgeom_line() - Linee connessegeom_smooth() - Linea di tendenzageom_boxplot(), geom_violin() - DistribuzioniConteggi:
geom_bar(), geom_col() - BarreMultipli layer: Combina più geometrie nello stesso grafico!

Ogni geom ha parametri specifici:
# Point: shape, size, alpha
geom_point(shape = 16, size = 3, alpha = 0.7)
# Line: linetype, linewidth
geom_line(linetype = "dashed", linewidth = 1)
# Smooth: method, se (standard error)
geom_smooth(method = "lm", se = TRUE)I geom ereditano le aesthetics da ggplot() ma possono avere le proprie:
Suddividi i dati in sottografici multipli (piccoli multipli)
Potente per:

Opzioni di personalizzazione:
# Scale libere per ogni panel
facet_wrap(~ class, scales = "free") # entrambi liberi
facet_wrap(~ class, scales = "free_x") # solo x libero
facet_wrap(~ class, scales = "free_y") # solo y libero
# Etichette personalizzate
facet_wrap(~ class, labeller = label_both) # mostra "class: suv"
# Margini
facet_grid(drv ~ cyl, margins = TRUE) # aggiunge totali marginaliBest practice: Mantieni scale fisse quando possibile per facilitare confronti!
Trasformazioni sui dati prima di plottare:
Stats comuni:
stat_bin() - Istogrammi (conta in bin)stat_count() - Bar chart (conta osservazioni)stat_smooth() - Modelli (lm, loess, gam)stat_boxplot() - Quartili e outlierstat_summary() - Funzioni custom (media, mediana, etc.)Ogni geom ha uno stat di default:

Usare stats direttamente (invece di geoms):
# Equivalenti
geom_bar(stat = "count")
stat_count(geom = "bar")
# Cambio di stat
geom_bar(stat = "identity") # usa valori y così come sono
# Summary personalizzate
stat_summary(fun = mean, geom = "point", size = 4)
stat_summary(fun.data = mean_se, geom = "errorbar")Variabili calcolate: Gli stats creano nuove variabili accessibili con after_stat():
Scoprire le variabili calcolate
Per vedere quali variabili sono disponibili con after_stat(), consulta la sezione Computed variables nella documentazione di ogni stat: ?stat_bin, ?stat_count, ?stat_smooth, etc.
Il “tessuto” su cui plottiamo - operano dopo scale:
Tipi principali:
coord_cartesian() - Default (x, y piano)coord_flip() - Scambia x ↔︎ ycoord_fixed() - Ratio fisso (utile per mappe)coord_polar() - Coordinate polari (pie chart)coord_map() - Proiezioni geografichecoord_trans() - Trasformazioni non-lineariZoom senza rimuovere dati:

Le scale traducono BIDIREZIONALMENTE tra dati e proprietà visive
Il problema fondamentale:
I tuoi dati raramente rappresentano direttamente proprietà grafiche:
gender → quale forma?temperature (15-35°C) → quale colore?date (2020-01-01) → quale posizione?category (A, B, C) → quale colore?Ogni aesthetics ha la sua scala:
ggplot(data, aes(x = date,
y = temp,
color = city)) +
geom_line()
# ggplot2 applica AUTOMATICAMENTE:
# scale_x_date() # per x
# scale_y_continuous() # per y
# scale_color_discrete() # per colorAutomatiche ma personalizzabili
Le scale vengono sempre applicate, anche se non le specifichi. Specificarle esplicitamente ti dà controllo totale.
Pattern di nomenclatura sistematico:
scale_<aesthetic>_<type>()
Per POSIZIONI (x, y):
# Continuous
scale_x_continuous()
scale_y_continuous()
# Trasformazioni
scale_x_log10() # scala logaritmica
scale_y_sqrt() # radice quadrata
scale_x_reverse() # inverte direzione
# Tipi speciali
scale_x_date() # date/datetime
scale_x_discrete() # categorie
scale_x_binned() # bins per continuousPer COLORE (color/fill):
Per ALTRE AESTHETICS:
# Size
scale_size_continuous(range = c(1, 10))
scale_size_area() # area proporzionale al valore
# Shape
scale_shape_manual(values = c(16, 17, 18))
scale_shape_discrete()
# Alpha (trasparenza)
scale_alpha_continuous(range = c(0.1, 1))
# Linetype
scale_linetype_manual(values = c("solid", "dashed"))Suffissi importanti:
_manual → valori completamente custom_identity → usa valore letterale dai datiComponenti comuni a tutte le scale (tutti opzionali):
scale_color_gradient(
name = "Temperatura (°C)", # Titolo legenda/asse
limits = c(0, 100), # Range dei dati
breaks = seq(0, 100, 25), # Tick marks
labels = c("0°", "25°", "50°", "75°", "100°"),
trans = "log10", # Trasformazione
guide = guide_colorbar( # Tipo di guida
barwidth = 15,
barheight = 0.5
)
)Controllo delle legende/assi:
Trasformazioni comuni:
# Coordinate vs Scale transforms
scale_x_log10() # trasforma DATI (prima stats)
coord_trans(x = "log10") # trasforma VISUAL
# Differenza cruciale:
ggplot(data, aes(x, y)) +
geom_smooth() +
scale_x_log10() # smooth su dati log-transformed
ggplot(data, aes(x, y)) +
geom_smooth() +
coord_trans(x = "log10") # smooth su dati originaliScale types riconoscono automaticamente i dati
ggplot2 sceglie intelligentemente: - factor/character → _discrete() - numeric/integer → _continuous()
- Date/POSIXct → _date()/_datetime()
Ma puoi sempre sovrascrivere manualmente!
Controllo completo su elementi non-data (tutto ciò che non rappresenta dati):
Temi built-in:
theme_gray() # default
theme_minimal() # minimalista
theme_classic() # assi classici
theme_bw() # bianco e nero
theme_dark() # sfondo scuro
theme_void() # vuoto (solo dati)Pacchetti con temi extra:
ggthemes: Economist, FiveThirtyEight, Tuftehrbrthemes: Typography-focusedtvthemes: Simpsons, Game of Thrones…
Personalizzazione con theme() - Categorie principali:
theme(
# Testo
plot.title = element_text(size = 16, face = "bold"),
axis.text = element_text(size = 10),
axis.title.x = element_text(margin = margin(t = 10)),
# Linee
axis.line = element_line(color = "black", linewidth = 0.5),
panel.grid.major = element_line(color = "gray90"),
panel.grid.minor = element_blank(), # rimuovi
# Rettangoli/Sfondi
panel.background = element_rect(fill = "white"),
plot.background = element_rect(fill = "gray95"),
# Legend
legend.position = "bottom", # "none", "left", "right", "top"
legend.title = element_blank()
)Quattro tipi di elementi per personalizzazione completa:
1. element_text() - Testo (titoli, labels, caption):
plot.title = element_text(
family = "Helvetica", face = "bold", size = 16,
color = "steelblue", hjust = 0.5, margin = margin(b = 10)
)2. element_line() - Linee (assi, griglie):
axis.line = element_line(color = "black", linewidth = 1, linetype = "solid")
panel.grid = element_line(color = "gray90", linewidth = 0.5)3. element_rect() - Rettangoli (sfondi, bordi):
panel.background = element_rect(fill = "white", color = "black", linewidth = 1)
legend.background = element_rect(fill = "gray95", color = NA)4. element_blank() - Rimuove elemento:
Gerarchia degli elementi - Da generale a specifico:
theme(
# Livello 1: Globale
text = element_text(family = "Arial", size = 12),
line = element_line(color = "gray30"),
# Livello 2: Categoria
axis.text = element_text(size = 10), # eredita family da text
# Livello 3: Specifico (override)
axis.text.x = element_text(angle = 45, hjust = 1),
axis.text.y = element_text(color = "red")
)Tip: Modifica elementi più generali possibile per consistenza!
Salvare tema custom:
Modifica posizione elementi dentro un layer (risolve overlapping):
# Identity - nessun aggiustamento (dati sovrapposti)
geom_bar(position = "identity", alpha = 0.5)
# Stack - impilati (default per barre)
geom_bar(position = "stack")
# Dodge - affiancati (gruppi side-by-side)
geom_bar(position = "dodge")
# Fill - impilati a 100%
geom_bar(position = "fill")
# Jitter - rumore casuale (scatter plot con sovrapposizione)
geom_point(position = "jitter")
geom_point(position = position_jitter(width = 0.2, height = 0))Fondamentale per visualizzare correttamente dati sovrapposti!
ggplot2 è compositivo - somma layer!
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(aes(color = class)) + # Layer 1
geom_smooth(color = "black") + # Layer 2
geom_rug(sides = "bl") # Layer 3Ogni layer può avere:
- Proprie estetiche
- Propri dati
- Proprie geometrie
Non memorizzare ogni funzione!
Capisci la logica:
Il sistema è consistente e prevedibile
I componenti sono progettati per lavorare insieme:
La grammatica rende ggplot2 intuitivo una volta capito il sistema!
ggplot2 è una piattaforma, non solo un package!
200+ estensioni disponibili:
patchwork - Composizione graficigganimate - Animazioniggrepel - Etichette intelligentiggraph - Network/grafiggridges - Ridge plotsdata = mpg)aes(x, y, color))geom_point())stat_bin())scale_color_*())coord_flip())facet_wrap())+ Theme per aspetto generale!
Libri:
Online:
Video: Thomas Lin Pedersen’s workshop (4.5h)

REVELO Training - Data Viz 2025