Azienda: Neonatal Health Solutions
Obiettivo: Creare un modello statistico in grado di prevedere con precisione il peso dei neonati alla nascita, basandosi su variabili cliniche raccolte da tre ospedali. Il progetto mira a migliorare la gestione delle gravidanze ad alto rischio, ottimizzare le risorse ospedaliere e garantire migliori risultati per la salute neonatale.
Il progetto si inserisce all’interno di un contesto di crescente attenzione verso la prevenzione delle complicazioni neonatali. La possibilità di prevedere il peso alla nascita dei neonati rappresenta un’opportunità fondamentale per migliorare la pianificazione clinica e ridurre i rischi associati a nascite problematiche, come parti prematuri o neonati con basso peso. Di seguito, i principali benefici che questo progetto porterà all’azienda e al settore sanitario:
Miglioramento delle previsioni cliniche: Il peso del neonato è un indicatore chiave della sua salute. Avere un modello predittivo accurato consente al personale medico di intervenire tempestivamente in caso di anomalie, riducendo le complicazioni perinatali come le difficoltà respiratorie o l’ipoglicemia.
Ottimizzazione delle risorse ospedaliere: Sapere in anticipo quali neonati potrebbero avere bisogno di cure intensive aiuta a organizzare le risorse umane e tecnologiche degli ospedali in modo efficiente. Questo si traduce in una riduzione dei costi operativi e una migliore pianificazione dell’utilizzo delle unità di terapia intensiva neonatale (TIN).
Prevenzione e identificazione dei fattori di rischio: Il modello potrà evidenziare i fattori che maggiormente influenzano negativamente il peso del neonato (come il fumo materno, gravidanze multiple o età avanzata della madre). Queste informazioni sono preziose per la prevenzione e la gestione personalizzata delle gravidanze, permettendo interventi proattivi in caso di rischio elevato.
Valutazione delle pratiche ospedaliere: Attraverso un’analisi comparativa tra i tre ospedali coinvolti, l’azienda potrà identificare eventuali differenze nei risultati clinici, come una maggiore incidenza di parti cesarei in una determinata struttura. Ciò consente di monitorare la qualità delle pratiche e armonizzare i protocolli tra i diversi centri ospedalieri, migliorando la coerenza delle cure.
Supporto alla pianificazione strategica: L’analisi dei dati e le previsioni possono essere utilizzate per prendere decisioni informate non solo a livello clinico ma anche strategico. L’azienda potrà sfruttare queste informazioni per implementare nuove politiche di salute pubblica, garantendo un impatto positivo sui tassi di mortalità e morbilità neonatale.
library(ggplot2)
library(moments)
library(patchwork)
library(car)
## Loading required package: carData
library(lmtest)
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(MASS)
##
## Attaching package: 'MASS'
## The following object is masked from 'package:patchwork':
##
## area
library(pscl)
## Classes and Methods for R originally developed in the
## Political Science Computational Laboratory
## Department of Political Science
## Stanford University (2002-2015),
## by and under the direction of Simon Jackman.
## hurdle and zeroinfl functions by Achim Zeileis.
library(scatterplot3d)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ lubridate 1.9.4 ✔ tibble 3.3.0
## ✔ purrr 1.1.0 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ dplyr::recode() masks car::recode()
## ✖ dplyr::select() masks MASS::select()
## ✖ purrr::some() masks car::some()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Per costruire il modello predittivo, abbiamo raccolto dati su 2500 neonati provenienti da tre ospedali. Le variabili raccolte includono:
L’obiettivo principale è identificare quali di queste variabili sono più predittive del peso alla nascita, con un focus particolare sull’impatto del fumo materno e delle settimane di gestazione, che potrebbero indicare nascite premature.
dati <- read.csv("neonati.csv",sep = ",",stringsAsFactors = T)
knitr::kable(head(dati,5))
Anni.madre | N.gravidanze | Fumatrici | Gestazione | Peso | Lunghezza | Cranio | Tipo.parto | Ospedale | Sesso |
---|---|---|---|---|---|---|---|---|---|
26 | 0 | 0 | 42 | 3380 | 490 | 325 | Nat | osp3 | M |
21 | 2 | 0 | 39 | 3150 | 490 | 345 | Nat | osp1 | F |
34 | 3 | 0 | 38 | 3640 | 500 | 375 | Nat | osp2 | M |
28 | 1 | 0 | 41 | 3690 | 515 | 365 | Nat | osp2 | M |
20 | 0 | 0 | 38 | 3700 | 480 | 335 | Nat | osp3 | F |
N <- dim(dati) [1]
cat("Il dataset contiene",N,"osservazioni\n")
## Il dataset contiene 2500 osservazioni
duplicati <- dati %>% group_by_all() %>% filter(n() > 1) %>% ungroup()
cat("Il dataset contiene",dim(duplicati) [1] ,"duplicati\n") #non ci sono duplicati
## Il dataset contiene 0 duplicati
attach(dati)
Nella prima fase, esploreremo le variabili attraverso un’analisi descrittiva per comprenderne la distribuzione e identificare eventuali outlier o anomalie.
str(dati)
## '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 ...
summary(dati)
## 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
Che tipo di variabili presenta il dataset?
Variabili quantitative continua
Variabili quantitative discrete
Variabili qualitative dummy (o fattori)
Variabili qualitative nominali
Ma andiamo ancora più nel dettaglio.
indici_vis <- function(data, feature){
#Gestione dati e label
feature_name <- deparse(substitute(feature)) # nome come stringa
values <- pull(data, {{feature}}) # estrae il vettore
# Calcoli statistici
mu <- mean(values, na.rm = TRUE)
sigma <- sd(values, na.rm = TRUE)
cv <- sigma / mu
sk <- skewness(values, na.rm = TRUE)
ku <- kurtosis(values, na.rm = TRUE) - 3 # kurtosi centrata
# Crea df per la label informativa
info_label <- data.frame(x = mu, y = 0,
label = paste0("μ = ", round(mu,2),
"\nσ = ", round(sigma,2),
"\nCV = ", round(cv,2),
"\nSkew = ", round(sk,2),
"\nKurt = ", round(ku,2)))
# Quantili
q_vals <- quantile(values, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
mean_val <- mean(values, na.rm = TRUE)
# Plot dei record ordinati
record_plot <- ggplot() +
geom_point(aes(x = seq_along(values), y = sort(values))) +
geom_hline(yintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_hline(yintercept = q_vals, color = "red", linetype = "dotted" , linewidth = 0.7) +
geom_label(aes(x = length(values)*0.9, y = mu),
label = paste0("Media: ", round(mu, 1)),
color = "blue", size = 3) +
geom_label(data = data.frame(y = q_vals, label = names(q_vals)),
aes(x = length(values)*0.9, y = y, label = paste0(label, ": ", round(y, 1))),
color = "red", size = 3) +
labs(title = "Distribuzione osservazioni ordinate",
x = "n. osservazione", y = "Valore osservazione") +
theme_minimal()
# Boxplot
box_plot <- ggplot(data, aes(y = {{feature}})) +
geom_boxplot(fill = "tan1") +
geom_hline(yintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_hline(yintercept = q_vals, color = "red", linetype = "dotted", linewidth = 0.7) +
labs(title = "Boxplot",
y = "Valore osservazione") +
theme_minimal()+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
# Grafico della densità con box dei valori
density_plot <- ggplot(data, aes(x = {{feature}})) +
geom_density(fill = "lightblue", color = "black", alpha = 0.6) +
geom_vline(xintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_label(data = info_label, aes(x = x, y = y, label = label),
hjust = -0.1, vjust = -0.1, size = 3, fill = "white", label.size = 0.3) +
labs(title = "Stima della densità di probabilità",
x = "Valore osservazione", y = "Densità osservazione") +
theme_minimal()
print((record_plot + box_plot) +
plot_annotation(title = paste("Analisi di:", feature_name,"\n"),
theme = theme(plot.title = element_text(size = 16))))
print(density_plot)
}
indici_vis(dati,Peso) # sotto 1000 gr?
indici_vis(dati,Anni.madre) # Gravidanza a meno di 10 anni da escudere
indici_vis(dati,Gestazione) # Sotto le 30 settimane sono gravidanze premature
indici_vis(dati,Lunghezza)
indici_vis(dati,Cranio)
distr_freq_Ngrav<-as.data.frame(
cbind(
ni=table(N.gravidanze),
fi=round(table(N.gravidanze)/N,2)))
knitr::kable(distr_freq_Ngrav) # 10-12 gravidanze??
ni | fi | |
---|---|---|
0 | 1096 | 0.44 |
1 | 818 | 0.33 |
2 | 340 | 0.14 |
3 | 150 | 0.06 |
4 | 48 | 0.02 |
5 | 21 | 0.01 |
6 | 11 | 0.00 |
7 | 1 | 0.00 |
8 | 8 | 0.00 |
9 | 2 | 0.00 |
10 | 3 | 0.00 |
11 | 1 | 0.00 |
12 | 1 | 0.00 |
distr_freq_fumo<-as.data.frame(
cbind(
ni=table(Fumatrici),
fi=round(table(Fumatrici)/N,2)))
knitr::kable(distr_freq_fumo) # Non fumatori poco rappresentati. Solo 4%
ni | fi | |
---|---|---|
0 | 2396 | 0.96 |
1 | 104 | 0.04 |
distr_freq_parto<-as.data.frame(
cbind(
ni=table(Tipo.parto),
fi=round(table(Tipo.parto)/N,2)))
knitr::kable(distr_freq_parto) # Prevalenza di parti narurtali
ni | fi | |
---|---|---|
Ces | 728 | 0.29 |
Nat | 1772 | 0.71 |
distr_freq_osped<-as.data.frame(
cbind(
ni=table(Ospedale),
fi=round(table(Ospedale)/N,2)))
knitr::kable(distr_freq_osped) # Dati perfettamente distribuiti tra i 3 ospedali
ni | fi | |
---|---|---|
osp1 | 816 | 0.33 |
osp2 | 849 | 0.34 |
osp3 | 835 | 0.33 |
distr_freq_sesso<-as.data.frame(
cbind(
ni=table(Sesso),
fi=round(table(Sesso)/N,2)))
knitr::kable(distr_freq_sesso) # Equa distribuzione tra maschi e femmine
ni | fi | |
---|---|---|
F | 1256 | 0.5 |
M | 1244 | 0.5 |
Considerazione dall’analisi delle variabili
Commentiamo le variabili in analisi in termini di distribuzione, outlier e anomalie
Queste considerazioni son rintracciabili nei seguenti subset:
knitr::kable(subset(dati, Anni.madre < 10))
Anni.madre | N.gravidanze | Fumatrici | Gestazione | Peso | Lunghezza | Cranio | Tipo.parto | Ospedale | Sesso | |
---|---|---|---|---|---|---|---|---|---|---|
1152 | 1 | 1 | 0 | 41 | 3250 | 490 | 350 | Nat | osp2 | F |
1380 | 0 | 0 | 0 | 39 | 3060 | 490 | 330 | Nat | osp3 | M |
knitr::kable(subset(dati, Gestazione < 30))
Anni.madre | N.gravidanze | Fumatrici | Gestazione | Peso | Lunghezza | Cranio | Tipo.parto | Ospedale | Sesso | |
---|---|---|---|---|---|---|---|---|---|---|
310 | 40 | 3 | 0 | 28 | 1560 | 420 | 379 | Nat | osp3 | F |
378 | 27 | 0 | 0 | 28 | 1285 | 400 | 274 | Nat | osp1 | F |
805 | 30 | 2 | 0 | 29 | 1190 | 360 | 272 | Nat | osp2 | F |
928 | 25 | 0 | 0 | 28 | 830 | 310 | 254 | Nat | osp1 | F |
1385 | 33 | 0 | 0 | 29 | 1620 | 410 | 292 | Nat | osp3 | F |
1429 | 24 | 4 | 0 | 29 | 1280 | 390 | 355 | Nat | osp1 | F |
1780 | 25 | 2 | 0 | 25 | 900 | 325 | 253 | Nat | osp3 | F |
2120 | 32 | 0 | 0 | 27 | 1140 | 370 | 267 | Nat | osp3 | F |
2175 | 37 | 8 | 0 | 28 | 930 | 355 | 235 | Nat | osp1 | F |
2437 | 28 | 1 | 0 | 27 | 980 | 320 | 265 | Nat | osp1 | M |
2452 | 28 | 0 | 0 | 26 | 930 | 345 | 245 | Ces | osp3 | F |
knitr::kable(subset(dati, Peso < 1000))
Anni.madre | N.gravidanze | Fumatrici | Gestazione | Peso | Lunghezza | Cranio | Tipo.parto | Ospedale | Sesso | |
---|---|---|---|---|---|---|---|---|---|---|
928 | 25 | 0 | 0 | 28 | 830 | 310 | 254 | Nat | osp1 | F |
1619 | 31 | 0 | 0 | 31 | 990 | 340 | 278 | Ces | osp2 | F |
1780 | 25 | 2 | 0 | 25 | 900 | 325 | 253 | Nat | osp3 | F |
2175 | 37 | 8 | 0 | 28 | 930 | 355 | 235 | Nat | osp1 | F |
2437 | 28 | 1 | 0 | 27 | 980 | 320 | 265 | Nat | osp1 | M |
2452 | 28 | 0 | 0 | 26 | 930 | 345 | 245 | Ces | osp3 | F |
#knitr::kable(subset(dati, Fumatrici==TRUE))
Inoltre si saggeranno le seguenti ipotesi con i test adatti:
Facciamo innanzitutto un controllo sui dati incrociando i parti cesarei agli ospedali coinvolti
mean(Tipo.parto == 'Ces')
## [1] 0.2912
# Filtra solo i casi con parto = "Ces"
dati_ces <- subset(dati, Tipo.parto == "Ces")
N_ces <- nrow(dati_ces)
distr_freq <- as.data.frame(
cbind(
ni = table(dati_ces$Ospedale),
fi = round(table(dati_ces$Ospedale) / N_ces, 2)
)
)
knitr::kable(distr_freq)
ni | fi | |
---|---|---|
osp1 | 242 | 0.33 |
osp2 | 254 | 0.35 |
osp3 | 232 | 0.32 |
Si può creare una tabella di contingenza e usare un test del Chiquadroto per saggiare l’ipotesi di indipendenza tra le variabili Ospedale e Tipo.parto
tab_cont <- table(Tipo.parto, Ospedale)
tab_cont
## Ospedale
## Tipo.parto osp1 osp2 osp3
## Ces 242 254 232
## Nat 574 595 603
# Per la visualizzazione: install.packages("ggpubr")
ggpubr::ggballoonplot(data=as.data.frame(tab_cont), fill="blue")
# Calcolo delle attese
attese <- tab_cont
for(i in 1:nrow(tab_cont)){
for(j in 1:ncol(tab_cont)){
attese[i,j] <- margin.table(tab_cont,1)[i] * margin.table(tab_cont,2)[j] / margin.table(tab_cont)
}
}
# Mentre le osservate sono
osservate<-tab_cont
# Test fatto a mano
chiquadro.manuale <- sum((osservate-attese)^2/attese)
chiquadro.manuale
## [1] 1.097202
plot(density(rchisq(1000000,df = 2)), xlim=c(0,40))
abline(v=qchisq(0.95,2),col=2)
points(chiquadro.manuale,0,cex=3,col=4)
# Usando la funzione chisq.test
test.indipendenza <- chisq.test(tab_cont)
test.indipendenza
##
## Pearson's Chi-squared test
##
## data: tab_cont
## X-squared = 1.0972, df = 2, p-value = 0.5778
Con un p-value di 0.5778 e un alpha di 0.05, accetto l’ipotesi nulla di indipendenza: i dati raccolti ci dicono che non si apprezzano differenze di tipo di parto tra i diversi ospedali.
Per il peso, in accordo a quanto dichiarato dal WHO (fonte: https://www.who.int/tools/child-growth-standards/standards/weight-for-age) la media dei neonati risulta:
Visto che il campione presenta una equidistribuzione di maschi-femmine, è possibile considerare un valore medio come media della popolazione (a valle faremo ulteriore considerazioni). Le ipotesi del test quindi diventano:
Usiamo un t-test a causa della dimensione ridotta del campione, della distribuzione non normale della variabile e della varianza della popolazione non nota a priori (varia tanto geograficamente).
Effetuiamo il test di normalità della variabile peso per confermare la nostra affermazione di non normalità e poi passiamo al t test per confronto campione-popolazione.
shapiro.test(Peso)
##
## Shapiro-Wilk normality test
##
## data: Peso
## W = 0.97066, p-value < 2.2e-16
# Peso ha una distribuzione non normale
mu_peso_popol <- 3250 #media delle medie della popolazione tra maschi e femmine
risu_ttest <- t.test(Peso,mu=mu_peso_popol) #di default two.sided e 0.95 di conf.level
risu_ttest
##
## One Sample t-test
##
## data: Peso
## t = 3.2456, df = 2499, p-value = 0.001188
## alternative hypothesis: true mean is not equal to 3250
## 95 percent confidence interval:
## 3263.490 3304.672
## sample estimates:
## mean of x
## 3284.081
plot(density(rt(100000,2499)),xlim=c(-4,4))
abline(v=qt(0.025,2499))
qt(0.025,2499)
## [1] -1.960914
abline(v=qt(0.975,2499))
qt(0.975,2499)
## [1] 1.960914
# abline(v=risu_ttest$statistic, col=2)
points(risu_ttest$statistic,0,cex=3,col=2)
Con un p-value di 0.0012 e un alpha di 0.05, rifiuto l’ipotesi nulla: la media della popolazione differisce significativamente dalla media del campione. NOTA si potrebbe approfondire differenziando il campione in base al sesso, considerando però che si avrebbero sempre meno dati a disposizione.
cat("Media popolazione neonati maschi: 3300 gr")
## Media popolazione neonati maschi: 3300 gr
mean(subset(Peso, Sesso == 'M'))
## [1] 3408.215
cat("Media popolazione neonati maschi: 3200 gr")
## Media popolazione neonati maschi: 3200 gr
mean(subset(Peso, Sesso == 'F'))
## [1] 3161.132
Passando alla lunghezza, in accordo a quanto dichiarato dal WHO (fonte: https://www.who.int/tools/child-growth-standards/standards/length-height-for-age), la media dei neonati risulta di:
Visto che il campione presenta una equidistribuzione di maschi-femmine, è possibile considerare un valore medio come media della popolazione. Analogamente a quanto visto per il peso:
shapiro.test(Lunghezza)
##
## Shapiro-Wilk normality test
##
## data: Lunghezza
## W = 0.90941, p-value < 2.2e-16
Le ipotesi del test quindi diventano:
mu_lungh_popol <- 495 #media delle medie della popolazione tra maschi e femmine
risu_ttest <- t.test(Lunghezza,mu=mu_lungh_popol)
risu_ttest
##
## One Sample t-test
##
## data: Lunghezza
## t = -0.58514, df = 2499, p-value = 0.5585
## alternative hypothesis: true mean is not equal to 495
## 95 percent confidence interval:
## 493.6598 495.7242
## sample estimates:
## mean of x
## 494.692
plot(density(rt(100000,2499)),xlim=c(-4,4))
abline(v=qt(0.025,2499))
qt(0.025,2499)
## [1] -1.960914
abline(v=qt(0.975,2499))
qt(0.975,2499)
## [1] 1.960914
# abline(v=risu_ttest$statistic, col=2)
points(risu_ttest$statistic,0,cex=3,col=2)
Con un p-value di 0.5585 e un alpha di 0.05, non posso rifiutare l’ipotesi nulla: la media campionaria è signitificativamente uguale a quella della popolazione
Prepariamo i dati necessari al test:
Lunghezza.M <- subset(Lunghezza, Sesso == 'M')
Lunghezza.F <- subset(Lunghezza, Sesso == 'F')
Cranio.M <- subset(Cranio, Sesso == 'M')
Cranio.F <- subset(Cranio, Sesso == 'F')
Usiamo il t test per il confronto delle medie campionarie. Occorre verificare che:
Le ipotesi del test quindi diventano:
Sappiamo che Lunghezza non è una variabile con una distribuzione normale. Lo stesso vale per Cranio, di cui a seguire il test di normalità. Date queste premesse, usiamo un test non parametrico: usiamo pairwise.wilcox.test.
summary(Lunghezza[Sesso=='M'])
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 320.0 490.0 500.0 499.7 515.0 560.0
summary(Lunghezza[Sesso=='F'])
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 310.0 480.0 490.0 489.8 505.0 565.0
boxplot(Lunghezza~Sesso, ylab = 'Lunghezza [mm]')
pairwise.wilcox.test(Lunghezza, Sesso,
paired = F, #non appaiati
pool.sd = T,
p.adjust.method = "none") # Son significativamente diversi
##
## Pairwise comparisons using Wilcoxon rank sum test with continuity correction
##
## data: Lunghezza and Sesso
##
## F
## M <2e-16
##
## P value adjustment method: none
shapiro.test(Cranio)
##
## Shapiro-Wilk normality test
##
## data: Cranio
## W = 0.96357, p-value < 2.2e-16
summary(Cranio[Sesso=='M'])
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 265.0 334.0 343.0 342.4 352.0 390.0
summary(Cranio[Sesso=='F'])
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 235.0 330.0 340.0 337.6 348.2 390.0
boxplot(Cranio~Sesso, ylab = 'Cranio [mm]')
pairwise.wilcox.test(Cranio, Sesso) # Son significativamente diversi
##
## Pairwise comparisons using Wilcoxon rank sum test with continuity correction
##
## data: Cranio and Sesso
##
## F
## M 9.6e-15
##
## P value adjustment method: holm
In entrambi casi, non si può accettare l’ipotesi nulla (in entrambi i casi con un p-value prossimo allo zero). Possiamo affermare che esistono differenze significative nelle misure antropometriche tra i due sessi: sia sulla lunghezza che sulle misure del cranio.
Verrà sviluppato un modello di regressione lineare multipla che includa tutte le variabili rilevanti. In questo modo, potremo quantificare l’impatto di ciascuna variabile indipendente sul peso del neonato ed eventuali interazioni. Ad esempio, ci aspettiamo che una maggiore durata della gestazione aumenterebbe in media il peso del neonato.
Dalle precedenti analisi emerge che la variabile Peso non presenta una distribuzione normale (con un p-value prossimo allo zero).
Potremmo quindi vagliare l’ipotesi di fare una correzione sulla variabile risposta oppure usare i GLM, ma prima di tutto andiamo a studiare la linearità del modello e se le ipotesi alla base vengono rispettate.
Iniziamo con lo studio della correlazione delle variabili in gioco.
# Studiamo le correlazioni in anticipo
# round(cor(dati),2) #non è possobile per via delle variabili qualitative
# Mentre utilizzando la funzione pair
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
{
usr <- par("usr")
on.exit(par(usr = usr))
par(usr = c(0, 1, 0, 1))
r <- (cor(x, y))
txt <- format(c(r, 1), digits = digits)[1]
txt <- paste0(prefix, txt)
if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
text(0.5, 0.5, txt, cex = 1.5)
}
#correlazioni
pairs(dati,lower.panel=panel.cor, upper.panel=panel.smooth)
Si può osservare una alta correlazione con Gestazione, Lunghezza, Cranio (e in parte Sesso).
Verifichiamo a parte le variabili qualitative. Lo faremo sia graficamente (boxplot) che non un ttest.
par(mfrow=c(1,2))
boxplot(Peso, ylab='Peso [gr]', xlab='Campione')
boxplot(Peso~Sesso, ylab = 'Peso [gr]')
t.test(Peso~Sesso) #differenze significative
##
## 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
par(mfrow=c(1,2))
boxplot(Peso, ylab='Peso [gr]', xlab='Campione')
boxplot(Peso~Ospedale, ylab = 'Peso [gr]')
pairwise.wilcox.test(Peso, Ospedale) #differenze non significative
##
## Pairwise comparisons using Wilcoxon rank sum test with continuity correction
##
## data: Peso and Ospedale
##
## osp1 osp2
## osp2 0.64 -
## osp3 0.49 0.26
##
## P value adjustment method: holm
par(mfrow=c(1,2))
boxplot(Peso, ylab='Peso [gr]', xlab='Campione')
boxplot(Peso~Tipo.parto, ylab = 'Peso [gr]')
t.test(Peso~Tipo.parto) #differenze non significative
##
## Welch Two Sample t-test
##
## data: Peso by Tipo.parto
## t = -0.12968, df = 1493, p-value = 0.8968
## alternative hypothesis: true difference in means between group Ces and group Nat is not equal to 0
## 95 percent confidence interval:
## -46.27992 40.54037
## sample estimates:
## mean in group Ces mean in group Nat
## 3282.047 3284.916
Viene confermata la correlazione tra Peso e Sesso e dai test possiamo affermare che esistono differenze significative di peso nei neonati di diverso sesso. Al contrario, sulla base dei dati a disposizione, non si osservano differenze significative di peso nei diversi ospedali e in base al tipo di parto.
Siamo pronti quindi a costruire passo passo un primo modello lineare che comprenda tutte le variabili. Negli step successivi andremo a migliorare il modello.
mod<-lm(Peso ~ . ,
data=dati)
summary(mod)
##
## Call:
## lm(formula = Peso ~ ., 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
# modelsummary(mod, output = "markdown")
# stargazer(mod, type = "html", title = "Modello di regressione completo")
AIC(mod)
## [1] 35171.95
BIC(mod)
## [1] 35241.84
Leggendo il summary si nota che le variabili più significative sono le settimane di gestazione, seguite da la variabili antropometriche Lunghezza e Cranio. I regressori non significativi sono Anni.madre, Fumatrici, Ospedale e, in seconda fascia, N.gravidanze e Tipo.parto.
Per far fronte alla non normalità della variabile peso (con possibili impatti sulla distribuzione dei residui) potremmo provare altre strategie: trasformazione di boxcox oppure modelli GLM. Per effettuare un confronto diretto, ci soffermeremo sui GLM e usero il BIC come parametro di confronto.
# boxcox(lm(Peso ~ . , data = dati))
# lambda <- 0.5
# shapiro.test(Peso^0.5)
# model_boxcox <- lm(Peso^0.5 ~ . , data = dati)
# summary(model_boxcox)
model_gamma <- glm(Peso ~ . , family = Gamma(link="log"), data = dati)
summary(model_gamma)
##
## Call:
## glm(formula = Peso ~ ., family = Gamma(link = "log"), data = dati)
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.417e+00 4.580e-02 96.441 < 2e-16 ***
## Anni.madre 2.799e-04 3.670e-04 0.763 0.4457
## N.gravidanze 3.581e-03 1.511e-03 2.370 0.0178 *
## Fumatrici -7.025e-03 8.925e-03 -0.787 0.4313
## Gestazione 1.883e-02 1.238e-03 15.214 < 2e-16 ***
## Lunghezza 3.401e-03 9.746e-05 34.897 < 2e-16 ***
## Cranio 3.604e-03 1.381e-04 26.098 < 2e-16 ***
## Tipo.partoNat 6.746e-03 3.917e-03 1.722 0.0851 .
## Ospedaleosp2 -2.857e-03 4.355e-03 -0.656 0.5119
## Ospedaleosp3 9.416e-03 4.374e-03 2.153 0.0314 *
## SessoM 1.793e-02 3.623e-03 4.949 7.96e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for Gamma family taken to be 0.0078821)
##
## Null deviance: 75.182 on 2499 degrees of freedom
## Residual deviance: 18.828 on 2489 degrees of freedom
## AIC: 35309
##
## Number of Fisher Scoring iterations: 5
# Confrontiamo i due modelli
BIC(mod,model_gamma)
## df BIC
## mod 12 35241.84
## model_gamma 12 35378.94
Dal confronto emerge che il modello di regressione GLM presenta un BIC maggiore del primo modello base sviluppato. Nel paragrafo successivo, approfondiremo la questione.
Attraverso tecniche di selezione del modello, come la minimizzazione del criterio di informazione di Akaike (AIC) o di Bayes (BIC), selezioneremo il modello più parsimonioso, eliminando le variabili non significative. Verranno considerati anche modelli con interazioni tra le variabili e possibili effetti non lineari.
Iniziamo la procedura stepwise eliminando i regrossori non significativi individuati in precedenza partendo da Anni.madre e Fumatrici, per poi continuare con Ospedale, Tipo.gravidanza e N.gravidanza.
# Procedura stepwise
mod1<-lm(Peso ~ . -Anni.madre -Fumatrici,
data=dati)
summary(mod1)
##
## Call:
## lm(formula = Peso ~ . - Anni.madre - Fumatrici, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1113.18 -181.16 -16.58 161.01 2620.19
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6707.4293 135.9438 -49.340 < 2e-16 ***
## N.gravidanze 12.3619 4.3325 2.853 0.00436 **
## Gestazione 31.9909 3.7896 8.442 < 2e-16 ***
## Lunghezza 10.3086 0.3004 34.316 < 2e-16 ***
## Cranio 10.4922 0.4254 24.661 < 2e-16 ***
## Tipo.partoNat 29.2803 12.0817 2.424 0.01544 *
## Ospedaleosp2 -11.0227 13.4363 -0.820 0.41209
## Ospedaleosp3 28.6408 13.4886 2.123 0.03382 *
## SessoM 77.4412 11.1756 6.930 5.36e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 273.9 on 2491 degrees of freedom
## Multiple R-squared: 0.7287, Adjusted R-squared: 0.7278
## F-statistic: 836.3 on 8 and 2491 DF, p-value: < 2.2e-16
# l'R2 adj rimane più o meno lo stesso. Eliminiamo anche Ospedale
mod2<-update(mod1, ~ . -Ospedale,
data=dati)
summary(mod2)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Tipo.parto + Sesso, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1129.31 -181.70 -16.31 161.07 2638.85
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6707.2971 135.9911 -49.322 < 2e-16 ***
## N.gravidanze 12.7558 4.3366 2.941 0.0033 **
## Gestazione 32.2713 3.7941 8.506 < 2e-16 ***
## Lunghezza 10.2864 0.3007 34.207 < 2e-16 ***
## Cranio 10.5057 0.4260 24.659 < 2e-16 ***
## Tipo.partoNat 30.0342 12.0969 2.483 0.0131 *
## SessoM 77.9285 11.1905 6.964 4.22e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 274.3 on 2493 degrees of freedom
## Multiple R-squared: 0.7277, Adjusted R-squared: 0.727
## F-statistic: 1110 on 6 and 2493 DF, p-value: < 2.2e-16
# Stesso discorso. L'R2 adj rimane più o meno lo stesso. Eliminiamo anche Tipo.parto e N.gravidanze
mod3<-update(mod2, ~ . -Tipo.parto -N.gravidanze,
data=dati)
summary(mod3)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso,
## data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1138.2 -184.3 -17.6 163.3 2627.3
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6651.1188 135.5172 -49.080 < 2e-16 ***
## Gestazione 31.2737 3.7856 8.261 2.31e-16 ***
## Lunghezza 10.2054 0.3007 33.939 < 2e-16 ***
## Cranio 10.6704 0.4245 25.139 < 2e-16 ***
## SessoM 79.1049 11.2117 7.056 2.22e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 275 on 2495 degrees of freedom
## Multiple R-squared: 0.7261, Adjusted R-squared: 0.7257
## F-statistic: 1654 on 4 and 2495 DF, p-value: < 2.2e-16
#AIC(mod3,mod2,mod1,mod,model_gamma)
#anova(mod3,mod2,mod1,mod,model_gamma)
BIC(mod3,mod2,mod1,mod,model_gamma)
## df BIC
## mod3 6 35220.54
## mod2 8 35221.75
## mod1 10 35228.03
## mod 12 35241.84
## model_gamma 12 35378.94
Usando come criterio la minimizzazione del BIC, il modello migliore sembra essere il modello 3 in cui il Peso è linearmente dipendente da Gestazione, Lunghezza, Cranio e Sesso. A questo punto possiamo prevare la procedura automatica e comparare i risultati. Come ultimo test prima della procedura automatica, proviamo a reintrodurre il fumo ma con delle relazioni non lineari.
mod4<-update(mod3, ~ . + Fumatrici*Anni.madre,
data=dati)
summary(mod4)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso +
## Fumatrici + Anni.madre + Fumatrici:Anni.madre, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1164.20 -184.25 -14.04 162.73 2615.88
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6724.5661 141.3994 -47.557 < 2e-16 ***
## Gestazione 32.4306 3.8264 8.475 < 2e-16 ***
## Lunghezza 10.2031 0.3012 33.878 < 2e-16 ***
## Cranio 10.6000 0.4260 24.883 < 2e-16 ***
## SessoM 78.8492 11.2127 7.032 2.62e-12 ***
## Fumatrici -39.9458 146.9672 -0.272 0.7858
## Anni.madre 1.9410 1.0806 1.796 0.0726 .
## Fumatrici:Anni.madre 0.4584 5.1005 0.090 0.9284
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 274.9 on 2492 degrees of freedom
## Multiple R-squared: 0.7266, Adjusted R-squared: 0.7258
## F-statistic: 946 on 7 and 2492 DF, p-value: < 2.2e-16
BIC(mod4,mod3,mod2,mod1,mod,model_gamma)
## df BIC
## mod4 9 35239.65
## mod3 6 35220.54
## mod2 8 35221.75
## mod1 10 35228.03
## mod 12 35241.84
## model_gamma 12 35378.94
La variabile Fumatrici si conferma essere non significativa. Il mod 3 rimane il migliore.
Procedura automatica
# Per ripetere tutta la procedura in automatico
stepwise.mod <- MASS::stepAIC(mod,
direction = "both",
k=log(N)) #AIC con k=2, log(n) per BIC
## Start: AIC=28139.32
## Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione + Lunghezza +
## Cranio + Tipo.parto + Ospedale + Sesso
##
## Df Sum of Sq RSS AIC
## - Anni.madre 1 46578 186809099 28132
## - Fumatrici 1 90019 186852540 28133
## - Ospedale 2 685979 187448501 28133
## - N.gravidanze 1 438452 187200974 28137
## - Tipo.parto 1 447929 187210450 28138
## <none> 186762521 28139
## - Sesso 1 3611021 190373542 28179
## - Gestazione 1 5458403 192220925 28204
## - Cranio 1 45326172 232088693 28675
## - Lunghezza 1 87951062 274713583 29096
##
## Step: AIC=28132.12
## Peso ~ N.gravidanze + Fumatrici + Gestazione + Lunghezza + Cranio +
## Tipo.parto + Ospedale + Sesso
##
## Df Sum of Sq RSS AIC
## - Fumatrici 1 90897 186899996 28126
## - Ospedale 2 692738 187501837 28126
## - Tipo.parto 1 448222 187257321 28130
## <none> 186809099 28132
## - N.gravidanze 1 633756 187442855 28133
## + Anni.madre 1 46578 186762521 28139
## - Sesso 1 3618736 190427835 28172
## - Gestazione 1 5412879 192221978 28196
## - Cranio 1 45588236 232397335 28670
## - Lunghezza 1 87950050 274759149 29089
##
## Step: AIC=28125.51
## Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Tipo.parto +
## Ospedale + Sesso
##
## Df Sum of Sq RSS AIC
## - Ospedale 2 701680 187601677 28119
## - Tipo.parto 1 440684 187340680 28124
## <none> 186899996 28126
## - N.gravidanze 1 610840 187510837 28126
## + Fumatrici 1 90897 186809099 28132
## + Anni.madre 1 47456 186852540 28133
## - Sesso 1 3602797 190502794 28165
## - Gestazione 1 5346781 192246777 28188
## - Cranio 1 45632149 232532146 28664
## - Lunghezza 1 88355030 275255027 29086
##
## Step: AIC=28119.23
## Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Tipo.parto +
## Sesso
##
## Df Sum of Sq RSS AIC
## - Tipo.parto 1 463870 188065546 28118
## <none> 187601677 28119
## - N.gravidanze 1 651066 188252743 28120
## + Ospedale 2 701680 186899996 28126
## + Fumatrici 1 99840 187501837 28126
## + Anni.madre 1 54392 187547285 28126
## - Sesso 1 3649259 191250936 28160
## - Gestazione 1 5444109 193045786 28183
## - Cranio 1 45758101 233359778 28657
## - Lunghezza 1 88054432 275656108 29074
##
## Step: AIC=28117.58
## Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Sesso
##
## Df Sum of Sq RSS AIC
## <none> 188065546 28118
## - N.gravidanze 1 623141 188688687 28118
## + Tipo.parto 1 463870 187601677 28119
## + Ospedale 2 724866 187340680 28124
## + Fumatrici 1 91892 187973654 28124
## + Anni.madre 1 54816 188010731 28125
## - Sesso 1 3655292 191720838 28158
## - Gestazione 1 5464853 193530399 28181
## - Cranio 1 46108583 234174130 28658
## - Lunghezza 1 87632762 275698308 29066
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
summary(mod3)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso,
## data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1138.2 -184.3 -17.6 163.3 2627.3
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6651.1188 135.5172 -49.080 < 2e-16 ***
## Gestazione 31.2737 3.7856 8.261 2.31e-16 ***
## Lunghezza 10.2054 0.3007 33.939 < 2e-16 ***
## Cranio 10.6704 0.4245 25.139 < 2e-16 ***
## SessoM 79.1049 11.2117 7.056 2.22e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 275 on 2495 degrees of freedom
## Multiple R-squared: 0.7261, Adjusted R-squared: 0.7257
## F-statistic: 1654 on 4 and 2495 DF, p-value: < 2.2e-16
La procedura automatica permette di giungere ad un modello molto simile al miglior modello trovato manualmente con al sola aggiunta della variabile numero di gravidanze. A questo punto proviamo a costruire un glm usando le stesse variabili identificare dalla procedura stepwise.
model_gamma_2 <- glm(Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Sesso, family = Gamma(link="log"), data = dati)
model_gamma_2
##
## Call: glm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza +
## Cranio + Sesso, family = Gamma(link = "log"), data = dati)
##
## Coefficients:
## (Intercept) N.gravidanze Gestazione Lunghezza Cranio
## 4.432756 0.004017 0.018799 0.003386 0.003623
## SessoM
## 0.018088
##
## Degrees of Freedom: 2499 Total (i.e. Null); 2494 Residual
## Null Deviance: 75.18
## Residual Deviance: 18.93 AIC: 35310
BIC(mod3,model_gamma,stepwise.mod,model_gamma_2)
## df BIC
## mod3 6 35220.54
## model_gamma 12 35378.94
## stepwise.mod 7 35220.10
## model_gamma_2 7 35353.69
Il miglior modello, utilizzando come parametro di scelta il BIC, risulta essere il modello scelto con la procedura stepwise. Il modello presenta un R² aggiustato di circa 0.727.
Tuttavia, consideranzo l’importanza scientifica della variabile Fumatrici, la aggiungeremo al modello per futuri sviluppi del modello. Si fa notare che la sua introduzione fa aumentare leggermente il BIC ma non altera l’R2.
stepwise.mod.final<-update(stepwise.mod, ~ . + Fumatrici,
data=dati)
summary(stepwise.mod.final)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Sesso + Fumatrici, data = dati)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1150.3 -181.3 -15.7 163.0 2636.3
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6681.6714 135.7178 -49.232 < 2e-16 ***
## N.gravidanze 12.7185 4.3450 2.927 0.00345 **
## Gestazione 32.5914 3.8051 8.565 < 2e-16 ***
## Lunghezza 10.2341 0.3009 34.011 < 2e-16 ***
## Cranio 10.5359 0.4262 24.718 < 2e-16 ***
## SessoM 78.1713 11.2028 6.978 3.83e-12 ***
## Fumatrici -30.4634 27.5948 -1.104 0.26972
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 274.6 on 2493 degrees of freedom
## Multiple R-squared: 0.7271, Adjusted R-squared: 0.7265
## F-statistic: 1107 on 6 and 2493 DF, p-value: < 2.2e-16
BIC(stepwise.mod,stepwise.mod.final)
## df BIC
## stepwise.mod 7 35220.1
## stepwise.mod.final 8 35226.7
Una volta ottenuto il modello finale, valuteremo la sua capacità predittiva utilizzando metriche come R² e il Root Mean Squared Error (RMSE). Un’attenzione particolare sarà rivolta all’analisi dei residui e alla presenza di valori influenti, che potrebbero distorcere le previsioni, indagando su di essi.
L’analisi del modello parte dal controllo dei problemi di multicollinearità.
# Gli indicatori devono stare sotto alla soglia di 5
knitr::kable(car::vif(stepwise.mod.final), col.names = c('multi_collinearità_indice'))
multi_collinearità_indice | |
---|---|
N.gravidanze | 1.026120 |
Gestazione | 1.675575 |
Lunghezza | 2.078644 |
Cranio | 1.624603 |
Sesso | 1.040271 |
Fumatrici | 1.006607 |
Non sussistono problemi di multicollinearità. Passiamo all’analisi dei residui
# Aanlisi dei residui
par(mfrow=c(2,2))
plot(stepwise.mod.final)
# attenzione all'osservazione 1551
knitr::kable(dati[1551,])
Anni.madre | N.gravidanze | Fumatrici | Gestazione | Peso | Lunghezza | Cranio | Tipo.parto | Ospedale | Sesso | |
---|---|---|---|---|---|---|---|---|---|---|
1551 | 35 | 1 | 0 | 38 | 4370 | 315 | 374 | Nat | osp3 | F |
Abbiamo individuato una osservazione pericolosa che va oltre la distanza di cook di 0.5, avvicinandosi molto a 1. Effettuiamo quindi i singoli test per studiare passo passo come si comportano le osservazioni e i residui e valutare, quindi, la qualità del modello.
# Indagine solle osservazioni leverage
par(mfrow=c(1,1))
lev<-hatvalues(stepwise.mod.final)
plot(lev, xlab = "Osservazioni", ylab = "Valore di leverage", main = "Osservazioni Leverage")
p<-sum(lev)
n<-length(lev)
soglia=2*p/n
abline(h=soglia,col=2) # oss 1551 ben visibile
#lev[lev>soglia]
length(lev[lev>soglia]) # ci 152 osservazione che sono lontane dallo spazio dei regressori, delle variabili esplicative
## [1] 211
# Studio degli outliers
plot(rstudent(stepwise.mod.final), xlab = "Osservazioni", ylab = "Valore rstudent outlier", main = "Osservazioni Outliers")
abline(h=c(-2,2)) # oss 1551 ben visibile
# facciamo un ttest sugli outliers
car::outlierTest(stepwise.mod.final) # osservazione 1511, 155 e 1306 da capire come gestire
## rstudent unadjusted p-value Bonferroni p
## 1551 10.039719 2.8060e-23 7.0149e-20
## 155 5.022108 5.4723e-07 1.3681e-03
## 1306 4.823102 1.4986e-06 3.7465e-03
# Ed infine, distanza di cook per valutare entrambe le cose contemporaneamente
cook<-cooks.distance(stepwise.mod.final)
plot(cook,ylim = c(0,1), xlab = "Osservazioni", ylab = "Distanza di cooks", main = "Osservazioni secondo Cooks")
Viene confermata la ‘pericolosità’ dell’osservazione 1551. La rimozione di questa osservazione potrebbe cambiare sensibilmente le metriche del modello in analisi. Si può pensare di rimuovere il record e rianalizzare il modello. Oppure si possono fare indagini più approfondite sul perché si tale osservazione.
Passando ai residui.
# Test di normalità dei residui
shapiro.test(residuals(stepwise.mod.final))
##
## Shapiro-Wilk normality test
##
## data: residuals(stepwise.mod.final)
## W = 0.9741, p-value < 2.2e-16
# rifiuto l'ipotesi nulla, i residui non sono normali
plot(density(residuals(stepwise.mod.final)))
# Test di omeoschedasticità
lmtest::bptest(stepwise.mod.final)
##
## studentized Breusch-Pagan test
##
## data: stepwise.mod.final
## BP = 89.798, df = 6, p-value < 2.2e-16
# rifiuto l'ipotesi nulla, la varianza non è costante
# test di autocorrelazione
lmtest::dwtest(stepwise.mod.final)
##
## Durbin-Watson test
##
## data: stepwise.mod.final
## DW = 1.9542, p-value = 0.126
## alternative hypothesis: true autocorrelation is greater than 0
# Non posso rifiutare l'ipotesi nulla, i residui non sono autocorrelati
Valutiamo infine altre metriche come: MSE e RMSE. Tali parametri sono utili come parametri di confronto. Proviamo a confrontare il modello migliore con il glm del paragrafo precedente.
#Valutiamo anche l'errore quadratico medio
MSE<-function(y_oss,y_prev){
return(sum((y_oss-y_prev)^2)/length(y_prev))
}
RMSE<-function(y_oss,y_prev){
return(sqrt(MSE(y_oss, y_prev)))
}
mse_train_stepwise<-MSE(dati$Peso, fitted(stepwise.mod.final))
mse_train_stepwise
## [1] 75189.46
mse_train_gamma<-MSE(dati$Peso, fitted(model_gamma_2))
mse_train_gamma
## [1] 76332.72
rmse_train_stepwise<-RMSE(dati$Peso, fitted(stepwise.mod.final))
rmse_train_stepwise
## [1] 274.207
rmse_train_gamma<-RMSE(dati$Peso, fitted(model_gamma_2))
rmse_train_gamma
## [1] 276.2838
Ricapitolando, dall’analisi della qualità del modello emerge che:
Una volta validato il modello, lo useremo per fare previsioni pratiche. Ad esempio, potremo stimare il peso di una neonata considerando una madre alla terza gravidanza che partorirà alla 39esima settimana.
# Previsione
dato.esempio = data.frame(
N.gravidanze = 3,
Gestazione = 39,
Lunghezza = 450,
Cranio = 370,
Sesso = "M",
Fumatrici = 0
)
cat("Il neonato peserà gr", round(predict(stepwise.mod, newdata = dato.esempio),2))
## Il neonato peserà gr 3206.95
Infine, utilizzeremo grafici e rappresentazioni visive per comunicare i risultati del modello e mostrare le relazioni più significative tra le variabili. Ad esempio, potremmo visualizzare l’impatto del numero di settimane di gestazione e del fumo sul peso previsto.
Fumatrici.factor <- factor(Fumatrici, levels = c(0,1), labels = c("No","Si"))
ggplot(data=dati)+
geom_point(aes(x=Gestazione,
y=Peso,
col=Fumatrici.factor))+ #position = "jitter"
geom_smooth(aes(x=Gestazione,
y=Peso,
col=Fumatrici.factor),se=F,method = "lm")+
labs(title = "Relazione tra peso e settimane di gestazione",
x = "Gestazione [sett]", y = "Peso [gr]")
## `geom_smooth()` using formula = 'y ~ x'
Se si mette in relazione il peso previsto con le settimane di gestazione e la variabile Fumatrici, si possono osservare diverse cose:
ggplot(data=dati)+
geom_point(aes(x=Gestazione,
y=Peso,
col=Sesso))+ #position = "jitter"
geom_smooth(aes(x=Gestazione,
y=Peso,
col=Sesso),se=F,method = "lm")+
labs(title = "Relazione tra peso e settimane di gestazione",
x = "Gestazione [sett]", y = "Peso [gr]")
## `geom_smooth()` using formula = 'y ~ x'
Analizzando invece la relazione peso previsto con le settimane di gestazione in funzione del sesso, si possono osservare invece:
Infine, grafichiamo la relazione che vige tra il peso e le altre misure antropometriche Lunghezza e Cranio
#car::scatter3d(Peso~Lunghezza+Cranio)
scatterplot3d(Lunghezza, Cranio, Peso, pch=16, color="blue",
xlab="Lunghezza [mm]", ylab="Cranio [mm]", zlab="Peso [gr]",
main="Relazione tra peso e misure antropometriche")
Da notare la presenza della osservazione 1551 emersa dall’analisi del modello. Per il resto, si può osservare una certa relazione lineare tra il peso del neonato e le lunghezze antropometriche.
Il progetto di previsione del peso neonatale è un’iniziativa fondamentale per Neonatal Health Solutions. Attraverso l’uso di dati clinici dettagliati e strumenti di analisi statistica avanzati, possiamo contribuire a migliorare la qualità della cura prenatale, ridurre i rischi per i neonati e ottimizzare l’efficienza delle risorse ospedaliere. Questo progetto rappresenta un punto di svolta per l’azienda, consentendo non solo un miglioramento della pratica clinica ma anche l’implementazione di politiche sanitarie più informate e proattive.
In conclusione, lo studio preliminare dei dati ci ha permesso di analizzare le variabili a disposizione osservando in particolare la tipologia, come si distribuiscono e come si relazionano. A seguire le principali osservazioni raccolte dal paragrafo 2.1:
Per quanto riguarda il modello lineare sviluppato dall’analisi ci sono diverse considerazioni da fare:
Nonostante queste premesse abbiamo utilizzato il modello per visualizzare le relazioni che sussistono tra le variabili e, ad esempio, se si mette in relazione il peso previsto con le settimane di gestazione e le variabili Fumatrici e Sesso, si possono osservare emerge che: