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.
Il progetto è sviluppato per l’azienda Neonatal Health Solutions e ha come obiettivo la costruzione di un modello statistico predittivo del peso neonatale. Il dataset contiene 2500 osservazioni raccolte tra 3 ospedali, con variabili cliniche, demografiche relative a madre e neonato.
L’analisi si inserisce in un contesto di prevenzione dalle complicazioni neonatali e di ottimizzazione delle risorse ospedaliere.
#Load the data into R (CSV file)
data<-read.csv("neonati.csv",stringsAsFactors = FALSE )
#First rows of the Dataset
library(knitr)
kable(head(data), caption = "First rows of the Dataset")
| 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 |
| 32 | 0 | 0 | 40 | 3200 | 495 | 340 | Nat | osp2 | F |
attach(data)
#N. observations
N<- dim(data)[1]
cat("Il dataset contiene",N,"osservazioni.\n")
## Il dataset contiene 2500 osservazioni.
#Duplicate check
library(dplyr)
##
## Caricamento pacchetto: 'dplyr'
## I seguenti oggetti sono mascherati da 'package:stats':
##
## filter, lag
## I seguenti oggetti sono mascherati da 'package:base':
##
## intersect, setdiff, setequal, union
duplicati<- data%>% group_by_all()%>% filter(n()>1)%>%ungroup()
cat("Il dataset contiene", nrow(duplicati),"duplicati.\n")
## Il dataset contiene 0 duplicati.
Obiettivo: Individuare i fattori che influenzano significativamente il peso neonatale.
In particolare:
Valutare l’impatto del fumo materno.
Analizzare l’effetto della durata della gestazione (nascite premature vs a termine).
Confrontare le differenze tra sessi , tipi di parto, e ospedali.
Integrare le variabili antropometriche (lunghezza, cranio) e materne (età, gravidanze) in un modello di regressione.
Di seguito otteniamo le variabili presenti nel Dataset:
#Variables Names
kable(data.frame(Variables=names(data)), caption = "Variables Names\n")
| Variables |
|---|
| Anni.madre |
| N.gravidanze |
| Fumatrici |
| Gestazione |
| Peso |
| Lunghezza |
| Cranio |
| Tipo.parto |
| Ospedale |
| Sesso |
Organizziamo le variabili estratte dal Dataset all’interno di una tabella, riportando per ogni variabile:
[ Variabile | Tipo di Variabile | Descrizione ]
variabili_df <- data.frame(
Variabile = c("Anni.madre","N.gravidanze","Fumatrici","Gestazione","Peso",
"Lunghezza","Cranio","Tipo.parto","Ospedale","Sesso"),
Tipo = c("Quantitativa continua","Quantitativa discreta","Qualitativa dicotomica",
"Quantitativa discreta","Quantitativa continua","Quantitativa continua",
"Quantitativa continua","Qualitativa nominale","Qualitativa nominale",
"Qualitativa dicotomica"),
Descrizione = c("Età della madre (anni)",
"Numero di gravidanze",
"Stato fumo materno (0/1)",
"Durata gestazione (settimane)",
"Peso alla nascita (grammi)",
"Lunghezza neonato (millimetri)",
"Diametro craniale (millimetri)",
"Tipo di parto (Nat/Ces)",
"Ospedale di nascita (osp1/osp2/osp3)",
"Sesso neonato (M/F)")
)
kable(variabili_df, caption = "Variable, Type & Description")
| Variabile | Tipo | Descrizione |
|---|---|---|
| Anni.madre | Quantitativa continua | Età della madre (anni) |
| N.gravidanze | Quantitativa discreta | Numero di gravidanze |
| Fumatrici | Qualitativa dicotomica | Stato fumo materno (0/1) |
| Gestazione | Quantitativa discreta | Durata gestazione (settimane) |
| Peso | Quantitativa continua | Peso alla nascita (grammi) |
| Lunghezza | Quantitativa continua | Lunghezza neonato (millimetri) |
| Cranio | Quantitativa continua | Diametro craniale (millimetri) |
| Tipo.parto | Qualitativa nominale | Tipo di parto (Nat/Ces) |
| Ospedale | Qualitativa nominale | Ospedale di nascita (osp1/osp2/osp3) |
| Sesso | Qualitativa dicotomica | Sesso neonato (M/F) |
NB: Le variabili qualitative saranno trattate come fattori nelle analisi inferenziali e nei modelli di regressione.
Nella prima fase, esploreremo le variabili attraverso un’analisi descrittiva per comprenderne la distribuzione e identificare eventuali outlier o anomalie.
Nella sezione precedente abbiamo elencato le variabili e le tipologia di variabile in esame. Costruiamo una tabella che consenta di evidenziare la distinzione tra le tipologie di variabili. La tabella distingue chiaramente guiderà la scelta dei test statistici e dei modelli nella fasi successive.
variabili_type_df <- data.frame(
Variabile_Quantitativa = c(
"Anni.madre (continua)",
"N.gravidanze (discreta)",
"Gestazione (discreta, settimane)",
"Peso (continua, grammi)",
"Lunghezza (continua, mm)",
"Cranio (continua, mm)"
),
Variabile_Qualitativa = c(
"Fumatrici (dicotomica: 0/1)",
"Tipo.parto (nominale: Nat/Ces)",
"Ospedale (nominale: osp1/osp2/osp3)",
"Sesso (dicotomica: M/F)",
"",
""
)
)
kable(variabili_type_df, caption = "Classification of Variables")
| Variabile_Quantitativa | Variabile_Qualitativa |
|---|---|
| Anni.madre (continua) | Fumatrici (dicotomica: 0/1) |
| N.gravidanze (discreta) | Tipo.parto (nominale: Nat/Ces) |
| Gestazione (discreta, settimane) | Ospedale (nominale: osp1/osp2/osp3) |
| Peso (continua, grammi) | Sesso (dicotomica: M/F) |
| Lunghezza (continua, mm) | |
| Cranio (continua, mm) |
A questo punto, come richiesto nell’enunciato della sezione, esploriamo le variabili attraverso un’analisi descrittiva per comprenderne la distribuzione.
Statistiche Descrittive di Base
Per le variabili quantitative (Anni.madre, N.gravidanze, Gestazione, Peso, Lunghezza, Cranio) calcolo media, mediana, deviazione standard, min/max e quartili.
kable(summary(data[, c("Anni.madre","N.gravidanze","Gestazione","Peso","Lunghezza","Cranio")]))
| Anni.madre | N.gravidanze | Gestazione | Peso | Lunghezza | Cranio | |
|---|---|---|---|---|---|---|
| Min. : 0.00 | Min. : 0.0000 | Min. :25.00 | Min. : 830 | Min. :310.0 | Min. :235 | |
| 1st Qu.:25.00 | 1st Qu.: 0.0000 | 1st Qu.:38.00 | 1st Qu.:2990 | 1st Qu.:480.0 | 1st Qu.:330 | |
| Median :28.00 | Median : 1.0000 | Median :39.00 | Median :3300 | Median :500.0 | Median :340 | |
| Mean :28.16 | Mean : 0.9812 | Mean :38.98 | Mean :3284 | Mean :494.7 | Mean :340 | |
| 3rd Qu.:32.00 | 3rd Qu.: 1.0000 | 3rd Qu.:40.00 | 3rd Qu.:3620 | 3rd Qu.:510.0 | 3rd Qu.:350 | |
| Max. :46.00 | Max. :12.0000 | Max. :43.00 | Max. :4930 | Max. :565.0 | Max. :390 |
Le medie e mediane risultano vicine per molte variabili, suggerendo distribuzioni abbastanza simmetriche. Tuttavia, il peso e la lunghezza mostrano range ampi, con possibili valori estremi che meritano attenzione.
Indici di Variabilità e Forma
library(moments)
library(dplyr)
#Define the quantitative variables
var_quant <- c("Anni.madre","N.gravidanze","Gestazione","Peso","Lunghezza","Cranio")
#Function to calculate the requested indices
calc_stas<- function(x){
c(
Varianza = var(x, na.rm = TRUE),
Dev_Std = sd(x, na.rm = TRUE),
Range = diff(range(x, na.rm = TRUE)),
IQR = IQR(x, na.rm = TRUE),
Asimmetria = skewness(x, na.rm = TRUE),
Curtosi = kurtosis(x, na.rm = TRUE)
)
}
#Apply the function to the qualitative variables
stats_df <- sapply(data[var_quant], calc_stas)%>%
round(2) %>%
as.data.frame()
stats_df<- tibble::rownames_to_column(stats_df,var= "Measure")
#Final table
kable(stats_df, caption = "Indices by size (rows) and variable (columns)")
| Measure | Anni.madre | N.gravidanze | Gestazione | Peso | Lunghezza | Cranio |
|---|---|---|---|---|---|---|
| Varianza | 27.81 | 1.64 | 3.49 | 275665.68 | 692.67 | 269.79 |
| Dev_Std | 5.27 | 1.28 | 1.87 | 525.04 | 26.32 | 16.43 |
| Range | 46.00 | 12.00 | 18.00 | 4100.00 | 255.00 | 155.00 |
| IQR | 7.00 | 1.00 | 2.00 | 630.00 | 30.00 | 20.00 |
| Asimmetria | 0.04 | 2.51 | -2.07 | -0.65 | -1.51 | -0.79 |
| Curtosi | 3.38 | 13.99 | 11.26 | 5.03 | 9.49 | 5.95 |
Il peso presenta asimmetria positiva e curtosi > 3, indicando code più pesanti e valori estremi.
L’età materna mostra una distribuzione più concentrata, ma con outlier (madri molto giovani o molto anziane).
Le variabili antropometriche (lunghezza, cranio) hanno distribuzioni più regolari, ma con alcuni valori anomali.
Distribuzione Grafiche & Outlier
Riporto una serie di distribuzioni grafiche per rappresentare la tendenza e la variabilità delle variabili:
Istogrammi per le var quantitative: evidenziano la forma della distribuzione (asimmetria, code, multimodalità).
Boxplot : utili per individuare outlier.
# Histogram for Peso
hist(Peso, main="Distribuzione Peso Neonati", xlab="Peso (g)", col="lightblue", border="black")
avg_peso<- mean(Peso, na.rm = TRUE)
Q1 <- quantile(Peso, 0.25)
Q3 <- quantile(Peso, 0.75)
abline(v = avg_peso, col = "red", lwd = 2,lty=2)
abline(v = Q1, col = "green", lwd = 1,lty=2)
abline(v = Q3, col = "green", lwd = 1,lty=2)
legend("topleft",legend= c(paste("µ = ",round(avg_peso,2),"g"),
paste("Q1 = ", round(Q1,0),"g"),
paste("Q3 = ", round(Q3,0),"g")),
col= c(2,"green", "green"), lwd= 2, lty=c(2,3,3),bty= "n")
# Boxplot for Peso
boxplot(Peso, main="Boxplot Peso Neonati", ylab="Peso (g)", col="lightgreen")
# Outlier detection for Peso
IQR <- IQR(Peso, na.rm = TRUE)
outliers <- Peso[Peso < (Q1 - 1.5*IQR) | Peso > (Q3 + 1.5*IQR)]
cat("la variabile Peso contiene", length(outliers), "valori anomali")
## la variabile Peso contiene 69 valori anomali
outlier_matrix <- matrix(outliers, ncol = 10, byrow = TRUE)
## Warning in matrix(outliers, ncol = 10, byrow = TRUE): data length [69] is not a
## sub-multiple or multiple of the number of rows [7]
kable(as.data.frame(t(outlier_matrix)), caption = "elenco outliers")
| V1 | V2 | V3 | V4 | V5 | V6 | V7 |
|---|---|---|---|---|---|---|
| 1370 | 1550 | 830 | 1840 | 4760 | 4650 | 2000 |
| 1340 | 1410 | 1615 | 1620 | 1800 | 4720 | 4690 |
| 4680 | 1900 | 1960 | 1280 | 1430 | 1780 | 1580 |
| 1500 | 1720 | 1770 | 1280 | 1950 | 1180 | 1170 |
| 1850 | 1980 | 1750 | 4810 | 1970 | 1890 | 4720 |
| 1560 | 1390 | 1170 | 2040 | 900 | 1140 | 1690 |
| 1280 | 1450 | 2040 | 4620 | 1780 | 1600 | 980 |
| 1750 | 1970 | 1980 | 1500 | 4580 | 1300 | 930 |
| 4600 | 1190 | 4600 | 2000 | 4930 | 930 | 1730 |
| 1285 | 2000 | 4900 | 990 | 4700 | 1750 | 1370 |
Commento: La maggior parte dei neonati pesa tra i 2990 e i 3600 g. Sono presenti outlier sotto i 2000 g (basso peso che è dovuto probabilmente a prematurità) e sopra i 4500 g. Alcuni casi estremi (<1000 g) sono clinicamente implausibili e potrebbero essere errori di registrazione.
# Histogram for Anni.Madre
hist(Anni.madre, main="Distribuzione Età Madri", xlab="Età (anni)", col="lightblue", border="black")
avg_eta<- mean(Anni.madre, na.rm = TRUE)
Q1 <- quantile(Anni.madre, 0.25)
Q3 <- quantile(Anni.madre, 0.75)
abline(v = avg_eta, col = "red", lwd = 2,lty=2)
abline(v = Q1, col = "green", lwd = 1,lty=2)
abline(v = Q3, col = "green", lwd = 1,lty=2)
legend("topleft",legend= c(paste("µ = ",round(avg_eta,2),"anni"),
paste("Q1 = ", round(Q1,0),"anni"),
paste("Q3 = ", round(Q3,0),"anni")),
col= c(2,"green", "green"), lwd= 2, lty=c(2,3,3),bty= "n")
# Boxplot for Anni.Madre
boxplot(Anni.madre, main="Boxplot Età Madri", ylab="Età", col="lightgreen")
# Outlier detection for Età Madri
IQR <- IQR(Anni.madre, na.rm = TRUE)
outliers <- Anni.madre[Anni.madre < (Q1 - 1.5*IQR) | Anni.madre > (Q3 + 1.5*IQR)]
cat("la variabile Anni.madre contiene", length(outliers), "valori anomali")
## la variabile Anni.madre contiene 13 valori anomali
outlier_matrix <- matrix(outliers, nrow = 1, byrow = TRUE)
kable(as.data.frame(t(outlier_matrix)), caption = "elenco outliers")
| V1 |
|---|
| 13 |
| 45 |
| 43 |
| 44 |
| 44 |
| 43 |
| 14 |
| 46 |
| 1 |
| 0 |
| 14 |
| 44 |
| 44 |
Commento: Distribuzione centrata sui 28-32 anni. Oultier: madri molto giovani (<18 anni) e madri oltre i 40 anni. Tra i 13 valori anomali, sono presenti 2 osservazioni con età <10anni (0,1), i quali potrebbero essere errori di registrazione (è impossibile che l’età della madre sia 0/1 anno).
# Histogram for Lunghezza
hist(Lunghezza, main="Lunghezza neonato", xlab="Lunghezza (cm)", col="lightblue", border="black")
avg_lung<- mean(Lunghezza, na.rm = TRUE)
Q1 <- quantile(Lunghezza, 0.25)
Q3 <- quantile(Lunghezza, 0.75)
abline(v = avg_lung, col = "red", lwd = 2,lty=2)
abline(v = Q1, col = "green", lwd = 1,lty=2)
abline(v = Q3, col = "green", lwd = 1,lty=2)
legend("topleft",legend= c(paste("µ = ",round(avg_lung,2),"cm"),
paste("Q1 = ", round(Q1,0),"cm"),
paste("Q3 = ", round(Q3,0),"cm")),
col= c(2,"green", "green"), lwd= 2, lty=c(2,3,3),bty= "n")
# Boxplot for Lunghezza
boxplot(Lunghezza, main="Boxplot Lunghezza neonato", ylab="Lunghezza", col="lightgreen")
# Outlier detection for Lunghezza
IQR <- Q3 - Q1
outliers <- Lunghezza[Lunghezza < (Q1 - 1.5*IQR) | Lunghezza > (Q3 + 1.5*IQR)]
cat("la variabile Lunghezza contiene", length(outliers), "valori anomali")
## la variabile Lunghezza contiene 59 valori anomali
outlier_matrix <- matrix(outliers, ncol = 10, byrow = FALSE)
## Warning in matrix(outliers, ncol = 10, byrow = FALSE): data length [59] is not
## a sub-multiple or multiple of the number of rows [6]
kable(as.data.frame(t(outlier_matrix)), caption = "elenco outliers")
| V1 | V2 | V3 | V4 | V5 | V6 |
|---|---|---|---|---|---|
| 390 | 400 | 410 | 405 | 420 | 420 |
| 360 | 430 | 400 | 410 | 560 | 380 |
| 420 | 390 | 405 | 360 | 430 | 310 |
| 390 | 430 | 390 | 420 | 410 | 420 |
| 370 | 430 | 430 | 410 | 385 | 390 |
| 560 | 315 | 420 | 340 | 410 | 430 |
| 430 | 380 | 325 | 420 | 420 | 410 |
| 400 | 430 | 355 | 370 | 410 | 380 |
| 355 | 410 | 425 | 400 | 370 | 430 |
| 565 | 405 | 320 | 345 | 430 | 390 |
Commento: La maggior parte dei neonati è lunga tra i 480 e i 510 cm. Sono presenti outlier sotto i 420 cm e sopra i 560 cm. Tutti i casi elencati (ben 59) non vanno eliminati ma interpretati con cautela.
# Histogram for Cranio
hist(Cranio, main="Diametro Cranio", xlab="Diametro (cm)", col="lightblue", border="black")
avg_cranio<- mean(Cranio, na.rm = TRUE)
abline(v = avg_cranio, col = "red", lwd = 2,lty=2)
Q1 <- quantile(Cranio, 0.25)
Q3 <- quantile(Cranio, 0.75)
abline(v = Q1, col = "green", lwd = 1,lty=2)
abline(v = Q3, col = "green", lwd = 1,lty=2)
legend("topleft",legend= c(paste("µ = ",round(avg_cranio,2),"cm"),
paste("Q1 = ", round(Q1,0),"cm"),
paste("Q3 = ", round(Q3,0),"cm")),
col= c(2,"green", "green"), lwd= 2, lty=c(2,3,3),bty= "n")
# Boxplot for Cranio
boxplot(Cranio, main="Boxplot Diametro Cranio", ylab="Diametro (cm)", col="lightgreen")
# Outlier detection for Lunghezza
IQR <- IQR(Cranio, na.rm = TRUE)
outliers <- Cranio[Cranio < (Q1 - 1.5*IQR) | Cranio > (Q3 + 1.5*IQR)]
cat("la variabile Cranio contiene", length(outliers), "valori anomali")
## la variabile Cranio contiene 48 valori anomali
outlier_matrix <- matrix(outliers, ncol = 10, byrow = FALSE)
## Warning in matrix(outliers, ncol = 10, byrow = FALSE): data length [48] is not
## a sub-multiple or multiple of the number of rows [5]
kable(as.data.frame(t(outlier_matrix)), caption = "elenco outliers")
| V1 | V2 | V3 | V4 | V5 |
|---|---|---|---|---|
| 298 | 382 | 287 | 273 | 285 |
| 280 | 390 | 384 | 295 | 276 |
| 382 | 274 | 289 | 295 | 386 |
| 390 | 277 | 280 | 272 | 254 |
| 297 | 385 | 295 | 275 | 266 |
| 383 | 390 | 292 | 292 | 293 |
| 278 | 253 | 277 | 390 | 381 |
| 270 | 267 | 290 | 276 | 235 |
| 294 | 299 | 298 | 290 | 273 |
| 290 | 265 | 245 | 298 | 382 |
Commento: La maggior parte dei neonati è ha il diametro del cranio tra i 330 e i 350 cm. Sono presenti outlier sotto i 290 cm e sopra i 560 cm. Tutti i casi elencati (ben 48) non vanno eliminati ma interpretati con cautela.
# Histogram for Gestazione
hist(Gestazione, main="Settimane di Gestazione", xlab="Weeks", col="lightblue", border="black")
avg_gest<- mean(Gestazione, na.rm = TRUE)
abline(v = avg_gest, col = "red", lwd = 2,lty=2)
Q1 <- quantile(Gestazione, 0.25)
Q3 <- quantile(Gestazione, 0.75)
abline(v = Q1, col = "green", lwd = 1,lty=2)
abline(v = Q3, col = "green", lwd = 1,lty=2)
legend("topleft",legend= c(paste("µ = ",round(avg_gest,0),"weeks"),
paste("Q1 = ", round(Q1,0),"weeks"),
paste("Q3 = ", round(Q3,0),"weeks")),
col= c(2,"green", "green"), lwd= 2, lty=c(2,3,3),bty= "n")
# Boxplot for Gestazione
boxplot(Gestazione, main="Boxplot Settimane di Gestazione", ylab="Weeks", col="lightgreen")
# Outlier detection for Gestazione
IQR <- IQR(Gestazione, na.rm = TRUE)
outliers <- Gestazione[Gestazione < (Q1 - 1.5*IQR) | Gestazione > (Q3 + 1.5*IQR)]
cat("la variabile Gestazione contiene", length(outliers), "valori anomali")
## la variabile Gestazione contiene 67 valori anomali
outlier_matrix <- matrix(outliers, ncol = 10, byrow = FALSE)
## Warning in matrix(outliers, ncol = 10, byrow = FALSE): data length [67] is not
## a sub-multiple or multiple of the number of rows [7]
kable(as.data.frame(t(outlier_matrix)), caption = "elenco outliers")
| V1 | V2 | V3 | V4 | V5 | V6 | V7 |
|---|---|---|---|---|---|---|
| 34 | 33 | 34 | 30 | 34 | 34 | 31 |
| 34 | 33 | 28 | 32 | 28 | 34 | 34 |
| 32 | 33 | 33 | 31 | 32 | 34 | 33 |
| 33 | 33 | 33 | 29 | 34 | 28 | 32 |
| 31 | 33 | 34 | 34 | 33 | 30 | 33 |
| 34 | 32 | 29 | 34 | 29 | 33 | 31 |
| 31 | 32 | 25 | 32 | 33 | 34 | 34 |
| 33 | 31 | 32 | 27 | 33 | 30 | 28 |
| 30 | 32 | 33 | 34 | 30 | 33 | 31 |
| 27 | 26 | 31 | 33 | 34 | 33 | 34 |
Commento: Distribuzione centrata sui 38-40 settimane. Outlier: tutti i valori anomali sono sotto le 35 settimane. Tra i 67 valori anomali sono presenti casi di nascite premature (< 30 settimane): potrebbero non risultare errori, ma casi clinici reali che spiegano i valori bassi di peso osservati.
# Frequency Function
freq_table <- function(x, N = length(x)) {
tb <- table(x)
data.frame(
cat = names(tb),
ni = as.integer(tb),
fi = round(as.numeric(tb) / N, 2),
check.names = FALSE
)
}
# Frequency Distribution
kable(freq_table(Fumatrici,length(Fumatrici)), caption = "Distribuzione Fumatrici")
| cat | ni | fi |
|---|---|---|
| 0 | 2396 | 0.96 |
| 1 | 104 | 0.04 |
kable(freq_table(Tipo.parto,length(Tipo.parto)), caption = "Distribuzione Tipo Parto")
| cat | ni | fi |
|---|---|---|
| Ces | 728 | 0.29 |
| Nat | 1772 | 0.71 |
kable(freq_table(Ospedale,length(Ospedale)), caption = "Distribuzione Ospedali")
| cat | ni | fi |
|---|---|---|
| osp1 | 816 | 0.33 |
| osp2 | 849 | 0.34 |
| osp3 | 835 | 0.33 |
kable(freq_table(Sesso,length(Sesso)), caption = "Distribuzione Sesso")
| cat | ni | fi |
|---|---|---|
| F | 1256 | 0.5 |
| M | 1244 | 0.5 |
Commento:
Solo il 4% delle madri è fumatrice: campione totalmente sbilanciato.
I parti naturali sono più frequenti (71%) rispetto a quelli cesarei (29%)
La distribuzione è equilibrata per sesso e ospedali.
Di seguito formuliamo le ipotesi in modo operativo e indichiamo i test statistici appropriati:
Domanda statistica: Esiste una associazione tra ospedale e tipologia di parto?
Variabili: Ospedale (osp1, osp2, osp3), Tipo.parto (Nat/Ces)
Dato che le variabili da sottoporre a test statistico sono categoriche uso Test chi-quadro su tabella di contingenza dove l’interpretazione attesa è :
H0 : Ospedale e tipo di parto sono indipendenti (proporzione di cesarei uguale nei 3 ospedali)
H1 : Ospedale e tipo di parto sono associati (almeno un ospedale ha proporzione diversa)
#Tab COntingenza ospedale x tipo di parto
tab_parto_osp<- table( Tipo.parto, Ospedale)
knitr::kable(round(tab_parto_osp, 3), caption = "Tab. di contingenza per ospedale")
| osp1 | osp2 | osp3 | |
|---|---|---|---|
| Ces | 242 | 254 | 232 |
| Nat | 574 | 595 | 603 |
## Proporzioni per ospedale (per riga)
prop_per_osp <- prop.table(tab_parto_osp, margin = 1)
knitr::kable(round(prop_per_osp, 3), caption = "Proporzioni per ospedale")
| osp1 | osp2 | osp3 | |
|---|---|---|---|
| Ces | 0.332 | 0.349 | 0.319 |
| Nat | 0.324 | 0.336 | 0.340 |
#Visualizzazione Baloon Plot
ggpubr::ggballoonplot(data=as.data.frame(tab_parto_osp), fill = "blue")
Commento: Ho generato un Baloon Plot (=grafico i cui punti d’intersezione tra 2 variabili corrisponde a un palloncino, la cui grandezza dipende dalla frequenza assoluta osservata).
Secondo le regole relative all’Indipendenza e Associazione, Il balloon plot mostra differenze visive nelle frequenze (palloncini con diverse dimensioni e senza un pattern preciso), ma la conferma statistica richiede il test del chi‑quadro.
#Calcolo delle Frequenze attese
attese<- tab_parto_osp
for(i in 1:nrow(tab_parto_osp)){
for(j in 1:ncol(tab_parto_osp)){
attese[i,j] <- margin.table(tab_parto_osp,1)[i] * margin.table(tab_parto_osp,2)[j] / margin.table(tab_parto_osp)
}
}
#Frequence osservate
osservate<- tab_parto_osp
#Calcolo manuale del Chi-Quadro
x_quadro_manuale<- sum((osservate-attese)^2/attese)
x_quadro_manuale
## [1] 1.097202
Vado a simulare graficamente la Distribuzione del Chi-Quadro
plot(density(rchisq(1000000,df = 2)), xlim=c(0,30))
abline(v=qchisq(0.95,2),col=2)
points(x_quadro_manuale,0,cex=3,col=4)
Commento: Il calcolo manuale e la simulazione grafica confermano che la statistica osservata rientra nella zona di accettazione e quindi per ora H0 non può venir rifiutata. Per avvalorare le nostre ipotesi usiamo il Test d’indipendenza con funzione integrata con lo scopo di calcolare statistica, p-value, G.d.L.
test_indipendenza<- chisq.test(tab_parto_osp)
test_indipendenza
##
## Pearson's Chi-squared test
##
## data: tab_parto_osp
## X-squared = 1.0972, df = 2, p-value = 0.5778
Conclusione: Dal calcolo si denota un p-value = 0.578 > alpha (liv. di significatività) = 0.05 -> non rifiutiamo H0. Non emergono differenze significative nelle proporzioni di cesarei tra ospedali.
Domanda Statistica: Le medie di Peso e Lunghezza osservate nel campione sono coerenti con i valori medi della popolazione?
Variabili: Peso (g), Lunghezza (mm)
Dato che vado a fare un confronto tra medie di gruppi utilizzo il test T, dove ho come ipotesi:
H0 : La media campionaria è uguale alla media della popolazione (µ campione = µ popolazione)
H1 : La media campionaria è diversa da quella della popolazione ((µ campione ≠ µ popolazione)
Andiamo a verificare l’ipotesi nulla prima per la variabile Peso e incomincio effettuando il test di normalità della variabile peso per verificare la sua non normalità e in fase successiva passiamo al t test per confronto popolazione campione.
shapiro.test(Peso)
##
## Shapiro-Wilk normality test
##
## data: Peso
## W = 0.97066, p-value < 2.2e-16
Commento: p-value = 0 -> i residui non seguono una distribuzione normale. Tuttavia dato che abbiamo campioni molto grandi anche anche piccole deviazioni diventano significative. Per questo motivo, grazie al Teorema del Limite Centrale, il t‑test rimane applicabile.
NB: Come valori di riferimento prendo come media del peso 3,3 kg (3300 g). Fonte : Manuali MSD – Sezione Pediatria, Parametri di accrescimento nei neonati (MSD Manuals, 2025).
# Valori di riferimento della popolazione
mu_peso_pop <- 3300
# Statistiche campionarie
avg_peso <- mean(Peso, na.rm = TRUE)
sd_peso <- sd(Peso, na.rm = TRUE)
n_peso <- sum(!is.na(Peso))
# Statistica t test
t_peso <- t.test(Peso, mu = mu_peso_pop)
t_peso
##
## One Sample t-test
##
## data: 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
#Quantili
cat("I valori di sogllia sono", qt(0.025,2499),", ",qt(0.975,2499))
## I valori di sogllia sono -1.960914 , 1.960914
Commento: Eseguo il t test a un campione della variabile Peso, confrontando la media osservata µ campione con µ popolazione (3250 g). I risultati ottenuti sono:
t value= -1.5 rientra nei valori di soglia = [ -1.96 ; +1.94 ]
p-value = 0.1296 > alpha = 0.05 : l’ipotesi nulla è non statisticamente rilevante, non rifiuto H0.
IC = [ 3263.5 3304 ] : sapendo che H0 = 3300 rientra per poco in IC, non rifiuto H0.
Fatte queste considerazioni non posso rifiutare H0e quindi la variabile Peso ha µ campione = µ popolazione. Volendo possiamo visualizza il grafico della distribuzione del peso e verifichiamo se il punto del t statistico si trova all’interno della zona d’accettazione.
plot(density(rt(100000,2499)),xlim=c(-4,4))
abline(v=qt(0.025,2499), col=2)
abline(v=qt(0.975,2499),col=2)
points(t_peso$statistic,0,cex=3,col=2)
Risultato : Come Volevasi Dimostrare, il punto si trova all’interno della zona d’accettazione-> H0 non si rifiuta.
Ora verifico l’ipotesi nulla prima per la variabile Lunghezza e per prima azione effettuo il test di normalità della variabile peso per verificare la sua non normalità e in fase successiva passp al t test per confronto popolazione campione.
shapiro.test(Lunghezza)
##
## Shapiro-Wilk normality test
##
## data: Lunghezza
## W = 0.90941, p-value < 2.2e-16
Commento: p-value = 0 -> i residui non seguono una distribuzione normale. Anche qui grazie alla numerosità del campione e al teorema del limite centrale il test-t rimane applicabile.
NB: In questo caso prendo come valore di riferimento la media della Lunghezza 50 cm (500 mm). Fonte : Manuali MSD – Sezione Pediatria, Parametri di accrescimento nei neonati (MSD Manuals, 2025).
# Valori di riferimento della popolazione
mu_peso_pop <- 500
# Statistiche campionarie
avg_lung <- mean(Lunghezza, na.rm = TRUE)
sd_lung <- sd(Lunghezza, na.rm = TRUE)
n_lung<- sum(!is.na(Lunghezza))
# Statistica t test
t_Lunghezza<- t.test(Lunghezza, mu = mu_peso_pop)
t_Lunghezza
##
## One Sample t-test
##
## data: 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
#Quantili
cat("I valori di sogllia sono", qt(0.025,2499),", ",qt(0.975,2499))
## I valori di sogllia sono -1.960914 , 1.960914
Commento: Nel caso della variabile Lunghezza i risultati ottenuti sono:
t value= -10.084 non rientra nei valori di soglia = [ -1.96 ; +1.94 ]
p-value = 0 < alpha = 0.05 : l’ipotesi nulla è statisticamente non rilevante, rifiuto H0.
IC = [ 493.66 495.72 ] : sapendo che H0 = 500, esso non rientra per poco in IC, quindi rifiuto H0.
Fatte queste considerazioni posso rifiutare H0 e quindi la variabile Lunghezza ha µ campione ≠ µ popolazione. Anche qui posso visualizzare e accertarmi tramite grafico se il punto si trova all’interno o meno della zona d’accettazione.
plot(density(rt(100000,2499)),xlim=c(-11,11))
abline(v=qt(0.025,2499), col=2)
abline(v=qt(0.975,2499),col=2)
points(t_Lunghezza$statistic,0,cex=3,col=2)
Risultato : Come Volevasi Dimostrare, il punto si totalmente al di fuori della zona d’accettazione-> H0 si rifiuta (La media campionaria è diversa da quella della popolazione).
3. Le misure antropometriche sono significativamente diverse tra i due sessi
Domanda Statistica: Le misure antropometriche (Peso, Lunghezza, Cranio) differiscono significativamente tra maschi e femmine?
Variabili: Quantitative : Peso (g), Lunghezza (mm), Cranio (mm) , Qualitativa : Sesso (M/F)
Dato che vado a fare un confronto tra multipli utilizzo Wilcoxon test (non parametrico), dove le ipotesi sono:
H0: Le medie delle misure antropometriche sono uguali tra maschi e femmine.
H1: Le medie delle misure antropometriche sono diverse tra maschi e femmine.
Per la variabile Lunghezza sappiamo già che non ha una distribuzione normale. Proprio per questa assunzione viene usato il test di Wilcoxon .
summary(Lunghezza[Sesso=='M'], caption = "Lenght per Men")
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 320.0 490.0 500.0 499.7 515.0 560.0
summary(Lunghezza[Sesso=='F'], caption = "Lenght per Women")
## 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]')
Commento: Dal boxplot si osserva che i neonati maschi tendono ad avere una lunghezza leggermente superiore rispetto alle femmine. La varianza è minore nei maschi, con una distribuzione più concentrata.
Appunto facciamo il wilcox test,
wilcox.test(Lunghezza ~ Sesso,
data = data,
exact = FALSE)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Lunghezza by Sesso
## W = 594455, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
Commento: Nel caso della variabile Lunghezza i risultati ottenuti sono:
Anche per la variabile Peso possiamo utilizzare il test di wilcoxon perché abbiamo dimostrato in precedenza la mancanza di una distribuzione normale.
summary(Peso[Sesso=='M'], caption = "Weight per Men")
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 980 3150 3430 3408 3720 4810
summary(Peso[Sesso=='F'], caption = "Weight per Women")
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 830 2900 3160 3161 3470 4930
boxplot(Peso~Sesso, ylab = 'Peso [g]')
Commento: Il peso medio dei neonati maschi è superiore a quello delle femmine (≈3500 g vs ≈3200 g). Le distribuzioni sono simili in varianza.
wilcox.test(Peso ~ Sesso,
data = data,
exact = FALSE)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Peso by Sesso
## W = 538641, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
Commento: Nel caso della variabile Peso, i risultati ottenuti sono:
Invece per la variabile Cranio non abbiamo idea se ha una distribuzione normale. Quindi applichiamo il test di shapiro.
shapiro.test(Cranio)
##
## Shapiro-Wilk normality test
##
## data: Cranio
## W = 0.96357, p-value < 2.2e-16
Commento: p-value=0 -> Anche la variabile peso non ha distribuzione normale.
summary(Cranio[Sesso=='M'], caption = "Weight per Men")
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 265.0 334.0 343.0 342.4 352.0 390.0
summary(Cranio[Sesso=='F'], caption = "Weight per Women")
## 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]')
Commento: Dal grafico dei boxplot si desume che i maschi presentano una media leggermente superiore (≈342 mm vs ≈337 mm), con varianze simili.
wilcox.test(Cranio ~ Sesso,
data = data,
exact = FALSE)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Cranio by Sesso
## W = 641638, p-value = 9.633e-15
## alternative hypothesis: true location shift is not equal to 0
Commento: Nel caso della variabile Cranio, i risultati ottenuti sono:
Conclusioni: Per tutte e tre le variabili antropometriche (Peso, Lunghezza, Cranio), il test di Wilcoxon ha portato al rifiuto dell’ipotesi nulla. Possiamo quindi affermare che esistono differenze statisticamente significative tra maschi e femmine nelle misure antropometriche alla nascita.
Obiettivo: Costruire un modello di regressione lineare multipla per spiegare la variabilità del Peso neonatale in funzione delle variabili disponibili (antropometriche, materne e contestuali).
Variabile Risposta: Peso (g) -> variabile quantitativa continua che rappresenta l’outcome di principale interesse.
Variabili regressori: Quantitative ( Anni.madre, N.gravidanze, Gestazione, Lunghezza, Cranio ) + Qualitative ( Fumatrici, Tipo.parto, Ospedale, Sesso )
Ipotesi:
H0: tutti i coefficienti = 0 (nessuna influenza sul peso).
H1: almeno un coefficiente è diverso da 0 (almeno una variabile influenza il peso).
Prima di costruire il modello di regressione lineare multipla, con il peso come variabile risposta, andiamo a studiare la linearità del modello. Per fare ciò mostro una matrice di scatterplot per correlazioni.
Ciò mi permette di verificare:
Multicollinearità: se le variabili indipendenti sono molto correlate.
Scelta Variabili: posso decidere quali mantenere o scartare.
Diagnosi Preliminare: Individuo gli outlier o pattern strani che potrebbero influenzare il modello.
NB: Nel grafico analizzo solo le variabili quantitative dell’analisi delle correlazioni.
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(data[sapply(data, is.numeric)], lower.panel=panel.cor, upper.panel=panel.smooth)
Commento: Nella matrtice di scatterplot si può osservare un’alta correlazione con la variabile Peso per le variabili Lunghezza, Cranio, Gestazione
Per verificare le variabili qualitative, utilizzo dei boxplot.
par(mfrow=c(1,2))
boxplot(Peso, ylab='Peso [gr]', xlab='Campione')
boxplot(Peso~Sesso, ylab = 'Peso [gr]')
Commento: Si denota un peso medio maggiore per i maschi rispetto al peso medio femminile ma mantengono la stessa variabilità. Inoltre risultano un numero maggiori di outliers verso il basso rispetto all’alto.
t.test(Peso~Sesso)
##
## 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
Risultati:
t = -12,1 : t risulta più lontano da 0 e quindi rifiuto H0.
p-value = 0 : rifiuto H0 -> rifiuto H0
IC = [ -287,1 ; -207,06 ] -> la Var stimata è [-287,1 ; -207,06] più pesante nei maschi.
par(mfrow=c(1,2))
boxplot(Peso, ylab='Fumatori', xlab='Campione')
boxplot(Peso~Fumatrici, ylab = 'Fumatori')
Commento: Si denota un leggero numero medio di non fumatori superiore rispetto ai fumatori con a sua volta una variabilità superiore dei non fumatori. Inoltre risultano un numero maggiori di outliers da parte dei non fumatori.
t.test(Peso~Fumatrici)
##
## Welch Two Sample t-test
##
## data: Peso by Fumatrici
## t = 1.034, df = 114.1, p-value = 0.3033
## alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
## 95 percent confidence interval:
## -45.61354 145.22674
## sample estimates:
## mean in group 0 mean in group 1
## 3286.153 3236.346
Risultati:
t = 1,03 : t risulta più vicino da 0 e quindi non rifiuto H0.
p-value = 0,3 > alpha = 0.05 : non rifiuto H0
IC = [ -45,61 ; 145,23 ] -> la Var stimata è [-287,1 ; -207,06] più pesante nei maschi.
par(mfrow=c(1,2))
boxplot(Peso, ylab='Tipo Ospedale', xlab='Campione')
boxplot(Peso~Ospedale, ylab = 'Tipo Ospedale')
Commento: Si denota tutte gli ospedali presentano +o - lo stesso numero medio di nascite (3200). Inoltre risultano per tutti gli ospedali un numero elevato di outliers verso il basso.
Costruiamo il modello di regressione lineare multipla che comprenda tutte le variabili.
mod_full <- lm(Peso ~ Anni.madre + N.gravidanze + Gestazione +
Lunghezza + Cranio + Fumatrici + Tipo.parto +
Ospedale + Sesso, data = data)
summary(mod_full)
##
## Call:
## lm(formula = Peso ~ Anni.madre + N.gravidanze + Gestazione +
## Lunghezza + Cranio + Fumatrici + Tipo.parto + Ospedale +
## Sesso, data = data)
##
## 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 *
## 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 ***
## Fumatrici -30.1631 27.5386 -1.095 0.2735
## 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
Commento:
RMSE ≈ 274 g → errore medio di previsione del peso neonatale
R^2 = 0.73 : Modello spiega il 73% della variabilità.
p-value = 0 : il modello è significativo.
Dal summary si nota che le variabili più significative sono Gestazione, Lunghezza e Cranio, mentre i regressori non significativi sono Anni.madre, Fumatrici, Ospedale e in seconda fascia N.gravidanze e Tipo.parto. A questo punto eliminiamo tutte le variabili non significative in prima istanza per verificare se il nuovo modello riesce a spiegare meglio la variabilità.
mod2<- lm(Peso ~ N.gravidanze + Gestazione +
Lunghezza + Cranio + Tipo.parto + Sesso , data = data)
summary(mod2)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Tipo.parto + Sesso, data = data)
##
## 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
Commento:
RMSE ≈ 274 g → errore medio di previsione del peso neonatale
R^2 = 0.73 : Modello spiega il 73% della variabilità.
p-value = 0 : il modello è significativo.
Nonostante eliminazione delle variabili in prima istanza, la variabilità del modello non è migliorata ma il modello è più semplice rispetto al modello precedente. A pari variabilità si sceglie sempre il modello più semplice. A questo punto valutiamo ancora di migliorare il modello eliminando le variabili non significative di seconda fascia come Tipo.parto e N.gravidanze.
mod3<- lm(Peso ~ Gestazione + Lunghezza + Cranio + Sesso , data = data)
summary(mod3)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso,
## data = data)
##
## 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
Commento:
RMSE ≈ 274 g → errore medio di previsione del peso neonatale
R^2 = 0.73 : Modello spiega il 73% della variabilità.
p-value = 0 : il modello è significativo.
Anche nonostante l’eliminazione delle variabili in seconda fascia, la variabilità del modello non è migliorata ma anche in questo caso il modello è migliore del precedente. Quindi come visto precedentemente scegliamo questo modello perché è più semplice.
Conclusione: ll modello finale (mod3) con Gestazione, Lunghezza, Cranio e Sesso spiega circa il 73% della variabilità del peso neonatale. Tutti i regressori inclusi risultano significativi, confermando che fattori antropometrici e biologici sono determinanti per il peso alla nascita.
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.
Nella sezione precedente abbiamo già applicato la procedura di Stepwise, eliminando progressivamente i regressori non significativi (Anni.madre, Fumatrici, Ospedale, Tipo.parto e N.gravidanze). Ora confrontiamo i modelli annidati ottenuti con metodi formali.
Adesso uso i seguenti metodi per confrontare i modelli annidati:
Anova : Metodo di confronto di modelli annidati, valutando la riduzione della devianza residua (RSS).
BIC: Metodo di confronto modelli, che permette di confrontare la bontà e l’adattamento dei modelli.
anova(mod3,mod2,mod_full)
## Analysis of Variance Table
##
## Model 1: Peso ~ Gestazione + Lunghezza + Cranio + Sesso
## Model 2: Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Tipo.parto +
## Sesso
## Model 3: Peso ~ Anni.madre + N.gravidanze + Gestazione + Lunghezza + Cranio +
## Fumatrici + Tipo.parto + Ospedale + Sesso
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 2495 188688687
## 2 2493 187601677 2 1087011 7.2433 0.0007301 ***
## 3 2489 186762521 4 839155 2.7959 0.0247927 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Commento: Il mod3 ha RSS più basso e quindi spiega meglio la variabilità dei dati rispetto agli altri. Inoltre p-value = 0.025 < alpha = 0.05 -> il p-value è significativo.
Risultato: Il mod3 è il modello più semplice e parsimonioso.
BIC(mod_full,mod2,mod3)
## df BIC
## mod_full 12 35241.84
## mod2 8 35221.75
## mod3 6 35220.54
Commento: Il modello con BIC minore è il mod3, quindi confermiamo la scelta del mod3 come quello che spiega meglio la variabilità del dataset.
Conclusione:
Sia l’ANOVA che il BIC convergono sulla scelta di mod3 come modello ottimale. Questo modello, che include solo Gestazione, Lunghezza, Cranio e Sesso, spiega circa il 73% della variabilità del peso neonatale, mantenendo un numero ridotto di regressori e garantendo interpretabilità clinica e statistica.
Una volta ottenuto il modello finale (mod3), valutiamo la sua capacità predittiva e la bontà dell’adattamento. Abbiamo già visto che:
R^2 = 0.73 : Il modello spiega il 73% della variabilità del peso neonatale.
RMSE = 274 g : l’errore medio nella previsione del Peso (g).
Inoltre l’analisi del modello parte dal controllo dei problemi di multicollinearità:
knitr::kable(car::vif(mod3), col.names = c('indice multicollinearità'))
| indice multicollinearità | |
|---|---|
| Gestazione | 1.653502 |
| Lunghezza | 2.069517 |
| Cranio | 1.606131 |
| Sesso | 1.038814 |
Commento: Tutti le variabili hanno VIF basso (<5) e quindi non sussistono problemi di multicollinearità.
Ora approfondiamo con l’analisi dei residui e la ricerca dei valori influenti.
Obiettivo: Verificare che i residui rispettino le assunzioni del modello lineare:
Media dei residui = 0
Varianza costante (omoschedaticità)
Distribuzione dei residui relativamente normale.
Assenza di Pattern sistematici.
Per prima cosa facciamo un’analisi grafica dei residui.
par(mfrow=c(2,2))
plot(mod3)
Risultati del Grafico:
Residuals vs Fitted : il grafico Residuals vs Fitted mostra un leggero pattern curvilineo, segnale di possibile non linearità o eteroschedasticità.
Q-Q Residuals : il Q-Q plot sembra vicino alla normale, ma il test di Shapiro rifiuta l’ipotesi di normalità.
Scale-Location : i punti seguono una distribuzione uniforme lungo un pattern predefinito. La linea rossa che definisce questo pattern è leggermente curvilineo e ciò dimostra la non eteroschedacità e omoschedasticità del modello.
Residuals-Leverage : La maggior parte dei punti si trova all’interno delle curve di Cook. Due osservazioni (etichettate “1780” e “1551”) hanno un po’ più di leva e residui più alti con “1551” dentro la curva di Cook’s DIstance. Questo è un’osservazione pericolosa cha va oltre la distanza di cook di 0.5 avvicinandosi molto a 1.
knitr::kable(data[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 |
Effettuiamo quindi i singoli test per studiare come si comportano le osservazioni e i residui e valutare la qualità del modello.
shapiro.test(mod3$residuals)
##
## Shapiro-Wilk normality test
##
## data: mod3$residuals
## W = 0.9742, p-value < 2.2e-16
lmtest::bptest(mod3)
##
## studentized Breusch-Pagan test
##
## data: mod3
## BP = 89.148, df = 4, p-value < 2.2e-16
lmtest::dwtest(mod3)
##
## Durbin-Watson test
##
## data: mod3
## DW = 1.9557, p-value = 0.1337
## alternative hypothesis: true autocorrelation is greater than 0
plot(density(residuals(mod3)))
Risultati Test Statistici:
Shapiro Test | W = 0.9742, p-value < 2.2e-16 : Il p-value è basso quindi si rifiuta H0 di normalità. I residui non sono compatibili con una distribuzione normale.
Breusch–Pagan | BP = 89.148, df = 4, p-value < 2.2e-16 : anche qui p-value basso quindi non ci sono evidenze di omoschedaticità. La varianza dei residui non può essere considerata costante.
Durbin-Watson | DW = 1.9557, p-value = 0.1337 : p-value> alpha =0.05 quindi in questo caso il valore è molto vicino a 2, e il p-value alto indica che non c’è autocorrelazione significativa. I residui sono indipendenti, come richiesto dal modello lineare classico.
Conclusione: Il modello mod3 supera solo 1 dei 3 test diagnostici, infatti l’indipendenza è OK mentre non vengono rispettate le assunzioni di Normalità e Varianza Costante. Possiamo utilizzare il modello perché le violazioni sono lievi ma bisogna fare particolare attenzione in fase di previsione.
#Leverage Analysis
par(mfrow=c(1,1))
lev <- hatvalues(mod3)
plot(lev)
p <- sum(lev)
n <- length(lev)
soglia = 2*p/n
abline(h=soglia, col=2)
lev[lev > soglia]
## 15 34 42 61 67 96
## 0.006144457 0.006237758 0.004252678 0.004587185 0.005345322 0.004801899
## 101 106 117 131 151 155
## 0.007185969 0.012812305 0.004746840 0.006964953 0.010847336 0.006670119
## 190 205 206 220 249 295
## 0.005288500 0.005297016 0.009402903 0.007376867 0.004655679 0.004008437
## 304 305 310 312 315 348
## 0.004419003 0.005382162 0.028757757 0.013126063 0.005342858 0.004187962
## 378 383 445 471 486 492
## 0.015366923 0.004305772 0.007094099 0.004289364 0.004740595 0.008175223
## 565 587 592 615 629 638
## 0.004635950 0.008375235 0.006313937 0.004575867 0.004041054 0.006216787
## 656 666 684 697 702 726
## 0.004735898 0.004345904 0.008750225 0.005809934 0.004719868 0.004038656
## 748 750 765 805 821 895
## 0.008236046 0.006712718 0.006049647 0.014303716 0.004039952 0.005203974
## 928 947 956 964 968 991
## 0.022095348 0.007803378 0.007670034 0.004618460 0.004075295 0.004259145
## 1014 1067 1091 1096 1130 1157
## 0.008221844 0.007868942 0.008927908 0.004268008 0.006561457 0.004080725
## 1166 1181 1188 1200 1238 1248
## 0.004020427 0.005586402 0.006404596 0.005445729 0.005374725 0.014573080
## 1273 1283 1293 1294 1356 1357
## 0.007080183 0.004055883 0.005555616 0.004735838 0.005285646 0.006526609
## 1361 1385 1395 1400 1402 1420
## 0.004080786 0.012017154 0.004646054 0.005559147 0.004788164 0.005130876
## 1428 1429 1551 1553 1556 1560
## 0.008191659 0.021037040 0.048521821 0.006810651 0.005868384 0.004588261
## 1593 1606 1610 1619 1628 1634
## 0.004855489 0.004746852 0.007761683 0.014504316 0.004663751 0.004527422
## 1686 1692 1693 1701 1712 1735
## 0.008715871 0.004260736 0.005041490 0.010197488 0.006945920 0.004370535
## 1780 1802 1806 1809 1827 1858
## 0.025528862 0.004005956 0.004090967 0.008388773 0.006008547 0.004328741
## 1868 1893 1911 1920 1977 2037
## 0.004746360 0.004245302 0.004481835 0.004131544 0.005384125 0.004234559
## 2040 2066 2089 2114 2115 2120
## 0.011429685 0.004013637 0.006252340 0.012850646 0.011763363 0.018002540
## 2140 2149 2175 2200 2215 2216
## 0.006090376 0.013033782 0.021167345 0.011030260 0.004833388 0.007548562
## 2220 2224 2225 2257 2307 2318
## 0.004023907 0.005780067 0.005408611 0.005767952 0.013898144 0.004770755
## 2337 2359 2391 2408 2437 2452
## 0.004700477 0.004750437 0.004386621 0.009632048 0.023757012 0.023227398
## 2458 2476 2478
## 0.008002531 0.004070348 0.005745434
cat("ci sono", length(lev[lev>soglia]),"osservazioni lontane dallo spazio dei regressori")
## ci sono 135 osservazioni lontane dallo spazio dei regressori
Commento: Il mod3 ha ben 135 osservazioni strutturalmente importanti ma non pericolosi (punti critici < 0.2, 0.3)
#Oultiers
plot(rstudent(mod3), xlab = "Osservazioni", ylab = "Valore rstudent outlier", main = "Osservazioni Outliers")
abline(h=c(-2,2))
car::outlierTest(mod3)
## rstudent unadjusted p-value Bonferroni p
## 1551 9.986149 4.7193e-23 1.1798e-19
## 155 4.951276 7.8654e-07 1.9663e-03
## 1306 4.781188 1.8440e-06 4.6100e-03
Risultati:
Queste osservazioni (1551, 155, 1306) hanno un peso/residuo che non è compatibile con il resto del campione. Tutti ben oltre la soglia ±3: sono osservazioni molto anomale rispetto al modello.
P-value estremamente basso per ciascun outlier
Bonferroni: anche dopo correzione i valori del p-value risultano estremamente significativi e questo significa che le osservazioni 1551, 155, 1306 sono outlier estremamente significativi.
cook<- cooks.distance(mod3)
plot(cook,ylim= c(0,1))
Commento: 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.
Conclusione:
Esistono osservazioni (1551,155,1306) che hanno alti valori di leva e outlier. In particolare, considerando la distanza di cooks, l’osservazione 1551 risulta particolarmente critica. Servono ulteriori indagini su queste osservazioni. Si può provare a rimuoverle dal set e ricalcolare le metriche.
Il modello rispetta l’indipendenza dei residui, ma mostra deviazioni da normalità e omoschedasticità. Tuttavia, con N=2500 il modello rimane robusto e utilizzabile, pur con cautela in fase predittiva.
Posso utilizzare il modello creato mod3 perché le violazioni sono lievi ma bisogna fare particolare attenzione in fase di previsione.
Sintesi Finale : Dopo aver costruito e confrontato diversi modelli, il modello finale (mod3) con Gestazione, Lunghezza, Cranio e Sesso risulta il più parsimonioso e spiega circa il 73% della variabilità del peso neonatale. L’analisi diagnostica evidenzia alcune violazioni (normalità ed eteroschedasticità dei residui) e la presenza di osservazioni influenti (es. ID 1551), che meritano ulteriori indagini. Nonostante ciò, il modello mantiene una buona capacità predittiva (RMSE ≈ 274 g) ed è interpretabile clinicamente: i fattori antropometrici e biologici sono determinanti per il peso alla nascita.
Obiettivo: Stimare il peso di una neonata considerando una madre alla terza gravidanza che partorirà alla 39esima settimana.
data_prediction<- data.frame(
N.gravidanze = 3,
Gestazione = 39,
Lunghezza = 500, #avg campione
Cranio = 340, #avg campione
Sesso = "F",
Fumatrici = 0
)
cat("Il neonato peserà gr", round(predict(mod3, newdata = data_prediction),2))
## Il neonato peserà gr 3299.19
Commento: Il modello prevede che una neonata con queste caratteristiche abbia un peso medio atteso di circa 3299 g.
Obiettivo: Utilizziamo i grafici e rappresentazioni visive per comunicare i risultati e mostrare i risultati del modello e mostrare le relazioni più significative tra le variabili.
In particolare, analizzo 2 aspetti rilevanti d’interesse clinico:
L’impatto del numero di settimane di gestazione sul peso neonatale,
L’effetto del fumo materno sul peso previsto.
library(ggplot2)
## Warning: il pacchetto 'ggplot2' è stato creato con R versione 4.5.2
ggplot(data= data)+
geom_point(aes(x = Gestazione, y = Peso, col= Sesso)) +
geom_smooth(aes(x = Gestazione, y = Peso, col= Sesso),method="lm", se=FALSE) +
geom_smooth(aes(x = Gestazione,y = Peso),
col = "black", se = FALSE, method = "lm")+
labs(title="Relazione tra settimane di gestazione e peso neonatale",
x="Gestazione [weeks]",
y="Peso [g]")
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
Commento: Il grafico mostra chiaramente una relazione positiva tra le settimane di gestazione e il peso neonatale: all’aumentare della gravidanza, il peso della nascita tende ad aumentare. Inoltre distinguendo per sesso:
La retta di regressione dei maschi si colloca leggermente più in alto rispetto a quella delle femmine.
Questo suggerisce che a parità di settimane di gestazione, i neonati maschi tendono ad avere un peso medio superiore rispetto alle femmine.
La distribuzione delle osservazioni non è uniforme: ci sono poche nascite premature (<35 settimane), quindi in quell’intervallo el stime sono meno robuste.
Risultato: La durata della gestazione è un fattore significativo per il peso della nascita e il sesso del neonato genera una variazione sistematica (i maschi risultano mediamente più pesanti delle femmine).
Smoker<- factor(Fumatrici,levels=c(0,1), labels = c("NO","SI"))
ggplot(data= data)+
geom_point(aes(x= Gestazione ,
y= Peso,
col= Smoker))+
geom_smooth(aes(x= Gestazione,
y= Peso, col=Smoker), se= FALSE, method = "lm")+
labs(title = "Effetto del fumo materno sul peso neonatale",x= "Gestazione [weeks]", y= "Peso [g]")
## `geom_smooth()` using formula = 'y ~ x'
Commento: Il grafico mostra la relazione tra settimane di gestazione e peso neonatale, distinguendo tra madri fumatrici e non fumatrici. In entrambi i gruppi si osserva un incremento del peso con l’aumentare della durata della gravidanza; tuttavia la retta di regressione relativa alle madri fumatrici si colloca sistematicamente al di sotto di quella delle non fumatrici.
Questo risultato evidenzia come il fumo materno sia associato a un peso neonatale mediamente inferiore, indipendente dall’età gestazionale.
Infine rappresentiamo la relazione tra Peso e le altre misure antropometriche (Lunghezza e Cranio), che in definitiva sono le variabili quantitative del modello finale ottenuto.
#car::scatter3d(Peso ~ Lunghezza + Cranio, data=data)
library(scatterplot3d)
## Warning: il pacchetto 'scatterplot3d' è stato creato con R versione 4.5.2
scatterplot3d(Lunghezza, Cranio, Peso, pch=20, color="blue",
xlab="Lunghezza [mm]", ylab="Cranio [mm]", zlab="Peso [gr]", main = "Relazione tra Peso e Misure Antropometriche", cex.symbols = 0.8)
Commento: Anche nel grafico 3D si nota l’osservazione anomale 1551, già vista nell’analisi dei residui.
Conclusione: Le visualizzazioni confermano i risultati del modello: la durata della gestazione e le misure antropometriche sono i principali predittori del peso neonatale, mentre il fumo materno mostra un effetto negativo, seppur meno marcato. Le rappresentazioni grafiche rendono immediata la comprensione di tali relazioni.
Il progetto di previsione del peso neonatale rappresenta un’iniziativa fondamentale per Neonatal Health Solutions, in quanto dimostra come l’uso di dati clinici dettagliati e strumenti di analisi statistica avanzati possa tradursi in conoscenze utili e applicabili.
Dall’analisi è emerso che:
Le variabili antropometriche ( Lunghezza, Cranio ) e la durata della gestazione (Gestazione) sono i predittori più forti e significativi del peso dei neonati.
Il sesso del neonato incide sistematicamente, con i maschi mediamente più pesanti delle femmine.
Variabili come età materna, tipo di parto, ospedale e fumo materno non hanno mostrato un impatto statisticamente rilevante nel dataset, pur mantenendo un interesse clinico che merita un ulteriore approfondimenti.
Il modello finale spiega 73% della variabilità del peso neonatale, garantendo un buon equilibrio tra accuratezza e semplicità. Le analisi diagnostiche hanno evidenziato alcune violazioni delle assunzioni classiche come:
La non normalità e eteroschedasticità dei residui
La presenza di outlier e osservazioni influenti (es. 1551)
Dal punto di vista applicativo, il modello consente di applicare stime predittive utili in ambito clinico e gestionale, supportando i professionisti sanitari nella valutazione del rischio neonatale e nella pianificazione delle risorse ospedaliere.