Il presente report analizza il Boston Housing Dataset, un insieme di dati che raccoglie informazioni sulle caratteristiche strutturali, ambientali e socio-economiche delle abitazioni, nonché sul prezzo medio degli immobili in diverse aree della città di Boston. L’obiettivo principale dell’analisi è individuare i fattori che influenzano il valore delle case e sviluppare un modello predittivo affidabile in grado di stimarne il prezzo.
Il dataset è stato originariamente introdotto da Harrison e Rubinfeld (1978) ed è stato successivamente utilizzato da Belsley, Kuh e Welsch (1980) nel contesto dello studio dei modelli di regressione. Per il presente lavoro, i dati sono stati reperiti dalla piattaforma Kaggle (link: https://www.kaggle.com/datasets/mdraselsarker/boston-housing-dataset?resource=download).
L’analisi si articola in diverse fasi. In primo luogo, viene condotta un’esplorazione preliminare dei dati (Exploratory Data Analysis, EDA) al fine di comprendere la distribuzione delle variabili e le relazioni esistenti. Successivamente, si applica un’analisi delle componenti principali (PCA) per ridurre la dimensionalità del dataset, seguita da un’analisi dei cluster volta a individuare gruppi omogenei di aree urbane. Infine, viene implementato un modello di regressione lineare multipla per stimare il prezzo degli immobili e valutarne le performance predittive.
Librerie utilizzate:
library(ggplot2)
library(corrplot)
library(dplyr)
library(cluster)
library(factoextra)
library(car)
library(reshape2)
library(sandwich)
library(lmtest)
L’analisi ha inizio con una Exploratory Data Analysis (EDA), finalizzata a una prima esplorazione dei dati. In questa fase preliminare è stata innanzitutto esaminata la struttura del dataset, verificando la tipologia delle variabili.
setwd("C:/Users/Sara/Desktop/Report")
df = read.csv("Boston.csv")
dim(df)
## [1] 506 14
names(df)
## [1] "CRIM" "ZN" "INDUS" "CHAS" "NOX" "RM" "AGE"
## [8] "DIS" "RAD" "TAX" "PTRATIO" "B" "LSTAT" "Price"
Il dataset è composto da 506 osservazioni e 14 variabili, di cui una è la variabile target, rappresentata da Price che indica il prezzo medio delle abitazioni occupate dai proprietari, espresso in migliaia di dollari. Le altre variabili sono:
CRIM: tasso di criminalità pro capite nella zona;
ZN: percentuale di terreno residenziale destinato a lotti di grandi dimensioni;
INDUS: quota di territorio occupata da attività industriali non commerciali;
CHAS: indica la presenza o meno del confine con il fiume Charles (1 se presente, 0 altrimenti);
NOX: concentrazione di ossidi di azoto nell’aria;
RM: numero medio di stanze per abitazione;
AGE: percentuale di abitazioni costruite prima del 1940;
DIS: distanza media ponderata dai principali centri di lavoro;
RAD: grado di accessibilità alle autostrade radiali, ovvero alle principali strade che collegano le zone periferiche al centro;
TAX: aliquota dell’imposta sugli immobili per ogni 10.000 dollari di valore;
PTRATIO: rapporto alunni/insegnanti nelle scuole;
B: indice sociodemografico che dipende dalla proporzione di popolazione afroamericana presente nella zona;
LSTAT: percentuale di popolazione appartenente a fasce sociali più svantaggiate;
sapply(df, class)
## CRIM ZN INDUS CHAS NOX RM AGE DIS
## "numeric" "numeric" "numeric" "integer" "numeric" "numeric" "numeric" "numeric"
## RAD TAX PTRATIO B LSTAT Price
## "integer" "integer" "numeric" "numeric" "numeric" "numeric"
Il dataset è composto da 11 variabili numeriche continue e 3 variabili discrete (TAX, RAD), di cui una dicotomica (CHAS).
colSums((is.na(df))) #verifico se ci sono valori nulli
## CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX
## 0 0 0 0 0 0 0 0 0 0
## PTRATIO B LSTAT Price
## 0 0 0 0
sum(duplicated((df))) #verifico se ci sono valori duplicati
## [1] 0
Contestualmente, è stata verificata la presenza di valori mancanti o osservazioni duplicate, che non sono risultati presenti nel dataset.
Successivamente, attraverso il calcolo delle principali statistiche descrittive, ottenute mediante la funzione summary(), è stato possibile analizzare le caratteristiche principali del dataset.
summary(df)
## CRIM ZN INDUS CHAS
## Min. : 0.00632 Min. : 0.00 Min. : 0.46 Min. :0.00000
## 1st Qu.: 0.08205 1st Qu.: 0.00 1st Qu.: 5.19 1st Qu.:0.00000
## Median : 0.25651 Median : 0.00 Median : 9.69 Median :0.00000
## Mean : 3.61352 Mean : 11.36 Mean :11.14 Mean :0.06917
## 3rd Qu.: 3.67708 3rd Qu.: 12.50 3rd Qu.:18.10 3rd Qu.:0.00000
## Max. :88.97620 Max. :100.00 Max. :27.74 Max. :1.00000
## NOX RM AGE DIS
## Min. :0.3850 Min. :3.561 Min. : 2.90 Min. : 1.130
## 1st Qu.:0.4490 1st Qu.:5.886 1st Qu.: 45.02 1st Qu.: 2.100
## Median :0.5380 Median :6.208 Median : 77.50 Median : 3.207
## Mean :0.5547 Mean :6.285 Mean : 68.57 Mean : 3.795
## 3rd Qu.:0.6240 3rd Qu.:6.623 3rd Qu.: 94.08 3rd Qu.: 5.188
## Max. :0.8710 Max. :8.780 Max. :100.00 Max. :12.127
## RAD TAX PTRATIO B
## Min. : 1.000 Min. :187.0 Min. :12.60 Min. : 0.32
## 1st Qu.: 4.000 1st Qu.:279.0 1st Qu.:17.40 1st Qu.:375.38
## Median : 5.000 Median :330.0 Median :19.05 Median :391.44
## Mean : 9.549 Mean :408.2 Mean :18.46 Mean :356.67
## 3rd Qu.:24.000 3rd Qu.:666.0 3rd Qu.:20.20 3rd Qu.:396.23
## Max. :24.000 Max. :711.0 Max. :22.00 Max. :396.90
## LSTAT Price
## Min. : 1.73 Min. : 5.00
## 1st Qu.: 6.95 1st Qu.:17.02
## Median :11.36 Median :21.20
## Mean :12.65 Mean :22.53
## 3rd Qu.:16.95 3rd Qu.:25.00
## Max. :37.97 Max. :50.00
Dall’analisi descrittiva emerge che la variabile CHAS presenta una distribuzione fortemente sbilanciata: la maggior parte delle osservazioni corrisponde ad abitazioni che non confinano con il fiume (0), mentre solo una piccola parte ha valore 1. Prima della visualizzazione, la variabile è stata trasformata in un fattore dicotomico con due livelli espliciti:
“No” per le abitazioni non confinanti;
“Si” per quelle che si affacciano sul fiume.
df$CHAS <- factor(df$CHAS, levels = c(0,1), labels = c("No", "Si"))
ggplot(df, aes(x=CHAS)) +
geom_bar(fill = "skyblue", color = "black") +
labs(title = "Distribuzione della variabile CHAS",
x = "Confina con il fiume Charles",
y = "Numero di abitazioni") +
theme_minimal()
Il grafico a barre conferma la distribuzione sbilanciata, in cui la maggioranza delle abitazioni appartiene alla categoria “No”, mentre solo una piccola quota si affaccia sul fiume.
par(mfrow = c(1, 4))
boxplot(df$CRIM, main='CRIM',col='Sky Blue')
boxplot(df$ZN, main='ZN',col='Sky Blue')
boxplot(df$RM, main='RM',col='Sky Blue')
boxplot(df$B, main='B',col='Sky Blue')
L’analisi grafica mediane boxplot evidenzia caratteristiche distintive anche nelle altre principali variabili. La variabile CRIM presenta una distribuzione fortemente asimmetrica a destra, con una mediana bassa (0.25) e numerosi valori estremi (outlier) che arrivano quasi fino ad 89. Ciò indica che, pur essendoci molte zone con un basso tasso di criminalità, esistono alcune aree con valori eccezionalmente elevati.
La variabile ZN mostra invece una concentrazione la maggior parte dei valori su 0 (nel summary sia la mediana che il primo quartile sono uguali a 0), con pochi casi isolati di valori elevati, rappresentati come outlier nel boxplot. Ciò suggerisce che la gran parte delle abitazioni non si trova in zone con ampie aree residenziali.
Per quanto riguarda RM, la distribuzione risulta più simmetrica e compatta, con mediana e media intorno a 6.2 e pochi outlier. La concentrazione dei dati attorno a un valore centrale indica una relativa omogeneità nella dimensione degli alloggi.
Infine, la variabile B presenta valori medi elevati (~356) ma con ampia variabilità, come evidenziato da numerosi outlier verso il basso.
par(mfrow = c(2, 4), mar = c(4,4,2,1))
hist(df$INDUS, main = "INDUS", col = "lightblue", breaks = 20, border = "black")
hist(df$TAX, main = "TAX", col = "lightgreen", breaks = 20, border = "black")
hist(df$PTRATIO, main = "PTRATIO", col = "pink",breaks = 20, border = "black")
hist(df$NOX, main = "NOX", col = "lightyellow", breaks = 20, border = "black")
hist(df$AGE, main = "AGE", col = "lightgray", breaks = 20, border = "black")
hist(df$DIS, main = "DIS", col = "orange",breaks = 20, border = "black")
hist(df$RAD, main = "RAD", col = "lightcoral",breaks = 20, border = "black")
hist(df$LSTAT, main = "LSTAT", col = "lightcyan", breaks = 20, border = "black")
Successivamente ho esaminato la distribuzione delle altre variabili del dataset tramite istogrammi. I risultati evidenziano i seguenti aspetti:
INDUS: mostra una distribuzione con valori prevalentemente bassi e un picco intorno a 18- 20, suggerendo la presenza di alcune zone fortemente industrializzate.
TAX: presenta una distribuzione bimodale con due gruppi distinti, uno con valori intorno a 300-400 e un altro con un picco marcato a 666, indicativo di differenze nella pressione fiscale tra aree.
PTRATIO: si concentra principalmente tra 17 e 21, suggerendo una certa omogeneità in questo fattore educativo con variazioni limitate.
NOX: evidenzia una distribuzione asimmetrica verso valori bassi, con una coda verso l’alto, riflettendo vari livelli di inquinamento nelle diverse zone.
AGE: è sbilanciata verso valori elevati, indicando che molte case sono piuttosto vecchie.
DIS: presenta una distribuzione a destra con una lunga coda, suggerendo che alcune abitazioni si trovano a distanze maggiori dai centri di lavoro.
RAD:ha un picco netto sul valore massimo 23, con il resto delle osservazioni distribuite tra valori più bassi, riflettendo una distinzione tra aree altamente accessibili e meno.
LSTAT: tende a valori bassi ma con una coda verso valori più alti, indicando la presenza di zone socio-economicamente svantaggiate.
df %>%
ggplot(aes(x = Price)) +
geom_histogram(
aes(y = after_stat(density)), bins = 30, fill = "coral", color = "black", alpha = 0.6) +
geom_density(
color = "darkred", linewidth = 1.2) +
labs(
title = "Distribuzione di Price",
x = "Price",
y = "Densità") +
theme_bw()
Dal grafico della variabile Price si osserva una distribuzione unimodale con un picco intorno a valori di prezzo tra 20 e 25, indicando che la maggior parte degli immobili si colloca in questa fascia di prezzi. La presenza di una coda destra più estesa suggerisce una discreta asimmetria positiva, con alcuni immobili aventi valori di prezzo più elevati rispetto alla media.
Data la presenza d asimmetrie significative in diverse variabili del dataset, è stata condotta un’analisi preliminae degli outlier per identificare valori anomali che potessero influenzare l’interpretazione dei risultati. Per individuare i valori anomali nelle variabili numeriche del dataset, è stato utilizzato il metodo dell’intervallo interquartile (IQR), in cui si è estratto il sottoinsieme delle colonne numeriche del dataset originale. Per ogni variabile numerica x sono stati calcolati:
Il primo quartile (Q1), ovvero il valore sotto cui si trova il 25% dei dati.
Il terzo quartile (Q3), ovvero il valore sotto cui si trova il 75% dei dati.
L’intervallo interquartile (IQR) come differenza tra Q3 e Q1.
Per individuare i valori anomali (outlier), si estendono i limiti naturali dei dati oltre il 25° e 75° percentile, calcolando:
lower = Q1 - 1.5 x IQR
upper = Q3 + 1.5 x IQR
Infine si sono contati i valori al di fuori di questi limiti.
df_num <- df%>%select_if(is.numeric)
outliers <- sapply(df_num, function(x) {
Q1 <- quantile(x, 0.25, na.rm= TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQRV <- Q3 - Q1
lower <- Q1- 1.5 * IQRV
upper <- Q3 + 1.5 * IQRV
sum(x<lower | x > upper, na.rm = TRUE)
})
outliers
## CRIM ZN INDUS NOX RM AGE DIS RAD TAX PTRATIO
## 66 68 0 0 30 0 5 0 0 15
## B LSTAT Price
## 77 7 40
L’individuazione dei valori anomali e stata effettuata sulle variabili numeriche utilizzando il metodo dell intervallo interquartile IQR. Per ciascuna variabile sono stati calcolati il primo quartile Q1 e il terzo quartile Q3 e l intervallo interquartile e stato definito come la differenza tra Q3 e Q1. I valori sono stati considerati outlier quando risultavano inferiori al limite inferiore o superiori al limite superiore determinati a partire dall IQR.
Per gestire i valori anomali nelle variabili numeriche, è stato applicato un procedimento di capping ai percentili 1° e 99°. Questo metodo consiste nel sostituire tutti i valori inferiori al 1° percentile con il valore stesso del 1° percentile e, analogamente, sostituire tutti i valori superiori al 99° percentile con il valore limite corrispondente.In questo modo si “limita” l’estensione dei valori estremi senza eliminarli, preservando la struttura generale dei dati ma riducendo l’impatto degli outlier sulle analisi e sui modelli.
df_num_capped <- as.data.frame(lapply(df_num, function(x) {
low_limit <- quantile(x, probs = 0.01)
high_limit <- quantile(x, probs = 0.99)
x <- ifelse(x < low_limit, low_limit,
ifelse(x > high_limit, high_limit, x))
return(x)
}))
summary(df_num_capped)
## CRIM ZN INDUS NOX
## Min. : 0.01361 Min. : 0.0 Min. : 1.254 Min. :0.3980
## 1st Qu.: 0.08205 1st Qu.: 0.0 1st Qu.: 5.190 1st Qu.:0.4490
## Median : 0.25651 Median : 0.0 Median : 9.690 Median :0.5380
## Mean : 3.37518 Mean :11.3 Mean :11.119 Mean :0.5548
## 3rd Qu.: 3.67708 3rd Qu.:12.5 3rd Qu.:18.100 3rd Qu.:0.6240
## Max. :41.37033 Max. :90.0 Max. :25.650 Max. :0.8710
## RM AGE DIS RAD
## Min. :4.524 Min. : 6.61 Min. :1.207 Min. : 1.000
## 1st Qu.:5.886 1st Qu.: 45.02 1st Qu.:2.100 1st Qu.: 4.000
## Median :6.208 Median : 77.50 Median :3.207 Median : 5.000
## Mean :6.287 Mean : 68.58 Mean :3.779 Mean : 9.549
## 3rd Qu.:6.623 3rd Qu.: 94.08 3rd Qu.:5.188 3rd Qu.:24.000
## Max. :8.335 Max. :100.00 Max. :9.223 Max. :24.000
## TAX PTRATIO B LSTAT
## Min. :188.0 Min. :13.00 Min. : 6.73 Min. : 2.883
## 1st Qu.:279.0 1st Qu.:17.40 1st Qu.:375.38 1st Qu.: 6.950
## Median :330.0 Median :19.05 Median :391.44 Median :11.360
## Mean :407.8 Mean :18.45 Mean :356.72 Mean :12.642
## 3rd Qu.:666.0 3rd Qu.:20.20 3rd Qu.:396.23 3rd Qu.:16.955
## Max. :666.0 Max. :21.20 Max. :396.90 Max. :33.919
## Price
## Min. : 7.01
## 1st Qu.:17.02
## Median :21.20
## Mean :22.54
## 3rd Qu.:25.00
## Max. :50.00
par(mfrow = c(2,3))
par(cex=0.6)
vars <- c("CRIM",
"ZN",
"RM",
"Price",
"B",
"DIS")
for (v in vars){
qqnorm(df_num_capped[[v]],
main = paste("Q-Q Plot per", v))
qqline(df_num_capped[[v]], col="mediumpurple") }
Nonostante l’applicazione del capping per limitare l’effetto degli outlier estremi, diverse variabili continuano a mostrare una forte asimmetria nelle loro distribuzioni, come evidenziato dai QQ plot. Questa situazione è probabilmente dovuta alla natura intrinseca delle variabili, che presentano distribuzioni fortemente sbilanciate con code pesanti o valori concentrati in fasce ristrette. Il capping infatti riduce solo l’impatto degli outlier più estremi, senza modificare la forma complessiva della distribuzione.
In questo caso si considera l’esclusione di queste variabili particolarmente problematiche per migliorare la qualità complessiva del modello.
vars <- df_num_capped[, c("NOX", "RM", "LSTAT", "PTRATIO", "Price", "INDUS", "TAX", "AGE", "DIS")]
ggplot(df, aes(x = CHAS, y = Price)) +
geom_boxplot(fill = "lightgreen") +
labs(title = "Distribuzione del prezzo in base a CHAS",
x = "Confina con fiume Charles",
y = "Prezzo") +
theme_minimal()
L’analisi bivariata tra il prezzo delle abitazioni e la variabile CHAS evidenzia differenze significative in funzione della vicinanza al fiume Charles. Il boxplot mostra come le abitazioni situate in prossimità del fiume presentino una mediana del prezzo più elevata rispetto a quelle che non confinano con il fiume. In particolare:
le abitazioni confinanti con il fiume: hanno una mediana di prezzo superiore a 25 e una maggiore variabilità;
le abitazioni non confinanti hanno mediana intorno a 20 con una distribuzione più concentrata e la presenza di alcuni outlier rappresentativi di prezzi elevati ma meno frequenti.
cor_matrix <- cor(vars)
corrplot(
cor_matrix,
method = "color",
type = "upper",
addCoef.col = "black",
number.cex = 0.7,
tl.cex = 0.8,
diag = FALSE,
col = colorRampPalette(c("steelblue", "white", "firebrick"))(200)
)
La matrice di correlazione evidenzia diverse relazioni significative tra le variabili analizzate. Si osservano correlazioni positive molto forti tra l’indice industriale (INDUS) e il livello di inquinamento da ossidi di azoto (NOX) (r = 0.77), nonché tra INDUS e l’imposta sulla proprietà (TAX) (r = 0.65). Questi risultati indicano che le aree con maggiore attività industriale tendono a presentare livelli di inquinamento e tassazioni più elevati.
Il numero medio di stanze per abitazione (RM) risulta fortemente correlato negativamente con NOX (r = -0.62) e con la percentuale di popolazione a basso status socioeconomico (LSTAT) (r = -0.36), mentre è positivamente correlato con il prezzo delle abitazioni (Price) (r = 0.71), suggerendo che abitazioni più spaziose sono associate a zone meno inquinate, con popolazioni meno svantaggiate e con valori immobiliari maggiori. Il prezzo delle abitazioni presenta inoltre una correlazione negativa marcata con LSTAT (r = -0.74), sottolineando come aree con popolazioni a basso status siano associate a prezzi inferiori.
Altre relazioni significative includono la correlazione negativa tra il rapporto studenti-insegnanti (PTRATIO) e il prezzo (r = -0.48) e la correlazione positiva tra l’età delle abitazioni (AGE) e le imposte (TAX) (r = 0.51).
Per migliorare l’interpretazione delle relazioni tra variabili nel matrix plot, è stata implementata una funzione personalizzata che sostituisce il classico scatter plot nel pannello superiore con la visualizzazione del coefficiente di correlazione di Pearson. La funzione calcola il valore della correlazione tra le due variabili considerando solo le osservazioni complete, formattando il risultato con due cifre decimali per una migliore leggibilità. Il valore numerico viene mostrato centralmente nel pannello, e assume colore rosso quando la correlazione è forte (valore assoluto superiore a 0.7), altrimenti viene visualizzato in nero, favorendo così un’immediata individuazione delle associazioni significative.
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor = 1.2, ...) {
usr <- par("usr"); on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
r <- cor(x, y, use = "complete.obs")
txt <- formatC(r, format = "f", digits = digits)
txt <- paste0(prefix, txt)
col <- ifelse(abs(r) > 0.7, "red", "black")
text(0.5, 0.5, txt, cex = cex.cor, col = col)
}
pairs(vars, upper.panel = panel.cor, lower.panel = panel.smooth)
Nel pairs plot sono rappresentate le principali relazioni bivariate tra le variabili del dataset. Nei pannelli inferiori sono riportati gli scatter plot con la curva di regressione locale (loess), che evidenziano la direzione e la forma delle relazioni tra ogni coppia di variabili. Ad esempio, si osserva una relazione inversa tra la percentuale di popolazione a basso status economico (LSTAT) e il prezzo dell’abitazione (Price). Al contrario, il numero medio di stanze per abitazione (RM) risulta correlato positivamente con il prezzo.
Nei pannelli superiori sono invece visualizzati i coefficienti di correlazione di Pearson fra le variabili, con valori numerici arrotondati a due cifre decimali. Le correlazioni più forti (valore assoluto superiore a 0.7) sono evidenziate in rosso per facilitarne l’individuazione. Tra queste si segnalano:
Una forte correlazione positiva tra NOX (ossidi di azoto) e INDUS (percentuale di terreno industriale), che indica che aree più industrializzate presentano livelli maggiori di inquinanti atmosferici (r = 0.77).
Una correlazione negativa marcata tra NOX e DIS (distanza dai centri di lavoro) (r = -0.78), a indicare che più si è lontani dalle aree di attività industriale, minore è il livello di NOX.
L’Analisi delle Componenti Principali (PCA) è una tecnica di riduzione della dimensionalità ampiamente utilizzata nell’analisi multivariata dei dati. Essa consente di sintetizzare l’informazione contenuta in un set di variabili correlate trasformandole in un numero minore di variabili, chiamate componenti principali, che sono combinazioni lineari ortogonali delle variabili originali.
Le componenti principali sono ordinate in modo tale da catturare progressivamente la massima varianza dei dati, facilitando così la visualizzazione, l’interpretazione e la successiva modellazione, soprattutto quando il dataset presenta elevate correlazioni tra le variabili o un numero molto grande di feature.
Un passaggio cruciale nella PCA è la standardizzazione delle variabili, ovvero la trasformazione delle variabili affinché abbiano media zero e varianza uno. Questo è particolarmente importante quando le variabili originali sono misurate su scale differenti, poiché la PCA è sensibile alla scala dei dati e tenderebbe a privilegiare le variabili con varianza maggiore, distorcendo i risultati.
La standardizzazione garantisce che ogni variabile contribuisca in modo equo alla determinazione delle componenti principali, evitando preponderanze dovute all’unità di misura o alla dispersione.
# Standardizza
vars_scaled <- scale(vars)
pca_result <- prcomp(vars_scaled, center = TRUE, scale. = TRUE)
summary(pca_result)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 2.2566 1.2234 0.89421 0.69859 0.60157 0.46882 0.45725
## Proportion of Variance 0.5658 0.1663 0.08885 0.05423 0.04021 0.02442 0.02323
## Cumulative Proportion 0.5658 0.7321 0.82098 0.87520 0.91541 0.93983 0.96307
## PC8 PC9
## Standard deviation 0.4399 0.37272
## Proportion of Variance 0.0215 0.01544
## Cumulative Proportion 0.9846 1.00000
Nel nostro caso, la prima componente principale (PC1) spiega circa il 56.6% della varianza totale, una quota significativa del dataset. La seconda componente (PC2) aggiunge un ulteriore 16.6%. Insieme, le prime due componenti spiegano oltre il 73% della varianza complessiva, indicando che gran parte dell’informazione multidimensionale è sintetizzata efficacemente in uno spazio bidimensionale. Le componenti dalla terza in poi spiegano porzioni di varianza sempre più piccole, con la terza componente che contribuisce con circa il 8.9%.
fviz_eig(pca_result, addlabels = TRUE, ylim = c(0, 60),
main = "Varianza cumulativa spiegata")
Il grafico della varianza cumulativa spiegata (scree plot) mostra chiaramente come la prima componente (PC1) catturi la maggior parte dell’informazione (56.6%), seguita da un deciso calo nell’apporto della seconda componente (16.6%). Usando il metodo del gomito, si identifica il punto in cui l’incremento aggiuntivo di varianza spiegata si riduce sensibilmente: in questo caso, dopo la seconda componente la pendenza della curva si appiattisce notevolmente. Pertanto, si decide di mantenere le prime due componenti principali che insieme spiegano oltre il 73% della varianza totale, garantendo un buon compromesso tra la semplicità del modello e la quantità di informazione preservata.
pca_result$rotation
## PC1 PC2 PC3 PC4 PC5 PC6
## NOX 0.3723236 -0.30201554 0.14179403 -0.19905540 0.0167699 -0.426349270
## RM -0.2596499 -0.50730325 -0.36800742 0.04628507 -0.5730561 -0.215483672
## LSTAT 0.3685906 0.20284296 0.28412033 0.08272247 -0.4309880 0.526469680
## PTRATIO 0.2314461 0.34286204 -0.75118885 0.41425778 0.1147280 -0.026993201
## Price -0.3172504 -0.47054010 -0.12578240 0.03947401 0.3043325 0.598369196
## INDUS 0.3841968 -0.15452358 -0.09661700 -0.25598099 0.3345003 -0.123414454
## TAX 0.3462726 -0.07960311 -0.39583576 -0.59830046 -0.2319013 0.323760840
## AGE 0.3470844 -0.29193883 0.12929442 0.52557646 -0.2843284 -0.007458728
## DIS -0.3404459 0.39481632 -0.02994807 -0.28018496 -0.3650791 -0.125461522
## PC7 PC8 PC9
## NOX -0.29598396 -0.21490151 0.628163232
## RM 0.33025503 -0.22737089 -0.045399789
## LSTAT 0.33903503 -0.29073126 0.270255324
## PTRATIO -0.02364551 -0.08064199 0.268385475
## Price -0.03917119 0.14642605 0.432349206
## INDUS 0.71956168 0.32978628 -0.002142242
## TAX -0.35617076 0.11082490 -0.247729999
## AGE -0.19377186 0.61251574 -0.087949721
## DIS 0.06413051 0.54169619 0.449717000
I coefficienti delle variabili, noti come loadings, rappresentano i pesi con cui ciascuna variabile originaria contribuisce alle componenti principali derivate dalla PCA. Questi coefficienti indicano quanto e in che direzione una variabile influenza ciascuna componente.
Per esempio, nella prima componente (PC1), sono importanti variabili come NOX, LSTAT, INDUS e TAX con segno positivo, mentre RM e DIS hanno un valore negativo. Questo significa che PC1 rappresenta un insieme di caratteristiche che vanno da zone più inquinate e con condizioni peggiori a zone con case più grandi e più distanti dall’inquinamento.
La seconda componente (PC2) ha invece carichi negativi su RM e Price e positivi su DIS e PTRATIO, indicando una diversa combinazione di variabili legate alla qualità abitativa e alla distanza dai servizi.
# PC1 vs PC2
fviz_pca_var(pca_result,
axes = c(1, 2),
col.var = "contrib",
gradient.cols = c("blue", "yellow", "red"),
repel = TRUE,
title = "Mappa variabili - PC1 vs PC2")
Il grafico rappresenta la proiezione delle variabili originali nello spazio definito dalle prime due componenti principali (PC1 e PC2) della PCA. Queste due componenti spiegano complessivamente circa il 73% della varianza totale, fornendo una sintesi efficace delle informazioni multidimensionali.
Le frecce indicano la direzione e l’intensità della correlazione di ciascuna variabile con le due componenti: frecce più lunghe corrispondono a variabili maggiormente correlate con la componente rappresentata sull’asse, mentre l’angolo tra due frecce segnala la relazione di correlazione tra due variabili (freccia parallela = correlazione positiva, freccia opposta = correlazione negativa, angolo retto = nessuna correlazione).
Dal grafico emergono diverse osservazioni rilevanti:
Variabili come NOX, INDUS, AGE e TAX sono fortemente correlate positivamente con PC1, indicando che questa componente sintetizza caratteristiche legate a inquinamento, presenza di industrie e vecchiaia delle abitazioni.
RM e Price, allineate in direzione opposta a queste variabili, mostrano un’associazione inversa con PC1, suggerendo che le abitazioni con più stanze e prezzo elevato sono generalmente associate a condizioni migliori e meno inquinate.
La variabile PTRATIO si associa maggiormente a PC2, evidenziando un diverso aspetto legato al rapporto studenti-docenti nelle scuole della zona.
Variabili come DIS e Price sono invece correlate positivamente con PC2, indicando che questa componente riflette anche la distanza dai centri di lavoro e la qualità abitativa.
La colorazione delle frecce, basata sulla contribuzione delle variabili alle componenti, mette in evidenza come NOX, DIS e Price siano tra le variabili più influenti nella definizione delle prime due componenti.
Il clustering è una tecnica di analisi dei dati che permette di suddividere un insieme di osservazioni in gruppi (cluster) omogenei, in cui gli elementi all’interno di uno stesso gruppo sono più simili tra loro rispetto a quelli appartenenti ad altri gruppi. Questo approccio è utile per scoprire strutture nascoste nel dataset e per raggruppare dati con caratteristiche simili.
Nel contesto di dati ad alta dimensionalità, come quelli analizzati con la PCA, il clustering viene spesso applicato sulle componenti principali per semplificare la struttura e migliorare la qualità del raggruppamento.
Prima di eseguire il clustering, è importante determinare il numero ottimale di cluster da utilizzare. Per questo si utilizzano metodi come:
Metodo del gomito (WSS): valuta la diminuzione della variabilità all’interno dei cluster al variare del numero di gruppi, suggerendo il punto in cui aggiungere nuovi cluster non migliora significativamente la qualità del raggruppamento.
Metodo della silhouette: quantifica quanto ogni punto è ben assegnato al proprio cluster, fornendo un indice che aiuta a identificare il numero di cluster che meglio separa le osservazioni
Per il metodo del gomito viene creato un vettore wss per memorizzare la somma delle distanze quadratiche all’interno dei cluster (Within-Cluster Sum of Squares) per valori di cluster da 1 a 10. Per ogni valore di k, il clustering k-means viene eseguito sui dati scalati (vars_scaled), con 25 inizializzazioni casuali per ridurre l’effetto di soluzioni subottimali. Il valore totale di somma delle distanze interne è salvato nel vettore wss.
Infine, viene tracciato un grafico che mostra come la WSS diminuisca all’aumentare del numero di cluster. Il grafico aiuta a identificare il punto in cui aggiungere ulteriori cluster non migliora significativamente la qualità del raggruppamento, ovvero il cosiddetto “gomito”. Questo punto è considerato il numero ottimale di cluster da utilizzare.
wss <- numeric(10)
set.seed(123)
for(k in 1:10){
wss[k] <- kmeans(vars_scaled, centers = k, nstart = 25)$tot.withinss
}
plot(1:10, wss, type="b",
xlab = "Numero di cluster",
ylab = "WSS",
main = "Metodo del gomito")
Nel grafico si osserva un forte calo di WSS passando da 1 a 2 cluster, seguito da una riduzione più graduale per valori maggiori di cluster. Il “gomito”, cioè il punto in cui la curva inizia a livellarsi, si presenta intorno a 2-3 cluster.
Successivamente si calcola la media dell’indice silhouette per un numero di cluster variabile da 2 a 10, al fine di valutare la qualità del clustering k-means.
avg_sil <- numeric(10)
for(k in 2:10){
kmeans_k <- kmeans(vars_scaled, centers = k, nstart = 25)
sil <- silhouette(kmeans_k$cluster, dist(vars_scaled))
avg_sil[k] <- mean(sil[, 3])
}
plot(2:10, avg_sil[2:10], type = "b",
xlab = "Numero di cluster", ylab = "silhouette",
main = "Metodo silhouette")
Nel grafico, il valore massimo della silhouette media si trova a 2 cluster, suggerendo che questa configurazione produce la miglior separazione e coesione dei gruppi. Aumentando il numero di cluster, l’indice tende a diminuire, indicando che ulteriori suddivisioni non migliorano la qualità del clustering.
Questo risultato conferma e rafforza l’indicazione già emersa dal metodo del gomito, evidenziando che 2 cluster rappresentano una scelta adeguata per i dati analizzati.
set.seed(123) # riproducibilità
kmeans_result2 <- kmeans(vars_scaled, centers = 2, nstart = 25)
kmeans_result2$size
## [1] 228 278
I valori indicano il numero di osservazioni presenti in ciascuno dei due cluster identificati dall’algoritmo di clustering. In questo caso, il primo cluster contiene 228 elementi, mentre il secondo cluster ne contiene 278. Questa distribuzione delle dimensioni suggerisce che i due gruppi sono abbastanza bilanciati in termini di numerosità, il che può facilitare ulteriori analisi e interpretazioni, evitando problemi legati a cluster troppo sbilanciati o piccoli. Per valutare la qualità del clustering effettuato con il metodo k-means, sono stati considerati due indicatori principali:
Coesione interna totale (tot.withinss): indica quanto i dati all’interno dei cluster sono raggruppati in modo compatto. Più basso è questo valore, più i cluster sono “stretti” e omogenei internamente.
Percentuale di variabilità spiegata tra cluster: questa percentuale rappresenta quanta parte della variabilità totale dei dati è spiegata dalla separazione tra i diversi cluster. Se la percentuale è alta, significa che i cluster sono ben distinti tra loro, cioè i centri dei cluster sono lontani e formano gruppi differenti. Al contrario, una percentuale bassa indica che i cluster sono poco separati e i dati potrebbero non essere suddivisi in modo molto efficace.
# calcolo della coesione totale
withinss2 <- kmeans_result2$tot.withinss
# variabilita
totss2 <- kmeans_result2$betweenss / kmeans_result2$totss * 100
withinss2
## [1] 2592.428
totss2
## [1] 42.96088
Per k=2, si ottiene una coesione interna (tot.withinss) pari a 2592.428 e una percentuale di variabilità spiegata tra i cluster del 42.96%. Questi valori indicano che i cluster non sono molto compatti internamente e la separazione tra i gruppi non è particolarmente marcata.
Per verificare se è possibile migliorare la qualità del clustering, si procede a calcolare il modello con k=3 cluster, confrontando i valori di coesione interna e di variabilità spiegata ottenuti.
set.seed(123)
kmeans_result3 <- kmeans(vars_scaled, centers = 3, nstart = 25)
kmeans_result3$size
## [1] 184 130 192
Il clustering con k=3 ha prodotto tre gruppi di dimensioni relativamente bilanciate: il primo cluster contiene 184 elementi, il secondo 130, e il terzo 192.
# calcolo della coesione totale
withinss3 <- kmeans_result3$tot.withinss
# variabilita
totss3 <- kmeans_result3$betweenss / kmeans_result3$totss * 100
withinss3
## [1] 2139.345
totss3
## [1] 52.92971
Per k=3, il valore di coesione interna (tot.withinss) è 2139.345, inferiore rispetto a valori ottenuti con un numero minore di cluster, il che indica che i punti all’interno di ciascun cluster sono più vicini tra loro e quindi i gruppi sono più compatti.
La percentuale di variabilità spiegata dal modello è circa il 52.93%, suggerendo che più della metà della variabilità totale dei dati è catturata dalla differenziazione tra i cluster. Ciò indica una buona separazione tra i gruppi e giustifica la scelta di k=3 come numero di cluster adeguato.
Per approfondire la comprensione dei gruppi individuati dal clustering con k=3, sono state integratoe le informazioni risultanti dall’assegnazione dei cluster direttamente nel dataset originale. In particolare, la variabile cluster è stata aggiunta al data frame contenente le variabili di interesse tramite la funzione mutate(), consentendo di associare ad ogni osservazione il proprio cluster di appartenenza come fattore categorico.
Successivamente, per ciascun cluster, sono state calcolate le medie di tutte le variabili numeriche mediante la funzione summarise() combinata con group_by(). Questa operazione ha prodotto una tabella riassuntiva che evidenzia le caratteristiche medie di ogni gruppo, facilitando l’identificazione delle differenze distintive tra i cluster.
vars <- vars %>%
mutate(cluster = as.factor(kmeans_result3$cluster))
# calcola la media per cluster
summary_cluster <- vars %>%
group_by(cluster) %>%
summarise(across(where(is.numeric), mean))
summary_cluster
## # A tibble: 3 × 10
## cluster NOX RM LSTAT PTRATIO Price INDUS TAX AGE DIS
## <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0.677 5.98 18.2 19.5 16.8 19.1 593. 91.2 2.01
## 2 2 0.467 7.05 5.64 16.5 33.3 4.66 290. 46.8 5.12
## 3 3 0.497 6.06 12.0 18.8 20.8 7.84 310. 61.7 4.56
Dall’analisi delle medie delle variabili chiave per ciascuno dei tre cluster emergono profili distinti che aiutano a comprendere le differenze sottostanti tra i gruppi individuati dal clustering:
Cluster 1 presenta i valori medi più elevati di NOX (0.68) e LSTAT (18.24), indicatori rispettivamente di maggiore inquinamento atmosferico e di una più alta percentuale di popolazione a basso status socio-economico. Parallelamente, il valore medio di RM (5.98), numero medio di stanze per abitazione, è relativamente basso ed è associato al prezzo medio degli immobili più contenuto (16.83). Questi dati suggeriscono che il cluster 1 rappresenta tipicamente aree con condizioni ambientali e socio-economiche più svantaggiate e abitazioni di qualità inferiore.
Cluster 2 si differenzia nettamente per il valore elevato di RM (7.05), che indica abitazioni più spaziose, e per i valori più bassi di NOX (0.47) e LSTAT (5.64), a indicare una minore presenza di inquinamento e di popolazione con basso status socio-economico. Il prezzo medio (33.27) risulta il più alto tra i cluster, coerente con un profilo di quartieri qualitativamente migliori, con infrastrutture e condizioni ambientali più favorevoli.
Cluster 3 assume un profilo intermedio: valori medi di NOX (0.50) e LSTAT (12.01) che denotano condizioni ambientali e sociali meno favorevoli rispetto al cluster 2, ma migliori rispetto al cluster 1. Anche la dimensione delle abitazioni (RM pari a 6.06) e il prezzo medio (20.77) si collocano a livelli intermedi, suggerendo che questo cluster rappresenta aree residenziali di qualità media.
fviz_cluster(kmeans_result3, data = vars_scaled,
geom = "point",
ellipse.type = "convex",
palette = "ginger",
ggtheme = theme_minimal(),
main = "Clustering con k=3")
La figura illustra la segmentazione ottenuta mediante il metodo di clustering con k=3, visualizzata sulle prime due componenti principali (Dim1 e Dim2) che cumulativamente spiegano il 73,2% della variabilità complessiva del dataset. L’analisi grafica evidenzia una netta separazione tra i tre cluster, a testimonianza dell’efficacia del modello nel differenziare gruppi omogenei. Si osserva una leggera sovrapposizione fra i cluster 1 e 3, indicativa di una zona di confine dove alcune osservazioni potrebbero avere caratteristiche comuni a entrambi i gruppi, possibile effetto di una riduzione dimensionale o di una vicinanza strutturale reale nei dati. Il cluster 2 appare ben separato e compatto rispetto agli altri due, suggerendo un gruppo con caratteristiche particolarmente omogenee.
La regressione lineare multipla è una tecnica statistica utilizzata per modellare la relazione tra una variabile dipendente continua e più variabili indipendenti. Questo metodo permette di quantificare l’effetto di ciascuna variabile esplicativa sul valore della variabile di interesse, controllando contemporaneamente l’influenza delle altre. Nel presente studio, la regressione lineare multipla sarà applicata per analizzare la relazione tra il prezzo degli immobili e le variabili selezionate, al fine di fornire un modello predittivo e interpretativo efficace.
Per garantire una valutazione affidabile delle performance del modello, il dataset è stato suddiviso in due sottoinsiemi: un set di addestramento, utilizzato per stimare i parametri del modello, e un set di test, riservato alla verifica delle capacità predittive su dati nuovi e non utilizzati in fase di stima. La suddivisione è stata effettuata in modo casuale ma riproducibile, assicurando che la stessa divisione possa essere replicata per confronti futuri e per garantire la trasparenza dell’analisi. Infine, è stata controllata la dimensione dei due sottoinsiemi per confermare che la proporzione tra training e test sia rispettata, garantendo un’adeguata quantità di dati per entrambe le fasi.
set.seed(123) # riproducibilità
n <- nrow(df)
train_indices <- sample(seq_len(n), size = floor(0.75 * n)) # 75% training
train_data <- df[train_indices, ]
test_data <- df[-train_indices, ]
dim(train_data)
## [1] 379 14
dim(test_data)
## [1] 127 14
La suddivisione del dataset originale ha prodotto due sottoinsiemi coerenti con le proporzioni previste: il training set contiene 379 osservazioni mentre il test set ne contiene 127, entrambi con 14 variabili.
Per ottimizzare il modello di regressione lineare multipla e migliorare l’equilibrio tra complessità e capacità predittiva, ho applicato la procedura di selezione automatica delle variabili tramite la funzione step(). Questa metodologia, nota come stepwise selection, consente di aggiungere o rimuovere iterativamente variabili dal modello in base a criteri statistici (come l’Akaike Information Criterion, AIC), al fine di individuare il sottoinsieme di predittori più rilevanti.
model1 <- lm(Price ~ ., data = train_data)
model_step <- step(model1)
## Start: AIC=1192.59
## Price ~ CRIM + ZN + INDUS + CHAS + NOX + RM + AGE + DIS + RAD +
## TAX + PTRATIO + B + LSTAT
##
## Df Sum of Sq RSS AIC
## - INDUS 1 0.20 8187.4 1190.6
## - AGE 1 0.63 8187.8 1190.6
## <none> 8187.2 1192.6
## - B 1 91.95 8279.1 1194.8
## - TAX 1 125.88 8313.1 1196.4
## - CRIM 1 197.96 8385.1 1199.6
## - ZN 1 265.67 8452.9 1202.7
## - NOX 1 305.50 8492.7 1204.5
## - CHAS 1 318.85 8506.0 1205.1
## - RAD 1 333.24 8520.4 1205.7
## - PTRATIO 1 765.69 8952.9 1224.5
## - DIS 1 1025.27 9212.5 1235.3
## - RM 1 1071.57 9258.8 1237.2
## - LSTAT 1 2448.24 10635.4 1289.7
##
## Step: AIC=1190.6
## Price ~ CRIM + ZN + CHAS + NOX + RM + AGE + DIS + RAD + TAX +
## PTRATIO + B + LSTAT
##
## Df Sum of Sq RSS AIC
## - AGE 1 0.67 8188.1 1188.6
## <none> 8187.4 1190.6
## - B 1 92.68 8280.1 1192.9
## - TAX 1 177.84 8365.2 1196.7
## - CRIM 1 197.77 8385.2 1197.6
## - ZN 1 273.98 8461.4 1201.1
## - CHAS 1 322.96 8510.3 1203.3
## - NOX 1 336.58 8524.0 1203.9
## - RAD 1 378.24 8565.6 1205.7
## - PTRATIO 1 771.76 8959.1 1222.7
## - DIS 1 1070.51 9257.9 1235.2
## - RM 1 1090.39 9277.8 1236.0
## - LSTAT 1 2451.19 10638.6 1287.8
##
## Step: AIC=1188.63
## Price ~ CRIM + ZN + CHAS + NOX + RM + DIS + RAD + TAX + PTRATIO +
## B + LSTAT
##
## Df Sum of Sq RSS AIC
## <none> 8188.1 1188.6
## - B 1 93.64 8281.7 1190.9
## - TAX 1 177.17 8365.2 1194.7
## - CRIM 1 197.49 8385.5 1195.7
## - ZN 1 274.77 8462.8 1199.1
## - CHAS 1 325.79 8513.8 1201.4
## - NOX 1 357.37 8545.4 1202.8
## - RAD 1 379.18 8567.2 1203.8
## - PTRATIO 1 772.20 8960.3 1220.8
## - RM 1 1118.87 9306.9 1235.2
## - DIS 1 1216.33 9404.4 1239.1
## - LSTAT 1 2636.59 10824.6 1292.4
summary(model_step)
##
## Call:
## lm(formula = Price ~ CRIM + ZN + CHAS + NOX + RM + DIS + RAD +
## TAX + PTRATIO + B + LSTAT, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.6715 -2.6671 -0.5173 1.6211 24.6904
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 38.898906 5.849474 6.650 1.07e-10 ***
## CRIM -0.103016 0.034625 -2.975 0.003122 **
## ZN 0.054315 0.015477 3.509 0.000505 ***
## CHASSi 3.660510 0.957928 3.821 0.000156 ***
## NOX -16.331036 4.080491 -4.002 7.59e-05 ***
## RM 3.350842 0.473174 7.082 7.31e-12 ***
## DIS -1.560359 0.211327 -7.384 1.04e-12 ***
## RAD 0.292992 0.071071 4.123 4.64e-05 ***
## TAX -0.010568 0.003750 -2.818 0.005095 **
## PTRATIO -0.889102 0.151127 -5.883 9.07e-09 ***
## B 0.006808 0.003323 2.049 0.041205 *
## LSTAT -0.596534 0.054875 -10.871 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.723 on 367 degrees of freedom
## Multiple R-squared: 0.7454, Adjusted R-squared: 0.7377
## F-statistic: 97.67 on 11 and 367 DF, p-value: < 2.2e-16
L’algoritmo ha iniziato con un modello completo che includeva tutte le 13 variabili predittive disponibili e ha valutato iterativamente l’impatto della rimozione di ciascuna variabile, confrontando il valore di AIC risultante.
Il processo ha identificato inizialmente due variabili (AGE e INDUS) la cui esclusione riduceva l’AIC (da 1192 a 1188), segnalando un miglioramento del compromesso tra bontà di adattamento e parsimonia del modello. Procedendo con la rimozione di queste variabili, la procedura ha fermato la selezione quando ulteriori esclusioni avrebbero peggiorato il modello (ovvero portato a un aumento dell’AIC).
Il modello finale quindi comprende 11 variabili: CRIM, ZN, CHAS, NOX, RM, DIS, RAD, TAX, PTRATIO, B e LSTAT. I coefficienti stimati mostrano che la maggioranza delle variabili mantiene un effetto significativo sul prezzo, confermando la rilevanza di tali predittori nella spiegazione della variabilità dei dati. Il modello finale presenta un Adjusted R-squared molto simile a quello iniziale (circa 0.7377), confermando che la semplificazione non ha sacrificato l’efficacia predittiva.
model2 <- lm(Price ~ CRIM + ZN + CHAS + NOX + RM + DIS + RAD + TAX + PTRATIO + B + LSTAT, data = train_data)
vif(model2)
## CRIM ZN CHAS NOX RM DIS RAD TAX
## 1.773329 2.331141 1.066539 3.723866 1.834417 3.511882 6.401656 6.764313
## PTRATIO B LSTAT
## 1.800462 1.347165 2.661182
Dopo aver identificato una forte correlazione tra la variabile TAX e altre variabili predittive del modello, si è scelto di escluderla per ridurre la multicollinearità.
model3 <- lm(Price ~ CRIM + ZN + CHAS + NOX + RM + DIS + RAD + PTRATIO + B + LSTAT, data = train_data)
summary(model3)
##
## Call:
## lm(formula = Price ~ CRIM + ZN + CHAS + NOX + RM + DIS + RAD +
## PTRATIO + B + LSTAT, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.3916 -2.9432 -0.4002 1.7085 24.7055
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.168837 5.871770 6.330 7.13e-10 ***
## CRIM -0.099736 0.034931 -2.855 0.00454 **
## ZN 0.046262 0.015354 3.013 0.00277 **
## CHASSi 3.919673 0.962453 4.073 5.70e-05 ***
## NOX -19.020137 4.004579 -4.750 2.93e-06 ***
## RM 3.535863 0.472995 7.475 5.68e-13 ***
## DIS -1.498626 0.212162 -7.064 8.16e-12 ***
## RAD 0.142739 0.047432 3.009 0.00280 **
## PTRATIO -0.946749 0.151142 -6.264 1.05e-09 ***
## B 0.007200 0.003351 2.148 0.03235 *
## LSTAT -0.602904 0.055343 -10.894 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.768 on 368 degrees of freedom
## Multiple R-squared: 0.7399, Adjusted R-squared: 0.7328
## F-statistic: 104.7 on 10 and 368 DF, p-value: < 2.2e-16
vif(model3)
## CRIM ZN CHAS NOX RM DIS RAD PTRATIO
## 1.771324 2.251669 1.056708 3.520204 1.799096 3.474144 2.798531 1.767473
## B LSTAT
## 1.344810 2.656666
Il modello di regressione lineare stimato spiega circa il 73.4% della variabilità della variabile dipendente Price (R² = 0.7328), suggerendo una buona capacità predittiva complessiva.
I coefficienti stimati indicano l’effetto marginale di ciascun predittore sul prezzo, tenendo costanti le altre variabili. La maggior parte dei coefficienti risulta statisticamente significativa (p-value < 0.01), in particolare variabili come RM, LSTAT, NOX e PTRATIO mostrano forti effetti con elevata significatività. Per quanto riguarda la multicollinearità, i valori di VIF riportati sono tutti inferiori a 5, con il valore massimo di circa 3.52 (per NOX), indicazione che la collinearità tra variabili è contenuta e non dovrebbe compromettere la stabilità delle stime.
Per valutare la bontà del modello di regressione e verificare l’ipotesi di normalità degli errori, si è calcolata la distribuzione dei residui del modello. In particolare, è stato estratto il vettore dei residui dal modello (model3) e ne sono stati calcolati la media e la deviazione standard. Successivamente, si è costruito un istogramma con densità normalizzata dei residui affiancato dalla curva teorica della distribuzione normale con media e deviazione standard corrispondenti.
residui <- residuals(model3)
residuals_df <- data.frame(residui = residui)
# Calcolo media e deviazione standard dei residui
mu <- mean(residui)
sigma <- sd(residui)
ggplot(residuals_df, aes(x = residui)) +
geom_histogram(aes(y = ..density..),
color = 'black', fill = 'skyblue', binwidth = 1) +
stat_function(fun = dnorm, args = list(mean = mu, sd = sigma),
color = "red", size = 1) +
ggtitle("Istogramma dei residui") +
ylab("Densità")
L’istogramma dei residui del modello mostra una distribuzione approssimativamente centrata attorno a zero, condizione necessaria per un modello adeguato. Tuttavia, si osserva una leggera asimmetria positiva, con una coda più estesa verso valori residui positivi rispetto a quanto atteso da una distribuzione normale teorica. Questa deviazione suggerisce una non perfetta normalità degli errori, che potrebbe indicare la presenza di eteroschedasticità, outlier o componenti sistematiche non adeguatamente catturate dal modello attuale.
Per approfondire queste osservazioni, si è analizzato il grafico Residui vs Valori Predetti, che consente di verificare l’ipotesi di omoscedasticità, ovvero che la varianza degli errori sia costante per ogni livello dei valori predetti. Infine, si è esaminato il Q-Q plot dei residui, utile per verificare la normalità della distribuzione degli errori confrontando quantili osservati con quantili teorici di una distribuzione normale.
par(mfrow = c(1, 2))
# Residui vs valori predetti
plot(model1$fitted.values, residuals(model3),
xlab = "Valori Predetti",
ylab = "Residui",
main = "Residui vs valori predetti")
abline(h = 0, col = "red")
# QQ-plot dei residui
qqnorm(residuals(model3))
qqline(residuals(model3), col = "red")
par(mfrow = c(1, 1))
Il grafico Residui vs Valori Predetti mostra una distribuzione dei residui abbastanza uniforme lungo tutto l’intervallo dei valori predetti, senza variazioni evidenti nella dispersione. Questo indica che la varianza degli errori è approssimativamente costante, confermando l’assunzione di omoscedasticità necessaria per la validità del modello.
Nel Q-Q plot, i residui si allineano in modo soddisfacente lungo la linea di riferimento, suggerendo una distribuzione dei residui vicina alla normalità. Sono tuttavia presenti leggere deviazioni nelle code, in particolare nella parte superiore destra, coerenti con una lieve asimmetria già osservata nell’istogramma.
Per accertare la validità dell’assunzione di omoschedasticità nel modello, è stato applicato il test studentized di Breusch-Pagan.
bptest(model3)
##
## studentized Breusch-Pagan test
##
## data: model3
## BP = 43.423, df = 10, p-value = 4.177e-06
Il test ha restituito una statistica di 56.427 con 10 gradi di libertà e un p-value estremamente basso (4.177e-06), inferiore alla soglia convenzionale di significatività (0.05). Tale risultato permette di rifiutare l’ipotesi nulla di varianza costante degli errori, indicando la presenza di eteroschedasticità. Questa condizione implica che la varianza degli errori varia in modo significativo in relazione ai valori delle variabili predittive, il che potrebbe compromettere l’efficienza degli stimatori ordinari e l’affidabilità degli intervalli di confidenza e dei test statistici basati su errori standard classici.
Al fine di correggere la presenza di eteroschedasticità rilevata nel modello di regressione (come evidenziato dal test di Breusch-Pagan), è stata applicata una stima degli errori standard robusti utilizzando il metodo HC3 tramite la funzione coeftest() del pacchetto lmtest con la matrice di varianza-covarianza robusta.
Il metodo HC3 tratta gli errori in modo da ridurne il bias legato alla varianza non costante degli errori (eteroschedasticità), modificando la matrice di varianza-covarianza degli stimatori dei coefficienti per renderla più affidabile rispetto ai metodi più semplici come HC0 o HC1. Questa procedura non modifica le stime puntuali dei coefficienti, ma aggiorna gli errori standard, i valori t e i p-value tenendo conto della non costanza della varianza degli errori. Di conseguenza, le inferenze statistiche risultano più affidabili, riducendo il rischio di trarre conclusioni errate dovute a varianze non omogenee degli errori.
coeftest(model3, vcov = vcovHC(model3, type = "HC3"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.1688365 9.3219967 3.9872 8.065e-05 ***
## CRIM -0.0997355 0.0390352 -2.5550 0.0110198 *
## ZN 0.0462620 0.0161210 2.8697 0.0043461 **
## CHASSi 3.9196734 1.3463724 2.9113 0.0038187 **
## NOX -19.0201371 4.1798612 -4.5504 7.283e-06 ***
## RM 3.5358626 0.9618044 3.6763 0.0002719 ***
## DIS -1.4986263 0.2594870 -5.7753 1.635e-08 ***
## RAD 0.1427392 0.0574894 2.4829 0.0134775 *
## PTRATIO -0.9467489 0.1293975 -7.3166 1.608e-12 ***
## B 0.0071996 0.0039412 1.8267 0.0685480 .
## LSTAT -0.6029043 0.1074885 -5.6090 4.004e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Il modello di regressione lineare multipla, stimato con correzione dei coefficienti tramite errori standard robusti HC3, evidenzia effetti significativi di molteplici predittori sulla variabile risposta “Prezzo”.
Le variabili CRIM, ZN, CHAS, NOX, RM, DIS, RAD, PTRATIO e LSTAT risultano fortemente associate al prezzo con livelli di significatività elevati, mentre la variabile B si mostra marginalmente significativa (p ≈ 0.068).
I coefficienti stimati indicano che un aumento di NOX e LSTAT è associato a una diminuzione del prezzo, coerentemente con la natura di queste caratteristiche, mentre un incremento di RM o CHAS tende a far aumentare il prezzo mediamente. L’adozione degli errori standard robusti HC3 ha permesso di ottenere inferenze più affidabili correggendo gli effetti di eventuale eteroschedasticità, rafforzando la fiducia nei risultati.
predicted_test <- predict(model3, newdata = test_data)
ggplot(data = test_data, aes(x = Price, y = predicted_test)) +
geom_point(color = "mediumpurple", alpha = 0.6, size = 2) +
geom_abline(slope = 1, intercept = 0, color = "red", size = 1) +
labs(
title = "Confronto tra valori osservati e predetti sul test set",
x = "Prezzi osservati",
y = "Prezzi predetti"
) +
theme_minimal()
Lo scatterplot mostra un confronto tra i prezzi osservati e quelli predetti dal modello sul test set. In generale, si osserva che le predizioni seguono abbastanza bene la linea di identità, indicando una buona capacità del modello di stimare i valori reali. Tuttavia, si nota una certa dispersione attorno alla retta, soprattutto per i prezzi più elevati, dove il modello tende a sottostimare o sovrastimare in qualche caso. Questa variabilità suggerisce che il modello non riesce a catturare completamente tutte le dinamiche nei dati, soprattutto per valori estremi.
Per valutare la precisione delle predizioni del modello, sono state calcolate due metriche di errore comunemente utilizzate in ambito di regressione: il Root Mean Squared Error (RMSE) e il Mean Absolute Error (MAE). Il RMSE fornisce una misura della deviazione quadratica media tra i valori osservati e quelli predetti, attribuendo un peso maggiore agli errori più grandi, mentre il MAE rappresenta la media degli scarti assoluti, offrendo una valutazione più diretta e meno influenzata da outlier.
residuals <- test_data$Price - predicted_test # residui
RMSE <- sqrt(mean(residuals^2))
MAE <- mean(abs(residuals))
RMSE
## [1] 4.962718
MAE
## [1] 3.698338
Le metriche di errore calcolate sul set di test indicano una buona capacità predittiva del modello. Il Root Mean Squared Error (RMSE) è pari a circa 4.96, mentre il Mean Absolute Error (MAE) risulta circa 3.70. Questi risultati indicano che, mediamente, le predizioni del modello si discostano dai valori reali rispettivamente di 4.96 e 3.70 unità di prezzo. Considerando la scala e la natura della variabile target, tali errori sono da ritenersi accettabili, confermando l’affidabilità del modello nel fornire stime precise.
L’analisi svolta sul Boston Housing Dataset ha permesso di identificare e quantificare i principali fattori che influenzano il prezzo medio delle abitazioni nell’area di Boston.
Dall’analisi esplorativa è emersa la presenza di distribuzioni asimmetriche e di valori anomali in alcune variabili, opportunamente gestiti con tecniche di capping per garantirne la robustezza nell’analisi successiva.
L’Analisi delle Componenti Principali ha sintetizzato efficacemente l’informazione multidimensionale riducendo il dataset a due componenti che spiegano complessivamente il 73% della varianza, facilitando la comprensione delle relazioni tra le variabili.
Il clustering ha identificato tre distinti gruppi di quartieri, caratterizzati da differenti condizioni ambientali, socio-economiche e abitative. In particolare, è stato possibile distinguere cluster rappresentanti aree svantaggiate con elevati livelli di inquinamento e minor qualità abitativa, cluster di qualità intermedia e cluster contraddistinti da caratteristiche ambientali favorevoli e prezzi elevati.
La regressione lineare multipla, sviluppata e raffinata attraverso procedure di selezione automatica e gestione della multicollinearità, ha mostrato un’elevata capacità predittiva, spiegando circa il 73% della variabilità del prezzo. Variabili quali il numero medio di stanze (RM), la percentuale di popolazione a basso status socio-economico (LSTAT), e la vicinanza al fiume Charles (CHAS) sono risultate fattori significativi con impatti attesi sul prezzo degli immobili.
La validazione del modello su un set di test indipendente ha confermato la sua buona capacità predittiva, con errori medi (RMSE ~4.96, MAE ~3.70) coerenti con l’ordine di grandezza della variabile target. L’analisi dei residui ha evidenziato una lieve eteroschedasticità, corretta mediante l’adozione di errori standard robusti, migliorando così l’affidabilità inferenziale del modello.