Il presente progetto ha l’obiettivo di costruire un modello statistico in grado di prevedere il peso dei neonati alla nascita, sulla base di variabili cliniche raccolte da tre ospedali. Il dataset contiene informazioni su 2500 neonati e include variabili relative alla madre, alla gravidanza e al neonato stesso.
# Caricamento pacchetti necessari
library(moments) # per skewness e kurtosis
library(lmtest) # per bptest e dwtest
library(car) # per vif e outlierTest
library(MASS) # per stepAIC
library(ggplot2) # per i grafici
# Caricamento dataset
dati = read.csv("neonati.csv", stringsAsFactors = TRUE)
# Prima esplorazione
head(dati)## Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza Cranio Tipo.parto
## 1 26 0 0 42 3380 490 325 Nat
## 2 21 2 0 39 3150 490 345 Nat
## 3 34 3 0 38 3640 500 375 Nat
## 4 28 1 0 41 3690 515 365 Nat
## 5 20 0 0 38 3700 480 335 Nat
## 6 32 0 0 40 3200 495 340 Nat
## Ospedale Sesso
## 1 osp3 M
## 2 osp1 F
## 3 osp2 M
## 4 osp2 M
## 5 osp3 F
## 6 osp2 F
## 'data.frame': 2500 obs. of 10 variables:
## $ Anni.madre : int 26 21 34 28 20 32 26 25 22 23 ...
## $ N.gravidanze: int 0 2 3 1 0 0 1 0 1 0 ...
## $ Fumatrici : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Gestazione : int 42 39 38 41 38 40 39 40 40 41 ...
## $ Peso : int 3380 3150 3640 3690 3700 3200 3100 3580 3670 3700 ...
## $ Lunghezza : int 490 490 500 515 480 495 480 510 500 510 ...
## $ Cranio : int 325 345 375 365 335 340 345 349 335 362 ...
## $ Tipo.parto : Factor w/ 2 levels "Ces","Nat": 2 2 2 2 2 2 2 2 1 1 ...
## $ Ospedale : Factor w/ 3 levels "osp1","osp2",..: 3 1 2 2 3 2 3 1 2 2 ...
## $ Sesso : Factor w/ 2 levels "F","M": 2 1 2 2 1 1 1 2 1 1 ...
## Anni.madre N.gravidanze Fumatrici Gestazione
## Min. : 0.00 Min. : 0.0000 Min. :0.0000 Min. :25.00
## 1st Qu.:25.00 1st Qu.: 0.0000 1st Qu.:0.0000 1st Qu.:38.00
## Median :28.00 Median : 1.0000 Median :0.0000 Median :39.00
## Mean :28.16 Mean : 0.9812 Mean :0.0416 Mean :38.98
## 3rd Qu.:32.00 3rd Qu.: 1.0000 3rd Qu.:0.0000 3rd Qu.:40.00
## Max. :46.00 Max. :12.0000 Max. :1.0000 Max. :43.00
## Peso Lunghezza Cranio Tipo.parto Ospedale Sesso
## Min. : 830 Min. :310.0 Min. :235 Ces: 728 osp1:816 F:1256
## 1st Qu.:2990 1st Qu.:480.0 1st Qu.:330 Nat:1772 osp2:849 M:1244
## Median :3300 Median :500.0 Median :340 osp3:835
## Mean :3284 Mean :494.7 Mean :340
## 3rd Qu.:3620 3rd Qu.:510.0 3rd Qu.:350
## Max. :4930 Max. :565.0 Max. :390
par(mfrow = c(1, 2))
hist(dati$Peso,
main = "Distribuzione del Peso",
xlab = "Peso (g)",
col = "lightblue",
border = "white")
plot(density(dati$Peso),
main = "Densità del Peso",
xlab = "Peso (g)",
col = "steelblue",
lwd = 2)## Skewness: -0.6470308
## Curtosi in eccesso: 2.031532
##
## Shapiro-Wilk normality test
##
## data: dati$Peso
## W = 0.97066, p-value < 2.2e-16
La variabile risposta
Pesopresenta una distribuzione approssimativamente normale, condizione importante per procedere con la regressione lineare.
par(mfrow = c(2, 3))
boxplot(dati$Peso, main = "Peso (g)", col = "lightblue")
boxplot(dati$Lunghezza, main = "Lunghezza (mm)", col = "lightgreen")
boxplot(dati$Cranio, main = "Cranio (mm)", col = "lightyellow")
boxplot(dati$Gestazione, main = "Gestazione (sett)", col = "lightpink")
boxplot(dati$Anni.madre, main = "Età madre", col = "lavender")
boxplot(dati$N.gravidanze, main = "N. gravidanze", col = "peachpuff")Vogliamo verificare se la proporzione di parti cesarei varia significativamente tra i tre ospedali.
H0: La proporzione di parti cesarei è uguale nei tre
ospedali
H1: Almeno un ospedale ha una proporzione diversa
##
## Ces Nat
## osp1 242 574
## osp2 254 595
## osp3 232 603
##
## Ces Nat
## osp1 0.2965686 0.7034314
## osp2 0.2991755 0.7008245
## osp3 0.2778443 0.7221557
# Visualizzazione
barplot(prop.table(tab_parto_osp, margin = 1),
beside = TRUE,
legend.text = rownames(tab_parto_osp),
col = c("steelblue", "tomato", "seagreen"),
main = "Proporzione tipo di parto per ospedale",
xlab = "Tipo di parto",
ylab = "Proporzione")##
## Pearson's Chi-squared test
##
## data: tab_parto_osp
## X-squared = 1.0972, df = 2, p-value = 0.5778
Se il p-value è inferiore a 0.05, si rifiuta H0: esistono differenze statisticamente significative nella proporzione di parti cesarei tra i tre ospedali.
Si assume che nella popolazione generale il peso medio alla nascita sia 3300 g e la lunghezza media sia 500 mm. Vogliamo verificare se le medie del nostro campione sono significativamente diverse da questi valori.
H0: μ_Peso = 3300 g
H1: μ_Peso ≠ 3300 g
##
## One Sample t-test
##
## data: dati$Peso
## t = -1.516, df = 2499, p-value = 0.1296
## alternative hypothesis: true mean is not equal to 3300
## 95 percent confidence interval:
## 3263.490 3304.672
## sample estimates:
## mean of x
## 3284.081
H0: μ_Lunghezza = 500 mm
H1: μ_Lunghezza ≠ 500 mm
##
## One Sample t-test
##
## data: dati$Lunghezza
## t = -10.084, df = 2499, p-value < 2.2e-16
## alternative hypothesis: true mean is not equal to 500
## 95 percent confidence interval:
## 493.6598 495.7242
## sample estimates:
## mean of x
## 494.692
Dalla lettura del p-value si determina se le medie del campione sono o meno significativamente uguali a quelle della popolazione di riferimento.
Vogliamo verificare se Peso, Lunghezza e Cranio differiscono significativamente tra maschi e femmine.
# Boxplot per variabile e sesso
par(mfrow = c(1, 3))
boxplot(Peso ~ Sesso, data = dati, main = "Peso per Sesso", col = c("pink","lightblue"))
boxplot(Lunghezza ~ Sesso, data = dati, main = "Lunghezza per Sesso", col = c("pink","lightblue"))
boxplot(Cranio ~ Sesso, data = dati, main = "Cranio per Sesso", col = c("pink","lightblue"))par(mfrow = c(1, 1))
# T test indipendente per ciascuna variabile
# H0: le medie dei due sessi sono uguali
# H1: le medie dei due sessi sono diverse
cat("--- Peso ---\n")## --- Peso ---
##
## Welch Two Sample t-test
##
## data: Peso by Sesso
## t = -12.106, df = 2490.7, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group F and group M is not equal to 0
## 95 percent confidence interval:
## -287.1051 -207.0615
## sample estimates:
## mean in group F mean in group M
## 3161.132 3408.215
## --- Lunghezza ---
##
## Welch Two Sample t-test
##
## data: Lunghezza by Sesso
## t = -9.582, df = 2459.3, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group F and group M is not equal to 0
## 95 percent confidence interval:
## -11.929470 -7.876273
## sample estimates:
## mean in group F mean in group M
## 489.7643 499.6672
## --- Cranio ---
##
## Welch Two Sample t-test
##
## data: Cranio by Sesso
## t = -7.4102, df = 2491.4, p-value = 1.718e-13
## alternative hypothesis: true difference in means between group F and group M is not equal to 0
## 95 percent confidence interval:
## -6.089912 -3.541270
## sample estimates:
## mean in group F mean in group M
## 337.6330 342.4486
Se i p-value risultano inferiori a 0.05, si rifiuta H0: le misure antropometriche sono significativamente diverse tra maschi e femmine.
Prima di costruire il modello di regressione, analizziamo le correlazioni tra le variabili quantitative.
# Selezioniamo solo le variabili numeriche
dati_num = dati[, c("Anni.madre", "N.gravidanze", "Gestazione", "Peso", "Lunghezza", "Cranio")]
# Matrice di correlazione
round(cor(dati_num), 2)## Anni.madre N.gravidanze Gestazione Peso Lunghezza Cranio
## Anni.madre 1.00 0.38 -0.14 -0.02 -0.06 0.02
## N.gravidanze 0.38 1.00 -0.10 0.00 -0.06 0.04
## Gestazione -0.14 -0.10 1.00 0.59 0.62 0.46
## Peso -0.02 0.00 0.59 1.00 0.80 0.70
## Lunghezza -0.06 -0.06 0.62 0.80 1.00 0.60
## Cranio 0.02 0.04 0.46 0.70 0.60 1.00
# Funzione per visualizzare le correlazioni nella matrice pairs
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...) {
par(usr = c(0, 1, 0, 1))
r <- abs(cor(x, y))
txt <- format(c(r, 0.123456789), digits = digits)[1]
txt <- paste0(prefix, txt)
if (missing(cex.cor)) cex.cor <- 0.8 / strwidth(txt)
text(0.5, 0.5, txt, cex = cex.cor * r)
}
pairs(dati_num,
upper.panel = panel.smooth,
lower.panel = panel.cor,
main = "Matrice di Correlazione e Scatterplot")Inseriamo tutte le variabili nel modello di partenza.
n = nrow(dati)
mod1 = lm(Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione +
Lunghezza + Cranio + Tipo.parto + Ospedale + Sesso,
data = dati)
summary(mod1)##
## Call:
## lm(formula = Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione +
## Lunghezza + Cranio + Tipo.parto + Ospedale + Sesso, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1124.40 -181.66 -14.42 160.91 2611.89
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6738.4762 141.3087 -47.686 < 2e-16 ***
## Anni.madre 0.8921 1.1323 0.788 0.4308
## N.gravidanze 11.2665 4.6608 2.417 0.0157 *
## Fumatrici -30.1631 27.5386 -1.095 0.2735
## Gestazione 32.5696 3.8187 8.529 < 2e-16 ***
## Lunghezza 10.2945 0.3007 34.236 < 2e-16 ***
## Cranio 10.4707 0.4260 24.578 < 2e-16 ***
## Tipo.partoNat 29.5254 12.0844 2.443 0.0146 *
## Ospedaleosp2 -11.2095 13.4379 -0.834 0.4043
## Ospedaleosp3 28.0958 13.4957 2.082 0.0375 *
## SessoM 77.5409 11.1776 6.937 5.08e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 273.9 on 2489 degrees of freedom
## Multiple R-squared: 0.7289, Adjusted R-squared: 0.7278
## F-statistic: 669.2 on 10 and 2489 DF, p-value: < 2.2e-16
Utilizziamo la procedura stepwise con criterio BIC per selezionare il modello più parsimonioso.
stepwise.mod = MASS::stepAIC(mod1,
direction = "both",
k = log(n), # BIC
trace = FALSE)
summary(stepwise.mod)##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Sesso, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1149.44 -180.81 -15.58 163.64 2639.72
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6681.1445 135.7229 -49.226 < 2e-16 ***
## N.gravidanze 12.4750 4.3396 2.875 0.00408 **
## Gestazione 32.3321 3.7980 8.513 < 2e-16 ***
## Lunghezza 10.2486 0.3006 34.090 < 2e-16 ***
## Cranio 10.5402 0.4262 24.728 < 2e-16 ***
## SessoM 77.9927 11.2021 6.962 4.26e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 274.6 on 2494 degrees of freedom
## Multiple R-squared: 0.727, Adjusted R-squared: 0.7265
## F-statistic: 1328 on 5 and 2494 DF, p-value: < 2.2e-16
## df AIC
## mod1 12 35171.95
## mod_fin 7 35179.33
## df BIC
## mod1 12 35241.84
## mod_fin 7 35220.10
Il modello selezionato in modo automatico minimizza il BIC, garantendo un buon equilibrio tra fit e parsimonia.
##
## Shapiro-Wilk normality test
##
## data: residuals(mod_fin)
## W = 0.97408, p-value < 2.2e-16
##
## studentized Breusch-Pagan test
##
## data: mod_fin
## BP = 90.253, df = 5, p-value < 2.2e-16
##
## Durbin-Watson test
##
## data: mod_fin
## DW = 1.9535, p-value = 0.1224
## alternative hypothesis: true autocorrelation is greater than 0
- Shapiro-Wilk: se p > 0.05, i residui sono normali
- Breusch-Pagan: se p > 0.05, la varianza è costante (omoschedasticità)
- Durbin-Watson: se p > 0.05, i residui non sono autocorrelati
lev = hatvalues(mod_fin)
p = sum(lev)
soglia = 2 * p / n
plot(lev,
main = "Valori di Leva",
ylab = "Leverage",
pch = 20,
col = "steelblue")
abline(h = soglia, col = 2, lty = 2)## Osservazioni con leverage elevato:
## 13 15 34 67 89 96 101 106 131 134 151 155 161 189 190 204
## 13 15 34 67 89 96 101 106 131 134 151 155 161 189 190 204
## 205 206 220 294 305 310 312 315 378 440 442 445 486 492 497 516
## 205 206 220 294 305 310 312 315 378 440 442 445 486 492 497 516
## 582 587 592 614 638 656 657 684 697 702 729 748 750 757 765 805
## 582 587 592 614 638 656 657 684 697 702 729 748 750 757 765 805
## 828 893 895 913 928 946 947 956 985 1008 1014 1049 1067 1091 1106 1130
## 828 893 895 913 928 946 947 956 985 1008 1014 1049 1067 1091 1106 1130
## 1166 1181 1188 1200 1219 1238 1248 1273 1291 1293 1311 1321 1325 1356 1357 1385
## 1166 1181 1188 1200 1219 1238 1248 1273 1291 1293 1311 1321 1325 1356 1357 1385
## 1395 1400 1402 1411 1420 1428 1429 1450 1505 1551 1553 1556 1573 1593 1606 1610
## 1395 1400 1402 1411 1420 1428 1429 1450 1505 1551 1553 1556 1573 1593 1606 1610
## 1617 1619 1628 1686 1693 1701 1712 1718 1727 1735 1780 1781 1809 1827 1868 1892
## 1617 1619 1628 1686 1693 1701 1712 1718 1727 1735 1780 1781 1809 1827 1868 1892
## 1962 1967 1977 2037 2040 2046 2086 2089 2098 2114 2115 2120 2140 2146 2148 2149
## 1962 1967 1977 2037 2040 2046 2086 2089 2098 2114 2115 2120 2140 2146 2148 2149
## 2157 2175 2200 2215 2216 2220 2221 2224 2225 2244 2257 2307 2317 2318 2337 2359
## 2157 2175 2200 2215 2216 2220 2221 2224 2225 2244 2257 2307 2317 2318 2337 2359
## 2408 2422 2436 2437 2452 2458 2471 2478
## 2408 2422 2436 2437 2452 2458 2471 2478
# Residui studentizzati
plot(rstudent(mod_fin),
main = "Residui Studentizzati",
ylab = "r studentizzato",
pch = 20,
col = "steelblue")
abline(h = c(-2, 2), col = 2, lty = 2)## rstudent unadjusted p-value Bonferroni p
## 1551 10.051908 2.4906e-23 6.2265e-20
## 155 5.027798 5.3138e-07 1.3285e-03
## 1306 4.827238 1.4681e-06 3.6702e-03
cook = cooks.distance(mod_fin)
plot(cook,
main = "Distanza di Cook",
ylab = "Cook's Distance",
pch = 20,
col = "steelblue")
abline(h = 0.5, col = "orange", lty = 2)
abline(h = 1.0, col = "red", lty = 2)Valori di Cook superiori a 0.5 (soglia di attenzione) o a 1 (soglia critica) indicano osservazioni fortemente influenti sul modello.
Stimiamo il peso di una neonata femmina da una madre alla terza gravidanza, non fumatrice, che partorirà alla 39esima settimana in modo naturale all’ospedale 1. Usiamo i valori medi di lunghezza e cranio per le femmine.
# Valori medi di riferimento per una femmina
media_lunghezza_F = mean(dati$Lunghezza[dati$Sesso == "F"])
media_cranio_F = mean(dati$Cranio[dati$Sesso == "F"])
media_anni_madre = mean(dati$Anni.madre)
nuova_obs = data.frame(
Anni.madre = media_anni_madre,
N.gravidanze = 3,
Fumatrici = 0,
Gestazione = 39,
Lunghezza = media_lunghezza_F,
Cranio = media_cranio_F,
Tipo.parto = factor("Nat", levels = levels(dati$Tipo.parto)),
Ospedale = factor("osp1", levels = levels(dati$Ospedale)),
Sesso = factor("F", levels = levels(dati$Sesso))
)
previsione = predict(mod_fin, newdata = nuova_obs, interval = "prediction", level = 0.95)
cat("Peso previsto:", round(previsione[1], 0), "g\n")## Peso previsto: 3195 g
cat("Intervallo di previsione al 95%: [", round(previsione[2], 0), ",", round(previsione[3], 0), "] g\n")## Intervallo di previsione al 95%: [ 2656 , 3734 ] g
ggplot(data = dati) +
geom_point(aes(x = Gestazione, y = Peso, col = factor(Fumatrici)),
alpha = 0.4, size = 1.5) +
geom_smooth(aes(x = Gestazione, y = Peso, col = factor(Fumatrici)),
method = "lm", se = FALSE) +
scale_color_manual(values = c("steelblue", "tomato"),
labels = c("Non fumatrice", "Fumatrice"),
name = "Fumo materno") +
labs(title = "Effetto della gestazione e del fumo sul peso",
x = "Settimane di gestazione",
y = "Peso (g)") +
theme_minimal()Il modello di regressione lineare multipla selezionato permette di spiegare una parte significativa della variabilità del peso neonatale. Le variabili risultate più predittive sono:
L’analisi dei residui ha confermato la bontà del modello: normalità, omoschedasticità e assenza di autocorrelazione. I valori influenti individuati (leverage, outlier, distanza di Cook) non compromettono la stabilità delle stime.
Le previsioni prodotte possono supportare il personale clinico nella gestione proattiva delle gravidanze a rischio. ```