Ricorda di scegliere il grafico in base al tipo di variabile:
| Grafico | Variabile | Funzione principale |
|---|---|---|
| Diagramma a barre | Qualitativa | barplot() |
| Grafico a torta | Qualtitativa | pie() |
| Diagramma a punti | Quantitativa | stripchart() |
| Istogramma | Quantitativa continua | hist() |
| Boxplot | Quantitativa (confronto gruppi) | boxplot() |
💡 Se si tratta di variabili discrete (spesso di conteggio) che assumono solo valori bassi, quindi con pochi valori distinti (esempio sono la variabile numero di fratelli, o il numero di sinistri con l’auto che l’assicurato denuncia in un anno) l’uso dei semplici grafici visti per le variabili categoriali si rivela spesso del tutto adeguato.
Consideriamo il data frame Cars93 del pacchetto MASS
(nel seguito ci restringeremo alle analisi di Price e
Length).
stripchartRappresenta su un grafico i singoli valori osservati (quindi non si perde alcun dettaglio). La funzione stripchart() del package graphics può essere usata a tale scopo.
library(MASS)
data("Cars93")
par(mfrow=c(1,2))
stripchart(Cars93$Price, pch=19, main ="Price", xlab = "Price",
method="stack", cex=1.2)
stripchart(Cars93$Length, pch=18, col = "red", main ="Length", xlab = "Length",
method="stack", cex=1.2)Il parametro method=stack consente di sovrapporre dati
osservati che risultano spesso avere il medesimo valore. Il default è
invece quello di mostrare il valore osservato senza dare conto della
eventuale molteplicità.
hist()L’istogramma divide il range di una variabile continua in intervalli (classi) e rappresenta la frequenza di ciascuna classe con un rettangolo proporzionale in altezza (o area, in caso di densità).
Il suo processo di costruzione prevede:
| Intervallo | Frequenza assoluta | Frequenza relativa |
|---|---|---|
| \((a_1, a_2]\) | \(n_1\) | \(f_1\) |
| \((a_2, a_3]\) | \(n_2\) | \(f_2\) |
| \(\vdots\) | \(\vdots\) | \(\vdots\) |
| \((a_i, a_{i+1}]\) | \(n_i\) | \(f_i\) |
| \(\vdots\) | \(\vdots\) | \(\vdots\) |
| \((a_I, a_{I+1}]\) | \(n_I\) | \(f_I\) |
Gli istogrammi si ottengono con hist (nella versione di
default, date \(n\) osservazioni viene
stabilito il numero di classi \(I\)
usando la regola di Sturges e quindi si calcola l’ampiezza di ogni
classe dividenzo il range per il numero di classi; di default vengono
rappresentate le frequenze assolute, poichè freq=T)
par(mfrow=c(1,2))
hist(Cars93$Price,
main = "Distribuzione del Prezzo",
xlab = "Prezzo (migliaia di $)",
ylab = "Frequenza assoluta",
col = "lightblue",
border = "white")
hist(Cars93$Length,
main = "Distribuzione della Lunghezza",
xlab = "Lunghezza (in pollici)",
ylab = "Frequenza assoluta",
col = "lightblue",
border = "white")L’istogramma ci consente di:
💡 La distribuzione di
Priceè fortemente asimmetrica a destra: pochi valori molto elevati allungano la coda. Questo può essere tipico di variabili economiche.
Esempi di Asimmetria/Simmetria
breaksIl numero di classi influenza notevolmente l’aspetto dell’istogramma. Troppo poche classi nascondono la forma della distribuzione; troppo la frammentano:
library(insuranceData)
data("AutoBi")
par(mfrow = c(1, 2))
hist(Cars93$Price,
breaks = 5,
main = "breaks = 5",
xlab = "Prezzo (migliaia di $)",
ylab = "Frequenza assoluta",
col = "lightblue",
border = "white")
hist(Cars93$Price,
breaks = 50,
main = "breaks = 50",
xlab = "Prezzo (migliaia di $)",
ylab = "Frequenza assoluta",
col = "lightblue",
border = "white")Con freq = FALSE sull’asse delle ordinate compare la
densità (area totale = 1). Si può sovrapporre una curva
di densità stimata con density():
hist(Cars93$Price,
freq = FALSE,
breaks = 20,
main = "Istogramma e densità",
xlab = "Prezzo (migliaia di $)",
ylab = "Frequenza assoluta",
col = "lightblue",
border = "white")
lines(density(Cars93$Price, na.rm = TRUE),
col = "darkblue",
lwd = 2)Quando la distribuzione è molto asimmetrica, la trasformazione logaritmica rende la forma più leggibile:
hist(log(Cars93$Price),
freq = FALSE,
main = "Distribuzione di log(Price)",
xlab = "log(Price)",
col = "lightblue",
border = "white")
lines(density(log(Cars93$Price)),
col = "darkblue",
lwd = 2)Se le classi hanno ampiezza diversa, è obbligatorio
usare freq = FALSE (densità): con frequenze assolute, le
classi più ampie apparirebbero artificialmente più alte.
breaks_custom <- c(0, 10, 30, max(Cars93$Price))
hist(Cars93$Price,
breaks = breaks_custom,
freq = FALSE,
main = "Istogramma con classi di ampiezza diversa",
xlab = "Price",
col = "lightblue",
border = "white")⚠️ Con classi di ampiezza uguale
freq = TRUEefreq = FALSEdanno istogrammi con la stessa forma (cambia solo la scala dell’asse y). Con classi di ampiezza diversa, usare semprefreq = FALSEper non distorcere la rappresentazione.
Esercizio 1
Usando la variabile
LOSSdiAutoBi:
Traccia l’istogramma con il numero di classi di default.
Traccia l’istogramma con
breaks = 20e conbreaks = 5affiancati (par(mfrow = ...)).Traccia l’istogramma di
log(LOSS)con la curva di densità sovrapposta.La distribuzione di
LOSSè simmetrica? Come cambia dopo la trasformazione log?
boxplot()Il boxplot (diagramma a scatola e baffi) riassume graficamente cinque statistiche:
┌─────────────┐
──┬────┤ IQR ├────┬── × × (outlier)
│ └─────────────┘ │
Q1-1.5·IQR Q1 Me Q3 Q3+1.5·IQR
| Elemento | Statistica |
|---|---|
| Linea centrale | Mediana (Q2) |
| Bordi della scatola | Primo (Q1) e terzo quartile (Q3) |
| Baffi | Fino a 1.5 × IQR dal bordo della scatola |
| Punti isolati | Outlier (oltre i baffi) |
La definizione di outlier nel diagramma a scatola deriva dalla seguente regola:
sono outlier (valori anomali) quei valori che sono più distanti dai bordi della scatola (cioè dai quartili) più di una volta e mezza lo scarto interquartile IQR. Pertanto tutti i punti che sono superiori a Q3 + 1.5IQR o inferiori a Q1 - 1.5IQR verranno annotati separatamente sul grafico;
se essi esistono (a destra o a sinistra) di conseguenza il baffo non va esteso fino al massimo o al minimo valore osservato, va invece esteso:
– a destra, fino al più grande valore che non sia segnalato come outlier (cioè il massimo valore osservato che risulti inferiore a Q3 + 1.5IQR);
– a sinistra, fino al più piccolo valore che non sia segnalato come outlier (cioè il minimo valore osservato che risulti superiore a Q1 - 1.5IQR).
Le medie di posizione sono valori che hanno una posizione definita rispetto alla sequenza ordinata dei dati. Il concetto chiave è quello di quantile empirico.
Fissata una proporzione \(p\) (con \(0 \leq p \leq 1\)), si definisce \(x_p\) quantile empirico di ordine \(p\) come quel valore \(x \in \mathbb{R}\) tale che:
\[x_p : \frac{\#(x_i \leq x_p)}{n} = p\]
⚠️ Non è detto che esista un unico valore che realizzi la condizione, né che ne esista alcuno. In R si usa l’interpolazione lineare fra i due quantili successivi osservati. Si veda
?quantileper le diverse convenzioni disponibili.
I quantili \(x_{0.25}\), \(x_{0.5}\), \(x_{0.75}\) sono detti rispettivamente primo, secondo e terzo quartile. Il secondo quartile è anche detto mediana (\(Me\)).
La mediana è definita convenzionalmente come:
\[Me = \begin{cases} x_{\left(\frac{n+1}{2}\right)} & \text{se } n \text{ è dispari} \\ \dfrac{x_{\left(\frac{n}{2}\right)} + x_{\left(\frac{n}{2}+1\right)}}{2} & \text{se } n \text{ è pari} \end{cases}\]
La mediana (a differenza) dell a media è molto meno sensibile ai valori estremi.
⚠️ Si ricorda che come per tutte le funzioni statistiche, in presenza di NA occorre usare
na.rm = TRUE
#> [1] 17.7
#> [1] 183
#> 0% 25% 50% 75% 100%
#> 7.4 12.2 17.7 23.3 61.9
#> 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
#> 7.40 9.84 11.34 13.74 15.78 17.70 19.34 20.84 26.22 33.62 61.90
# Verifica: il quantile di ordine 0.5 è la mediana
median(Cars93$Price) == quantile(Cars93$Price, probs = 0.5)#> 50%
#> TRUE
Lo scarto interquartile è una misura di variabilità data dalla differenza fra il terzo e il primo quartile: \[IQR = Q_3 - Q_1\]
Tiene conto solo della dispersione nella parte centrale della
distribuzione (il 50% dei dati centrali), ignorando i valori estremi. È
leggibile direttamente dall’output di summary() ma esiste
una funzione dedicata IQR() per il suo calcolo.
#> [1] 11.1
#> [1] 18
La funzione per ottenere un boxplot in R è
boxplot().
boxplot(Cars93$Price,
main = "Boxplot del Prezzo",
ylab = "Prezzo (migliaia di $)",
col = "lightblue")L’uso più efficace del boxplot è il confronto della
distribuzione di una variabile quantitativa tra i gruppi definiti da una
variabile qualitativa. Si usa la notazione formula
y ~ gruppo e l’argomento data permette di
evitare l’accesso mediante dollaro alle variabili. L’argomento
las = 2 chepermette di ruotare le etichette sull’asse x
(utile quando sono lunghe)
boxplot(Price ~ Type,
data = Cars93,
main = "Prezzi per tipo di auto",
xlab = "Tipo",
ylab = "Prezzi (migliaia di $)",
las = 2)L’argomento notch = TRUE aggiunge le incisioni per il
confronto visivo delle mediane.
boxplot(Price ~ Origin,
data = Cars93,
main = "Prezzi per origine",
xlab = "Origine",
ylab = "Prezzi",
col = c("lightblue", "lightpink"),
notch = TRUE)💡 Se le incisioni di due boxplot non si sovrappongono, c’è evidenza visiva che le mediane siano diverse. In questo caso non vi è evidenza visiva che le mediane siano diverse.
Esercizio 2
Usando
AutoBi:
Costruisci i boxplot di
LOSSelog(LOSS)Costruisci i boxplot di
log(LOSS)separatamente per Ricorso all’avvocato (ATTORNEY). Ci sono differenze tra i danni nei due gruppi? Questo conferma quanto visto rendendoLOSSin factorCostruisci i boxplot di
CLMAGEper le combinazioni di sesso e avvocato usandointeraction(CLMSEX, ATTORNEY).
ecdf()Una modo per rappresentare graficamente un insieme di dati è quello di ottenere una versione empirica della funzione di ripartizione di una variabile aleatroria \(X\), cioè \(F(x) = Pr(X \leq x)\). Inoltre l’inversa della funzione di ripartizione fornisce i quantili per cui il quantile \(x_p = F^{-1}(p)\). Ovviamente il quantile per ogni \(0 < p < 1\) esiste se la funzione di ripartizione non ha discontinuità.
L’equivalente empirico di tale funzione, denotato con \(F_n(x)\), avendo osservato l’insieme di dati \(x_1, x_2, \ldots , x_n\), per una variabile quantitativa, si ottiene cercando il valore che \(x \in \mathbb{R}\) fornisce la proporzione di unità inferiori o pari a \(x\), ovvero
\[F_n(x) = \text{proporzione}(x_i \leq x) = \frac{\text{numero di valori} \leq x}{n}\]
Considerando i dati ordinati \(x_{(1)} < x_{(2)} < \ldots < x_{(n-1)} < x_{(n)}\) la funzione di ripartizione empirica (cumulata empirica) sarà pari a:
Si tratta quindi di una funzione a scalini definita come: \[F_n(x) = \begin{cases} 0 \quad \text{se} \quad x < x(1) \\ i/n \quad \text{se} \quad x_{(i)} \leq x < x(i+1) \quad i = 1, 2, \ldots , n - 1 \\ 1 \quad \text{se} \quad x \geq x_{(n)} \end{cases}\]
In R otteniamo la funzione di ripartizione empirica mediante
ecdf() che è basata sulla funzione stepfun() che è
appositamente costruita per trattare funzioni a gradini.
Esempio
par(mfrow=c(1,2))
# consideriamo l!insieme di 9 dati nel vettore xx
xx<-c(148, 172, 155, 168, 162, 175, 178, 186, 174)
sort(xx)#> [1] 148 155 162 168 172 174 175 178 186
Nel caso della lunghezza delle auto, si ha
plot(ecdf(Cars93$Length), main="funzione di ripartizione empirica per lunghezza",
xlab="lunghezza", ylab="proporzione di casi")Tale rappresentazione grafica è di interesse per vari motivi:
plot(ecdf(Cars93$Length), main="funzione di ripartizione empirica per lunghezza e mediana",
xlab="lunghezza", ylab="proporzione di casi")
segments(130, 0.5,
median(Cars93$Length), 0.5,
col = "red", lty = "dashed")
segments(median(Cars93$Length), 0,
median(Cars93$Length), 0.5,
col = "red", lty = "dashed")Scegliere il grafico in base al tipo di variabile
Istogramma: il parametro breaks
influenza molto l’aspetto — troppo poche classi nascondono la forma,
troppe la frammentano. Il default è un buon punto di partenza.
Con classi di ampiezza diversa usare sempre
freq = FALSE
freq = FALSE per sovrapporre curve di
densità: lines(density(...)) richiede che l’asse y
dell’istogramma sia in scala di densità, non di frequenze
assolute.
Trasformazione logaritmica: quando la distribuzione è molto asimmetrica a destra, lavorare su scala logaritmica rende la forma più leggibile e i confronti più efficaci.
Boxplot con notch = TRUE: se le
incisioni di due boxplot non si sovrappongono, c’è evidenza visiva che
le mediane differiscano.
Boxplot per gruppi: usare la notazione formula
y ~ gruppo e l’argomento data per evitare di
ripetere il nome del data frame. Con
interaction(var1, var2) si ottengono boxplot per tutte le
combinazioni di due fattori.
ECDF: conserva l’informazione su ogni singolo valore, è adatta a variabili discrete, e permette di leggere visivamente i quantili come intersezioni con linee orizzontali (es. mediana a \(y = 0.5\)).
par(mfrow = c(r, c)) per affiancare
più grafici; ricordarsi di ripristinare con
par(mfrow = c(1, 1)) al termine.
# DIAGRAMMA A PUNTI
stripchart(x, method = "stack", pch = 19, main = "...", xlab = "...") #stack per punti impilati
# ISTOGRAMMA
hist(x) # frequenze assolute, breaks di Sturges
hist(x, breaks = 20) # numero di classi personalizzato (indicazione)
hist(x, freq = FALSE) # densità di frequenza (area totale = 1)
hist(x, freq = FALSE, breaks = c(...), border = "white") # classi di ampiezza diversa → freq=FALSE obbligatorio
lines(density(x, na.rm = TRUE), col = "blue", lwd = 2) # curva densità sovrapposta
# TRASFORMAZIONE LOG
hist(log(x), freq = FALSE)
lines(density(log(x)), col = "blue", lwd = 2)
# MEDIANA, QUARTILI, IQR
median(x, na.rm = TRUE)
quantile(x, na.rm = TRUE) # min, Q1, Me, Q3, max
quantile(x, probs = seq(0, 1, 0.1), na.rm = TRUE) # decili
IQR(x, na.rm = TRUE) # scarto interquartile
# BOXPLOT
boxplot(x, main = "...", ylab = "...", col = "lightblue")
boxplot(y ~ gruppo, data = df, las = 2) # per gruppi
boxplot(y ~ gruppo, data = df, notch = TRUE) # con incisioni per mediane
boxplot(y ~ interaction(g1, g2), data = df) # per combinazioni di gruppi
# ECDF
plot(ecdf(x)) # funzione di ripartizione empirica
# Aggiungere segmenti
segments(x0, y0, x1, y1, col = "red", lty = "dashed")
# LAYOUT MULTIPLO
par(mfrow = c(r, c)) # griglia r righe × c colonne
par(mfrow = c(1, 1)) # ripristina grafico singolo