Libraries
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
library(ggplot2)
library(summarytools)
library(readr)
library(moments)
library(car)
## Warning: il pacchetto 'car' è stato creato con R versione 4.4.3
## Caricamento del pacchetto richiesto: carData
## Warning: il pacchetto 'carData' è stato creato con R versione 4.4.3
##
## Caricamento pacchetto: 'car'
## Il seguente oggetto è mascherato da 'package:dplyr':
##
## recode
library(corrplot)
## Warning: il pacchetto 'corrplot' è stato creato con R versione 4.4.3
## corrplot 0.95 loaded
library(MASS)
## Warning: il pacchetto 'MASS' è stato creato con R versione 4.4.3
##
## Caricamento pacchetto: 'MASS'
## Il seguente oggetto è mascherato da 'package:dplyr':
##
## select
Load Data
Neonati_df <- read_csv("neonati.csv", show_col_types = FALSE)
Neonati_df
## # A tibble: 2,500 × 10
## Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza Cranio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 26 0 0 42 3380 490 325
## 2 21 2 0 39 3150 490 345
## 3 34 3 0 38 3640 500 375
## 4 28 1 0 41 3690 515 365
## 5 20 0 0 38 3700 480 335
## 6 32 0 0 40 3200 495 340
## 7 26 1 0 39 3100 480 345
## 8 25 0 0 40 3580 510 349
## 9 22 1 0 40 3670 500 335
## 10 23 0 0 41 3700 510 362
## # ℹ 2,490 more rows
## # ℹ 3 more variables: Tipo.parto <chr>, Ospedale <chr>, Sesso <chr>
Verifico che non ci siano valori mancanti e duplicati
colSums(is.na(Neonati_df))
## Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza
## 0 0 0 0 0 0
## Cranio Tipo.parto Ospedale Sesso
## 0 0 0 0
sum(duplicated(Neonati_df))
## [1] 0
Non sono presenti né valori mancanti né duplicati all’interno del dataset
# Suddivisione delle variabili in base alla loro tipologia
bin_var <- c("Fumatrici", "Tipo.parto", "Sesso")
cat_var <- c("Ospedale")
num_var <- c("Anni.madre", "N.gravidanze", "Gestazione", "Peso", "Lunghezza", "Cranio")
# Statistica descrittiva delle variabili binarie
for (temp_var in bin_var){
print(temp_var)
ni <- table(Neonati_df[[temp_var]])
fi <- round(100*prop.table(ni), 1)
print(cbind(ni, fi))
barplot(table(Neonati_df[[temp_var]]), col = c("red", "blue"), main = paste("Distribuzione", temp_var))
}
## [1] "Fumatrici"
## ni fi
## 0 2396 95.8
## 1 104 4.2
## [1] "Tipo.parto"
## ni fi
## Ces 728 29.1
## Nat 1772 70.9
## [1] "Sesso"
## ni fi
## F 1256 50.2
## M 1244 49.8
è preponderante la presenza di madri non fumatrici rispetto a quelle fumatrici. Quasi il 30% dei parti presenti all’interno del dataset è cesario. Il sesso dei nascituri è bilanciato, con valori prossimi al 50% per entrambi i valori
# Statistica descrittiva delle variabili categoriali
for (temp_var in cat_var){
print(temp_var)
ni <- table(Neonati_df[[temp_var]])
fi <- round(100*prop.table(ni), 1)
print(cbind(ni, fi))
barplot(table(Neonati_df[[temp_var]]), col = c("red", "blue", "green"), main = paste("Distribuzione", temp_var))
}
## [1] "Ospedale"
## ni fi
## osp1 816 32.6
## osp2 849 34.0
## osp3 835 33.4
I dati resgistrati provengono in modo bilanciato da tutte e tre gli ospedali presi in esame.
# Statistica descrittiva delle variabili quantitative
num_var_stats <- data.frame(
Variable = num_var,
Min = round(sapply(Neonati_df[num_var], min, na.rm = TRUE), 1),
Q1 = round(sapply(Neonati_df[num_var], function(x) quantile(x, probs = 0.25, na.rm = TRUE)), 1),
Q3 = round(sapply(Neonati_df[num_var], function(x) quantile(x, probs = 0.75, na.rm = TRUE)), 1),
Max = round(sapply(Neonati_df[num_var], max, na.rm = TRUE), 1),
Range = round(sapply(Neonati_df[num_var], function(x) max(x, na.rm = TRUE) - min(x, na.rm = TRUE)), 1),
IQR = round(sapply(Neonati_df[num_var], IQR, na.rm = TRUE), 1),
Mean = round(sapply(Neonati_df[num_var], mean, na.rm = TRUE), 2),
Median = round(sapply(Neonati_df[num_var], median, na.rm = TRUE), 2),
Variance = round(sapply(Neonati_df[num_var], var, na.rm = TRUE), 3),
Std_Dev = round(sapply(Neonati_df[num_var], sd, na.rm = TRUE), 3),
RSD = round(sapply(Neonati_df[num_var], function(x) (sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)) * 100), 3),
Skewness = round(sapply(Neonati_df[num_var], skewness, na.rm = TRUE), 3),
Kurtosis = round(sapply(Neonati_df[num_var], kurtosis, na.rm = TRUE), 3)
)
print(num_var_stats)
## Variable Min Q1 Q3 Max Range IQR Mean Median
## Anni.madre Anni.madre 0 25 32 46 46 7 28.16 28
## N.gravidanze N.gravidanze 0 0 1 12 12 1 0.98 1
## Gestazione Gestazione 25 38 40 43 18 2 38.98 39
## Peso Peso 830 2990 3620 4930 4100 630 3284.08 3300
## Lunghezza Lunghezza 310 480 510 565 255 30 494.69 500
## Cranio Cranio 235 330 350 390 155 20 340.03 340
## Variance Std_Dev RSD Skewness Kurtosis
## Anni.madre 27.811 5.274 18.725 0.043 3.380
## N.gravidanze 1.640 1.281 130.512 2.514 13.989
## Gestazione 3.492 1.869 4.794 -2.065 11.258
## Peso 275665.683 525.039 15.987 -0.647 5.032
## Lunghezza 692.671 26.319 5.320 -1.515 9.487
## Cranio 269.791 16.425 4.831 -0.785 5.946
for (temp_var in num_var){
print(temp_var)
# Recupero dei valori di Q1 e IQR dalla tabella `num_var_stats`
Q1 <- num_var_stats[num_var_stats$Variable == temp_var, "Q1"]
Q3 <- num_var_stats[num_var_stats$Variable == temp_var, "Q3"]
IQR <- num_var_stats[num_var_stats$Variable == temp_var, "IQR"]
# Calcolo del numero di outlier
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
N_Outliers <- sum(Neonati_df[[temp_var]] < lower_bound | Neonati_df[[temp_var]] > upper_bound, na.rm = TRUE)
print(paste("Numero di Outlier per", temp_var, ":", N_Outliers))
boxplot(Neonati_df[[temp_var]], col = "orange", main = paste("Distribuzione", temp_var))
}
## [1] "Anni.madre"
## [1] "Numero di Outlier per Anni.madre : 13"
## [1] "N.gravidanze"
## [1] "Numero di Outlier per N.gravidanze : 246"
## [1] "Gestazione"
## [1] "Numero di Outlier per Gestazione : 67"
## [1] "Peso"
## [1] "Numero di Outlier per Peso : 69"
## [1] "Lunghezza"
## [1] "Numero di Outlier per Lunghezza : 59"
## [1] "Cranio"
## [1] "Numero di Outlier per Cranio : 48"
Ad esclusione dell’età della madre, tutte le variabili quantitative presentano distribuzioni non normali e asimmetriche. Tuttavia bisogna far presente che dal grafico della variabile Anni madre ci sono due valori insolitamente bassi, di cui uno di essi è 0. Questo potrebbe essere dato da qualche errore nella trasposizione dei dati. Si può optare o di rimuovere queste osservazioni perdendo però anche le informazioni sulle altre variabili o di sostituiire il valore errato con la media della distribuzione, questo ci permette di mantenere le altre informazioni e non dovrebbe comportare
# Ispezione delle osservazioni anomale
Neonati_df[Neonati_df$Anni.madre<10, ]
## # A tibble: 2 × 10
## Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza Cranio Tipo.parto
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 1 1 0 41 3250 490 350 Nat
## 2 0 0 0 39 3060 490 330 Nat
## # ℹ 2 more variables: Ospedale <chr>, Sesso <chr>
Le due osservazioni anomale non presentano altre stranezze negli altri parametri, er tale motivo si opterà per la sostituizione dei valori anomali con il valore medio.
Neonati_df$Anni.madre[Neonati_df$Anni.madre < 10] <- 28
Adesso si può ricalcolare e graficare nuovamente le variabili quantitiative.
# Statistica descrittiva delle variabili quantitative
num_var_stats <- data.frame(
Variable = num_var,
Min = round(sapply(Neonati_df[num_var], min, na.rm = TRUE), 1),
Q1 = round(sapply(Neonati_df[num_var], function(x) quantile(x, probs = 0.25, na.rm = TRUE)), 1),
Q3 = round(sapply(Neonati_df[num_var], function(x) quantile(x, probs = 0.75, na.rm = TRUE)), 1),
Max = round(sapply(Neonati_df[num_var], max, na.rm = TRUE), 1),
Range = round(sapply(Neonati_df[num_var], function(x) max(x, na.rm = TRUE) - min(x, na.rm = TRUE)), 1),
IQR = round(sapply(Neonati_df[num_var], IQR, na.rm = TRUE), 1),
Mean = round(sapply(Neonati_df[num_var], mean, na.rm = TRUE), 2),
Median = round(sapply(Neonati_df[num_var], median, na.rm = TRUE), 2),
Variance = round(sapply(Neonati_df[num_var], var, na.rm = TRUE), 3),
Std_Dev = round(sapply(Neonati_df[num_var], sd, na.rm = TRUE), 3),
RSD = round(sapply(Neonati_df[num_var], function(x) (sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)) * 100), 3),
Skewness = round(sapply(Neonati_df[num_var], skewness, na.rm = TRUE), 3),
Kurtosis = round(sapply(Neonati_df[num_var], kurtosis, na.rm = TRUE), 3)
)
print(num_var_stats)
## Variable Min Q1 Q3 Max Range IQR Mean Median
## Anni.madre Anni.madre 13 25 32 46 33 7 28.19 28
## N.gravidanze N.gravidanze 0 0 1 12 12 1 0.98 1
## Gestazione Gestazione 25 38 40 43 18 2 38.98 39
## Peso Peso 830 2990 3620 4930 4100 630 3284.08 3300
## Lunghezza Lunghezza 310 480 510 565 255 30 494.69 500
## Cranio Cranio 235 330 350 390 155 20 340.03 340
## Variance Std_Dev RSD Skewness Kurtosis
## Anni.madre 27.197 5.215 18.503 0.151 2.897
## N.gravidanze 1.640 1.281 130.512 2.514 13.989
## Gestazione 3.492 1.869 4.794 -2.065 11.258
## Peso 275665.683 525.039 15.987 -0.647 5.032
## Lunghezza 692.671 26.319 5.320 -1.515 9.487
## Cranio 269.791 16.425 4.831 -0.785 5.946
for (temp_var in num_var){
print(temp_var)
# Recupero dei valori di Q1 e IQR dalla tabella `num_var_stats`
Q1 <- num_var_stats[num_var_stats$Variable == temp_var, "Q1"]
Q3 <- num_var_stats[num_var_stats$Variable == temp_var, "Q3"]
IQR <- num_var_stats[num_var_stats$Variable == temp_var, "IQR"]
# Calcolo del numero di outlier
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
N_Outliers <- sum(Neonati_df[[temp_var]] < lower_bound | Neonati_df[[temp_var]] > upper_bound, na.rm = TRUE)
print(paste("Numero di Outlier per", temp_var, ":", N_Outliers))
boxplot(Neonati_df[[temp_var]], col = "orange", main = paste("Distribuzione", temp_var))
}
## [1] "Anni.madre"
## [1] "Numero di Outlier per Anni.madre : 11"
## [1] "N.gravidanze"
## [1] "Numero di Outlier per N.gravidanze : 246"
## [1] "Gestazione"
## [1] "Numero di Outlier per Gestazione : 67"
## [1] "Peso"
## [1] "Numero di Outlier per Peso : 69"
## [1] "Lunghezza"
## [1] "Numero di Outlier per Lunghezza : 59"
## [1] "Cranio"
## [1] "Numero di Outlier per Cranio : 48"
Valutiamo adesso tramite test di ipotesi se ci sono ospedali in cui si fanno un maggior numero di parti cesari. Dato che il confronto verte su due tipologie di variabili categoriali verrà eseguito un Chi-square test.
preg_hosp_table <- table(Neonati_df$Tipo.parto, Neonati_df$Ospedale)
print(preg_hosp_table)
##
## osp1 osp2 osp3
## Ces 242 254 232
## Nat 574 595 603
print(prop.table(preg_hosp_table))
##
## osp1 osp2 osp3
## Ces 0.0968 0.1016 0.0928
## Nat 0.2296 0.2380 0.2412
chisq.test(preg_hosp_table)
##
## Pearson's Chi-squared test
##
## data: preg_hosp_table
## X-squared = 1.0972, df = 2, p-value = 0.5778
Dato che dal test il p-value è maggiore di 0.05 non siamo in grado di rifiutare l’ipotesi nulla e quindi accettiamo che non ci sono prove evidenti che un ospedale faccia più parti cesari rispetto ad altri.
Si vuole anche valutare se i dati del peso e della lunghezza raccolti dal campione sono significativamente uguali a quelli della popolazione. Per fare ciò si svolgerà un one-sample t-test per ciascuna caratteristica in cui impongo come valore di confronto per la media della popolazione il valore riportato dal sito dell’ospedale Bambino Gesù.
# One-Sample t-Test per il peso
t.test(Neonati_df$Peso, mu=3300)
##
## One Sample t-test
##
## data: Neonati_df$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
# One-Sample t-Test per la lunghezza
t.test(Neonati_df$Lunghezza, mu=500)
##
## One Sample t-test
##
## data: Neonati_df$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
Dai dati ottenuti si evince che nel caso del peso il p-value è maggiore di 0.05 e quindi non possiamo rifiutare l’ipotesi nulla, ciò significa che il nostro campione riporta una media del peso dei neonati simile a quella della popolazione, ciò è anche confermato dal fatto che all’interno dell’intervallo di confidenza ricade il valore medio atteso. Viceversa per quanto riguarda la lunghezza è stato ottenuto un p-value prossimo allo 0, ciò significa che dobbiamo rifiutare l’ipotesi di uguaglianza e accettare che il nostro campione non riporta una media delle lunghezze dei neonati significativamente uguale a quella della popolazione, ciò è avvalorato dal fatto che l’intervallo di confidenza non contiene il valore medio della popolazione.
Infine come ultima valutazione di confronto tra i dati raccolti all’interno del dataset si vuole verificare se ci sono significative differenze tra i sessi per le misure antropometriche(Peso, Lugnhezza, Dimensione Cranio)
Prima di eseguire il test di confronto bisogna valutare normalità di ogni singola distribuzione tramite il test di Shapiro-Wilk, se ciò non fosse rispettata i gruppi verrano confrontati tramite Wilcoxon test. Viceversa, se i gruppi hanno una distribuzione normale si passerà al test di Levene per verificare che l’uguagluanzia tra le varianze. Qualora le varianze tra i gruppi sono uguali per la medesima proprietà allora si potrà eseguire il confronto tramite il t-test Student, altrimenti si eseguirà il t-test di Welch.
shapiro.test(Neonati_df$Peso[Neonati_df$Sesso == "M"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Peso[Neonati_df$Sesso == "M"]
## W = 0.96647, p-value = 2.321e-16
shapiro.test(Neonati_df$Peso[Neonati_df$Sesso == "F"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Peso[Neonati_df$Sesso == "F"]
## W = 0.96285, p-value < 2.2e-16
Le distribuzioni del peso per entrambi i sessi non sono normali quindi si eseguirà un Wilcoxon Test
wilcox.test(Peso ~ Sesso, data = Neonati_df)
##
## 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
Il test per il confronto dei pesi tra i sessi ha un p-value molto più piccolo di 0.05. Per tale ragione dobbiamo rifiutare l’ipotesi nulla di uguaglianza e affermare che c’è una differenza significativa nel peso dei neonati tra maschi e femmine.
median(Neonati_df$Peso[Neonati_df$Sesso == "M"])
## [1] 3430
median(Neonati_df$Peso[Neonati_df$Sesso == "F"])
## [1] 3160
Procediamo in modo analogo con le altre caratteristiche.
shapiro.test(Neonati_df$Lunghezza[Neonati_df$Sesso == "M"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Lunghezza[Neonati_df$Sesso == "M"]
## W = 0.92028, p-value < 2.2e-16
shapiro.test(Neonati_df$Lunghezza[Neonati_df$Sesso == "F"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Lunghezza[Neonati_df$Sesso == "F"]
## W = 0.89953, p-value < 2.2e-16
Anche in questo caso le distribuzioni delle lunghezze per entrambi i sessi non sono normali quindi si eseguirà un Wilcoxon Test
wilcox.test(Lunghezza ~ Sesso, data = Neonati_df)
##
## 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
Il test per il confronto delle lunghezze tra i sessi ha un p-value molto più piccolo di 0.05. Per tale ragione dobbiamo rifiutare l’ipotesi nulla di uguaglianza e affermare che c’è una differenza significativa nelle lunghezze dei neonati tra maschi e femmine.
median(Neonati_df$Lunghezza[Neonati_df$Sesso == "M"])
## [1] 500
median(Neonati_df$Lunghezza[Neonati_df$Sesso == "F"])
## [1] 490
shapiro.test(Neonati_df$Cranio[Neonati_df$Sesso == "M"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Cranio[Neonati_df$Sesso == "M"]
## W = 0.97046, p-value = 3.006e-15
shapiro.test(Neonati_df$Cranio[Neonati_df$Sesso == "F"])
##
## Shapiro-Wilk normality test
##
## data: Neonati_df$Cranio[Neonati_df$Sesso == "F"]
## W = 0.95543, p-value < 2.2e-16
Anche in quest’ultimo caso le distribuzioni delle dimensioni del cranio per entrambi i sessi non sono normali quindi si eseguirà un Wilcoxon Test
wilcox.test(Cranio ~ Sesso, data = Neonati_df)
##
## 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
Il test per il confronto delle dimensioni del cranio tra i sessi ha un p-value molto più piccolo di 0.05. Per tale ragione dobbiamo rifiutare l’ipotesi nulla di uguaglianza e affermare che c’è una differenza significativa nelle dimensioni dei neonati tra maschi e femmine.
median(Neonati_df$Cranio[Neonati_df$Sesso == "M"])
## [1] 343
median(Neonati_df$Cranio[Neonati_df$Sesso == "F"])
## [1] 340
Alla luce di questi test si evince che per tutte e tre le misure antropometriche ci sono delle differenze significative tra maschi e femmine. In generale i maschi tendono ad avere valori superiori alle femmine.
Si vuole sviluppare un modello di regressione lineare multipla per prevedere il peso del neonato in base alle variabili rilevanti.
Innanzitutto bisogna valutare e quantificare l’impatto di ogni variabile sul fattore peso del neonato.
Come primo approcio si può calcolare la matrice di correlazione tra le variabili quantitative. In questa circostanza ci è utile codificare le variabili categoriali binarie in valori numerici 0 e 1, senza però tralascaire il fatto che per il calcolo della correlazioni tra valori continui e binari non è un approccio ottimale. Così facendo l’unica varaibile esclusa sarà l’ospedale presso cui è stata eseguita l’osservazione, che verrà valutata per completezza a parte.
#Conversione dei valori binari in numerici
Neonati_df$Tipo.parto_num <- ifelse(Neonati_df$Tipo.parto == "Ces", 1, 0)
Neonati_df$Sesso_num <- ifelse(Neonati_df$Sesso == "M", 1, 0)
#Matrice di correlazione delle varaibili
num_fact <- Neonati_df[, c("Anni.madre", "N.gravidanze", "Fumatrici",
"Gestazione", "Lunghezza", "Cranio", "Tipo.parto_num",
"Sesso_num", "Peso")]
corr_matr <- cor(num_fact, use = "complete.obs")
#Heatmap della matrice di correlazione
corrplot(corr_matr, method = "color", type = "upper",
addCoef.col = "black", tl.col = "black", tl.srt = 45)
#Scatterplot accoppiati e linea di interpolazione
pairs(num_fact,
panel = panel.smooth,
main = "Matrice di Scatterplot")
Da questo primo test si evince che le varaibili continue Anni della
madre e Numero delle gravidanze sono fattori ininfluenti per il peso del
neonato. Viceversa la lunghezza, la dimensione del cranio e la durata
della gestazione sono altamente correlate con il peso in modo positivo.
Le variabili binarie Fumatrice e Tipo di parto non sembrano avere
nessuna correlazione, ma sarà bene eseguire un’analisi dedicata per
esse. Infine anche il sesso sembrerebbe avere ha un’impatto positivo sul
peso del neonato seppur in maniera decisamente inferiore rispetto alle
altre tre variabili. In questa analisi ci sono anche altre informazioni
da non trascurare, infatti sono evidenti delle correlazioni positive tra
le stesse variabili con cui costruire il modello (Gestazione, Lunghezza
e Cranio). Questo potrebbe tornare utile per ottimizzare il modello
sfruttando le interazioni tra le variabili.
Come ultima variabile è rimasta da valutare l’impatto degll’ospedale, che è una varibiale categoriale. Ciò verrà fatto sia numericamente tramite un test anova sulla sola relazione lineare Ospedale-Peso e sia visivamente tramite una rappresentazione boxplot del peso in relazione ai diversi ospedali in esame.
# Relazione numerica Peso-Ospedale
anova(lm(Peso ~ Ospedale, data = Neonati_df))
## Analysis of Variance Table
##
## Response: Peso
## Df Sum Sq Mean Sq F value Pr(>F)
## Ospedale 2 936237 468118 1.6991 0.1831
## Residuals 2497 687952305 275512
#Confronto visivo Peso-Ospedale
boxplot(Peso ~ Ospedale, data = Neonati_df, main="Peso vs Ospedale")
Tramite il test anova si evince che la variabile Ospedale ha un peso
infimo sulla spiegazione dell’andamento della varaibile peso, ciò si
nota dall’alto valore della somma dei quadrati dei residui rispetto a
quello della variabile ospedale e dal fatto che il p-value del test è
maggiore di 0.05. Anche visivamente sembra evidente il poco impatto che
ha la variabile sul peso. Tutte e tre i boxplot appaiono distribuiti nel
medesimo range e con i medesimi valori medi. In sintesi l’ospedale
presso cui sono stati eseguite le osservazioni non mostra evidenze
statisticamente significanti sulla variazione del peso alla nascita.
Proseguiamo adesso a fare un controllo analogo sulle variabili binarie che hanno dato bassi valori di correlazione nella matrice.
#Confronto visivo
par(mfrow=c(1,3))
boxplot(Peso~Fumatrici, data = Neonati_df)
boxplot(Peso~Tipo.parto_num, data = Neonati_df)
boxplot(Peso~Sesso_num, data = Neonati_df)
# T-Test
t.test(Peso~Fumatrici, data = Neonati_df)
##
## 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
t.test(Peso~Tipo.parto_num, data = Neonati_df)
##
## Welch Two Sample t-test
##
## data: Peso by Tipo.parto_num
## t = 0.12968, df = 1493, p-value = 0.8968
## alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
## 95 percent confidence interval:
## -40.54037 46.27992
## sample estimates:
## mean in group 0 mean in group 1
## 3284.916 3282.047
t.test(Peso~Sesso_num, data = Neonati_df)
##
## Welch Two Sample t-test
##
## data: Peso by Sesso_num
## t = -12.106, df = 2490.7, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
## 95 percent confidence interval:
## -287.1051 -207.0615
## sample estimates:
## mean in group 0 mean in group 1
## 3161.132 3408.215
Sia dai grafici che dai test numerici si conferma ciò che si è notato inizialmente. Soltanto la variabile binaria Sesso è significativa nella variazione del peso.
Partiamo a creare un modello completo sfruttando tutte le variabili numeriche.
# Regressione Lineare Multipla Completa
lin_full_mod <- lm(Peso ~ Anni.madre + N.gravidanze + Fumatrici +
Gestazione + Lunghezza + Cranio +
Tipo.parto_num + Sesso_num, data = Neonati_df)
summary(lin_full_mod)
##
## Call:
## lm(formula = Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione +
## Lunghezza + Cranio + Tipo.parto_num + Sesso_num, data = Neonati_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1140.23 -181.78 -14.89 160.22 2630.40
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6707.2324 141.1162 -47.530 < 2e-16 ***
## Anni.madre 0.8647 1.1475 0.754 0.4512
## N.gravidanze 11.7148 4.6712 2.508 0.0122 *
## Fumatrici -31.5829 27.5739 -1.145 0.2522
## Gestazione 32.8391 3.8219 8.592 < 2e-16 ***
## Lunghezza 10.2723 0.3010 34.129 < 2e-16 ***
## Cranio 10.4844 0.4266 24.575 < 2e-16 ***
## Tipo.parto_num -30.2562 12.0994 -2.501 0.0125 *
## Sesso_num 78.0338 11.1925 6.972 3.99e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 274.3 on 2491 degrees of freedom
## Multiple R-squared: 0.7279, Adjusted R-squared: 0.727
## F-statistic: 832.9 on 8 and 2491 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_full_mod))^2)), "\n")
## RMSE: 273.8314
Da un primo sguardo alle informazioni del modello completo si nota che come le variabili maggiormente significative sono le stese evidenziate in precedenza (Gestazione, Lunghezza, Cranio e Sesso). In questa analisi risultano marginalmente signifiative anche il numero delle gravidanze e il tipo di parto, dato che mostrano un p-value inferiore a 0.05. Infine i valori di R-squared e Adjusted R-squared sono nell’intorno del 73%. Dobbiamo cercare di ottimizzare le varibili affinchè il modello abbia un valore di Adjusted R-squared migliore.
Sfruttiamo il criterio di informazione di Akaike per ricavare le variabili più significative.
lin_aic_mod <- stepAIC(lin_full_mod, direction = "both", k=2, trace = TRUE)
## Start: AIC=28080.56
## Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione + Lunghezza +
## Cranio + Tipo.parto_num + Sesso_num
##
## Df Sum of Sq RSS AIC
## - Anni.madre 1 42734 187501837 28079
## - Fumatrici 1 98728 187557831 28080
## <none> 187459103 28081
## - Tipo.parto_num 1 470578 187929681 28085
## - N.gravidanze 1 473298 187932401 28085
## - Sesso_num 1 3658038 191117141 28127
## - Gestazione 1 5555847 193014950 28152
## - Cranio 1 45450123 232909226 28621
## - Lunghezza 1 87653102 275112206 29038
##
## Step: AIC=28079.13
## Peso ~ N.gravidanze + Fumatrici + Gestazione + Lunghezza + Cranio +
## Tipo.parto_num + Sesso_num
##
## Df Sum of Sq RSS AIC
## - Fumatrici 1 99840 187601677 28079
## <none> 187501837 28079
## + Anni.madre 1 42734 187459103 28081
## - Tipo.parto_num 1 471817 187973654 28083
## - N.gravidanze 1 675718 188177555 28086
## - Sesso_num 1 3665908 191167745 28126
## - Gestazione 1 5514506 193016343 28150
## - Cranio 1 45711714 233213551 28623
## - Lunghezza 1 87642114 275143951 29036
##
## Step: AIC=28078.46
## Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Tipo.parto_num +
## Sesso_num
##
## Df Sum of Sq RSS AIC
## <none> 187601677 28079
## + Fumatrici 1 99840 187501837 28079
## + Anni.madre 1 43845 187557831 28080
## - Tipo.parto_num 1 463870 188065546 28083
## - N.gravidanze 1 651066 188252743 28085
## - Sesso_num 1 3649259 191250936 28125
## - Gestazione 1 5444109 193045786 28148
## - Cranio 1 45758101 233359778 28622
## - Lunghezza 1 88054432 275656108 29039
summary(lin_aic_mod)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Tipo.parto_num + Sesso_num, data = Neonati_df)
##
## 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) -6677.2629 135.5916 -49.245 < 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.parto_num -30.0342 12.0969 -2.483 0.0131 *
## Sesso_num 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
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_aic_mod))^2)), "\n")
## RMSE: 273.9355
Il metodo stepwise AIC, come è noto, tendere a essere meno severo nel rimuovere delle variabili con l’ottica di essere più preciso ed infatti in questo caso non ha escluso tutte le varaibili che durante il controllo con la matrice di correlazione erano poco influenti al peso (N.gravidanze e Tipo.parto). Tuttavia R-Squared non ha sortito grossi miglioramenti rispetto al modello lineare completo.
n <- nrow(Neonati_df)
lin_bic_mod <- stepAIC(lin_full_mod, direction = "both", k=log(n), trace = TRUE)
## Start: AIC=28132.98
## Peso ~ Anni.madre + N.gravidanze + Fumatrici + Gestazione + Lunghezza +
## Cranio + Tipo.parto_num + Sesso_num
##
## Df Sum of Sq RSS AIC
## - Anni.madre 1 42734 187501837 28126
## - Fumatrici 1 98728 187557831 28127
## - Tipo.parto_num 1 470578 187929681 28131
## - N.gravidanze 1 473298 187932401 28132
## <none> 187459103 28133
## - Sesso_num 1 3658038 191117141 28174
## - Gestazione 1 5555847 193014950 28198
## - Cranio 1 45450123 232909226 28668
## - Lunghezza 1 87653102 275112206 29084
##
## Step: AIC=28125.73
## Peso ~ N.gravidanze + Fumatrici + Gestazione + Lunghezza + Cranio +
## Tipo.parto_num + Sesso_num
##
## Df Sum of Sq RSS AIC
## - Fumatrici 1 99840 187601677 28119
## - Tipo.parto_num 1 471817 187973654 28124
## <none> 187501837 28126
## - N.gravidanze 1 675718 188177555 28127
## + Anni.madre 1 42734 187459103 28133
## - Sesso_num 1 3665908 191167745 28166
## - Gestazione 1 5514506 193016343 28190
## - Cranio 1 45711714 233213551 28663
## - Lunghezza 1 87642114 275143951 29077
##
## Step: AIC=28119.23
## Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio + Tipo.parto_num +
## Sesso_num
##
## Df Sum of Sq RSS AIC
## - Tipo.parto_num 1 463870 188065546 28118
## <none> 187601677 28119
## - N.gravidanze 1 651066 188252743 28120
## + Fumatrici 1 99840 187501837 28126
## + Anni.madre 1 43845 187557831 28127
## - Sesso_num 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_num
##
## Df Sum of Sq RSS AIC
## <none> 188065546 28118
## - N.gravidanze 1 623141 188688687 28118
## + Tipo.parto_num 1 463870 187601677 28119
## + Fumatrici 1 91892 187973654 28124
## + Anni.madre 1 45044 188020502 28125
## - Sesso_num 1 3655292 191720838 28158
## - Gestazione 1 5464853 193530399 28181
## - Cranio 1 46108583 234174130 28658
## - Lunghezza 1 87632762 275698308 29066
summary(lin_bic_mod)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Sesso_num, data = Neonati_df)
##
## 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 ***
## Sesso_num 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
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_bic_mod))^2)), "\n")
## RMSE: 274.274
In questo caso, rispetto al metodo AIC, il metodo di rimozione BIC è stato leggermente più aggressivo nella rimozione delle varaibili. Ha mantenuto le variabili che avevamo riscontrato con la matrice di correlazione essere le più significative, ma anche in questo caso non ha escluso la varaibile N.gravidanze. Seppur si sia abbassato leggermente il valore di R-Squared rispetto al modello completo e quello post stepwise AIC, la variazione è trascuraible rispetto alla semplificazione del modello così ottenuta.
Partendo dal modello ridotto ottenuto tramite il criterio di Bayes si valuteranno eventuali cambi di perfomance introducendo le interazioni tra le varibili di secondo ordine e possibili effetti quadratici.
lin_ord2_mod <- lm(Peso ~ (N.gravidanze + Gestazione + Lunghezza + Cranio + Sesso_num)^2 +
I(N.gravidanze^2) + I(Gestazione^2) + I(Lunghezza^2) + I(Cranio^2) + I(Sesso_num^2),
data = Neonati_df)
summary(lin_ord2_mod)
##
## Call:
## lm(formula = Peso ~ (N.gravidanze + Gestazione + Lunghezza +
## Cranio + Sesso_num)^2 + I(N.gravidanze^2) + I(Gestazione^2) +
## I(Lunghezza^2) + I(Cranio^2) + I(Sesso_num^2), data = Neonati_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1193.3 -179.2 -10.0 166.2 1329.6
##
## Coefficients: (1 not defined because of singularities)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.866e+02 1.254e+03 0.707 0.47962
## N.gravidanze -1.324e+02 8.494e+01 -1.559 0.11920
## Gestazione 1.506e+02 7.665e+01 1.965 0.04957 *
## Lunghezza -3.215e+00 6.021e+00 -0.534 0.59335
## Cranio -2.896e+01 1.057e+01 -2.740 0.00619 **
## Sesso_num -6.479e+01 2.750e+02 -0.236 0.81378
## I(N.gravidanze^2) -3.603e+00 1.334e+00 -2.700 0.00698 **
## I(Gestazione^2) -5.160e+00 1.633e+00 -3.160 0.00160 **
## I(Lunghezza^2) 5.755e-02 6.715e-03 8.571 < 2e-16 ***
## I(Cranio^2) 4.576e-02 1.912e-02 2.393 0.01679 *
## I(Sesso_num^2) NA NA NA NA
## N.gravidanze:Gestazione -3.716e+00 2.731e+00 -1.361 0.17365
## N.gravidanze:Lunghezza 5.472e-02 2.374e-01 0.230 0.81775
## N.gravidanze:Cranio 8.166e-01 3.566e-01 2.290 0.02210 *
## N.gravidanze:Sesso_num 1.026e+01 8.947e+00 1.147 0.25167
## Gestazione:Lunghezza -3.194e-01 1.992e-01 -1.603 0.10897
## Gestazione:Cranio 1.304e+00 2.261e-01 5.769 8.94e-09 ***
## Gestazione:Sesso_num 1.106e+01 7.688e+00 1.439 0.15036
## Lunghezza:Cranio -8.782e-02 1.699e-02 -5.167 2.57e-07 ***
## Lunghezza:Sesso_num -5.888e-01 6.147e-01 -0.958 0.33826
## Cranio:Sesso_num -3.450e-02 8.632e-01 -0.040 0.96812
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 265.8 on 2480 degrees of freedom
## Multiple R-squared: 0.7457, Adjusted R-squared: 0.7437
## F-statistic: 382.7 on 19 and 2480 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_ord2_mod))^2)), "\n")
## RMSE: 264.7266
Il modello ottenuto è decisamente più complesso rispetto a quello iniziale. I fattori che sembrano avere più significatività sono quelli di ordine superiore, in particolare Lunghezza^2, Gestazione-Cranio e Lunghezza-Cranio. Il sesso del neonato sembra non avere più rilevanza in questo modello, mentre il numero di gravidanze sembra dare un effetto marginale. L’Adjusted R-squared di questo modello è aumentato (74%) come è anche diminuito RMSE rispetto ai modelli precedenti, un miglioramento non così accentuato in relazione alla complessità del modello ottenuto.
Riproviamo ad eseguire le tecniche stepwise AIC e BIC su questo nuovo modello.
lin_aic_ord2_mod <- stepAIC(lin_ord2_mod, direction = "both", k=2, trace = FALSE)
summary(lin_aic_ord2_mod)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Sesso_num + I(N.gravidanze^2) + I(Gestazione^2) + I(Lunghezza^2) +
## I(Cranio^2) + N.gravidanze:Cranio + Gestazione:Lunghezza +
## Gestazione:Cranio + Lunghezza:Cranio, data = Neonati_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1194.65 -180.79 -13.32 166.51 1332.61
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.043e+03 1.237e+03 0.843 0.39910
## N.gravidanze -1.879e+02 7.678e+01 -2.447 0.01448 *
## Gestazione 1.323e+02 7.536e+01 1.755 0.07937 .
## Lunghezza -2.308e+00 5.938e+00 -0.389 0.69754
## Cranio -2.913e+01 1.033e+01 -2.820 0.00484 **
## Sesso_num 7.302e+01 1.090e+01 6.700 2.56e-11 ***
## I(N.gravidanze^2) -3.350e+00 1.269e+00 -2.639 0.00836 **
## I(Gestazione^2) -4.770e+00 1.614e+00 -2.955 0.00316 **
## I(Lunghezza^2) 5.550e-02 6.502e-03 8.537 < 2e-16 ***
## I(Cranio^2) 5.146e-02 1.869e-02 2.754 0.00593 **
## N.gravidanze:Cranio 6.476e-01 2.255e-01 2.872 0.00412 **
## Gestazione:Lunghezza -2.873e-01 1.965e-01 -1.462 0.14378
## Gestazione:Cranio 1.227e+00 2.129e-01 5.762 9.34e-09 ***
## Lunghezza:Cranio -8.887e-02 1.671e-02 -5.319 1.14e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 265.7 on 2486 degrees of freedom
## Multiple R-squared: 0.7452, Adjusted R-squared: 0.7438
## F-statistic: 559.2 on 13 and 2486 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_aic_ord2_mod))^2)), "\n")
## RMSE: 264.9913
n <- nrow(Neonati_df)
lin_bic_ord2_mod <- stepAIC(lin_ord2_mod, direction = "both", k=log(n), trace = FALSE)
summary(lin_bic_ord2_mod)
##
## Call:
## lm(formula = Peso ~ N.gravidanze + Gestazione + Lunghezza + Cranio +
## Sesso_num + I(Gestazione^2) + I(Lunghezza^2) + I(Cranio^2) +
## Gestazione:Cranio + Lunghezza:Cranio, data = Neonati_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1167.37 -182.19 -11.71 168.33 1318.88
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.032e+02 1.190e+03 -0.171 0.864479
## N.gravidanze 1.483e+01 4.216e+00 3.516 0.000445 ***
## Gestazione 1.802e+02 7.431e+01 2.425 0.015395 *
## Lunghezza -7.234e+00 5.657e+00 -1.279 0.201045
## Cranio -2.058e+01 1.006e+01 -2.046 0.040877 *
## Sesso_num 7.275e+01 1.092e+01 6.662 3.30e-11 ***
## I(Gestazione^2) -6.298e+00 1.032e+00 -6.102 1.21e-09 ***
## I(Lunghezza^2) 5.048e-02 5.042e-03 10.012 < 2e-16 ***
## I(Cranio^2) 5.483e-02 1.839e-02 2.981 0.002901 **
## Gestazione:Cranio 1.014e+00 2.056e-01 4.932 8.69e-07 ***
## Lunghezza:Cranio -9.269e-02 1.579e-02 -5.870 4.94e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 266.5 on 2489 degrees of freedom
## Multiple R-squared: 0.7435, Adjusted R-squared: 0.7424
## F-statistic: 721.3 on 10 and 2489 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_bic_ord2_mod))^2)), "\n")
## RMSE: 265.8746
Anche in questo caso il modello ottenuto tramite stepwise BIC è risultato essere più snello rispetto a quello AIC e i valori delle metriche sono migliori rispetto alle controparti prive di elementi di ordine superiore.
Come ultimo step si può valutare di rimuovere manualmente la varaibile Numero di gravidanze dato che in una prima fase di selezione sembrava essere poco o nulla influente sul fattore peso ed è anche l’unica variabile che nell’ulitmo modello ottenuto non sembra avere effetti di ordine superiore.
lin_bic_ord2_red_mod <- update(lin_bic_ord2_mod,~.-N.gravidanze)
summary(lin_bic_ord2_red_mod)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso_num +
## I(Gestazione^2) + I(Lunghezza^2) + I(Cranio^2) + Gestazione:Cranio +
## Lunghezza:Cranio, data = Neonati_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1153.54 -183.48 -11.87 168.55 1306.70
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -225.33592 1192.90777 -0.189 0.85019
## Gestazione 177.71542 74.47582 2.386 0.01710 *
## Lunghezza -6.80501 5.66829 -1.201 0.23004
## Cranio -20.62418 10.08123 -2.046 0.04088 *
## Sesso_num 74.15643 10.93780 6.780 1.50e-11 ***
## I(Gestazione^2) -6.20920 1.03409 -6.004 2.20e-09 ***
## I(Lunghezza^2) 0.04981 0.00505 9.863 < 2e-16 ***
## I(Cranio^2) 0.05571 0.01843 3.022 0.00253 **
## Gestazione:Cranio 0.99719 0.20602 4.840 1.38e-06 ***
## Lunghezza:Cranio -0.09218 0.01583 -5.824 6.48e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 267.1 on 2490 degrees of freedom
## Multiple R-squared: 0.7422, Adjusted R-squared: 0.7413
## F-statistic: 796.5 on 9 and 2490 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df$Peso - predict(lin_bic_ord2_red_mod))^2)), "\n")
## RMSE: 266.5342
La rimozione della variabile numero di gravidanze ha comportato un leggero calo delle perfomance, tuttavia è del tutto accettabile dato che è pur sempre bene scegliere un modello parsimonioso di variabili. Per tale motivo verrà scelto come modello finale lin_bic_ord2_red_mod
#2.3) Valutazione del modello Adesso che è stato scelto il modello e valutato le metriche R^2 e RMSE si vuole porre l’attenzione sull’analisi dei residui in modo da identificare eventuali anomalie.
# Plot dei grafici di analisi del modello
par(mfrow = c(2, 2))
plot(lin_bic_ord2_red_mod)
Dai risultati ottenuti si nota che: - Dal primo grafico i residui
sembrano distribuiti in modo casuale attorno allo zero con una leggera
dispersione accentuanta per valori di output più alti, che potrebbe
indicare eteroscedasticità leggera. - Dal grafico Q-Q i punti centrali
si allineano bene con la retta tratteggiata ciò implica una buona
approssimazione alla normalità, tuttavia le code (soprattutto quella
superiore) mostrano deviazioni dalla normalità riconducibili ad alcuni
outlier. - Dal grafico Scale-Location i punti sono relativamente
dispersi in modo uniforme, ma c’è un leggero aumento della varianza
all’aumentare dei valori predetti sintomo di una potenziale lieve
eteroscedasticità. - Infine dal grafico del Leverage si nota che la
maggior parte dei punti ha basso leverage, tuttavia alcuni punti (1551,
1429, 310) sono più distanti dal gruppo e tendono ad essere vicini se
non addirittua oltre le linee di Cook’s distance.
Questi punti anomali potrebbero essere influenti e hanno bisogno di un’analisi approfondita.
#2.3.1) Analisi dei punti influenti
#Leverage
lev <- hatvalues(lin_bic_ord2_red_mod)
#Soglia del valore di Leverage
p = sum(lev)
threshold_lev = 2*p/n
# Grafico dei punti
plot(lev)
abline(h=threshold_lev, col="red")
Dal grafico dei punti in base al Leverage si nota che la soglia è
prossima allo zero, questo comporta un elevato di punti al di sopra di
essa. Tuttavia si nota come soltanto uno in particolare sia al di sopra
dello 0.5. per tale motivo é bene identificarlo.
# Identificazione del punto estremo
lev[lev>0.5]
## 1551
## 0.6534458
Il punto identificato è il 1551, lo stesso che era risulato evidente durante l’analisi grafica dei residui.
Passiamo adesso ad una valutazione degli outliers.
#Outliers
resid <- rstudent(lin_bic_ord2_red_mod)
#Grafico dei punti
plot(resid)
abline(h=c(-2,2), col=2)
# Test di identificazione degli outliers
outlierTest(lin_bic_ord2_red_mod)
## rstudent unadjusted p-value Bonferroni p
## 1306 4.920817 9.1813e-07 0.0022953
## 155 4.520239 6.4660e-06 0.0161650
## 1694 4.373643 1.2722e-05 0.0318040
## 1399 -4.340729 1.4769e-05 0.0369210
Dal grafico anche in questo risultano essere molti i punti al di fuori o in prossimità delle sogli e di ouliers, ma dal test eseguito è emerso che soltanto 4 punti sono da considerare effettivamente come punti anomali.
Come ultimo test sui punti passiamo al calcolo della distanza di Cook.
# Calcolo della Cook's distance
cooksD <- cooks.distance(lin_bic_ord2_red_mod)
#Grafico dei punti
plot(cooksD)
abline(h=c(0.5,1), col="red")
é evidente dal grafico che tutti i punti ad esclusione di uno siano
lontatni dalle soglie di criticità di Cook. Passiamo adesso ad
indentificare il punto che sta ben oltre la soglia di 1.
# Identificazione del punto estremo
cooksD[cooksD>1]
## 1551
## 2.92047
# Soglia di Cook's Distance
influential_point <- which(cooksD > 1)
# Dataset con sole osservazioni influenti
Neonati_influenti_df <- Neonati_df[influential_point, ]
Neonati_influenti_df
## # A tibble: 1 × 12
## Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza Cranio Tipo.parto
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 35 1 0 38 4370 315 374 Nat
## # ℹ 4 more variables: Ospedale <chr>, Sesso <chr>, Tipo.parto_num <dbl>,
## # Sesso_num <dbl>
Anche in questo caso il punto più influente è lo stesso visto durante l’analisi tramite Leverage, cioè il punto 1551. Quello che si nota da questa osservazione è che il peso è insolitamente alto rispetto alla lunghezza del neonato se confrontato coi valori medi e mediani riportati nella tabella dell’analisi statistica. Qeusto ci spiega perchè il modello non riesce a fittarlo in maniera idonea. Si potrebbe ipotizzare di riomuovere tale osservazione e ricalcolare le metriche del modello.
# Rimozione dei punti influenti
Neonati_df_clean <- Neonati_df[-influential_point, ]
# Riadattamento del modello
lin_mod_clean <- lm(Peso ~ Gestazione + Lunghezza + Cranio +
Sesso_num + I(Gestazione^2) + I(Lunghezza^2) + I(Cranio^2) +
Gestazione:Cranio + Lunghezza:Cranio, data = Neonati_df_clean)
summary(lin_mod_clean)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso_num +
## I(Gestazione^2) + I(Lunghezza^2) + I(Cranio^2) + Gestazione:Cranio +
## Lunghezza:Cranio, data = Neonati_df_clean)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1158.05 -182.66 -12.72 163.24 1303.34
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 97.42565 1192.23828 0.082 0.934879
## Gestazione 165.03521 74.32820 2.220 0.026484 *
## Lunghezza -5.29453 5.66471 -0.935 0.350058
## Cranio -23.20248 10.07304 -2.303 0.021337 *
## Sesso_num 73.76967 10.90636 6.764 1.67e-11 ***
## I(Gestazione^2) -4.80866 1.09043 -4.410 1.08e-05 ***
## I(Lunghezza^2) 0.02655 0.00775 3.426 0.000623 ***
## I(Cranio^2) 0.02961 0.01953 1.516 0.129619
## Gestazione:Cranio 0.71174 0.21778 3.268 0.001097 **
## Lunghezza:Cranio -0.02875 0.02252 -1.276 0.201952
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 266.3 on 2489 degrees of freedom
## Multiple R-squared: 0.7434, Adjusted R-squared: 0.7424
## F-statistic: 801 on 9 and 2489 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df_clean$Peso - predict(lin_mod_clean))^2)), "\n")
## RMSE: 265.7571
La sola rimozione di quel punto ha migliorato le metriche del modello ed inoltre sembra aver ridotto l’influenza di alcuni effetti (Cranio^2 e Lunghezza-Cranio).
A questo punto si può provare ad applicare un ultima correzione a tali effetti rimuovendoli.
# Riadattamento del modello
lin_red_mod_clean <- lm(Peso ~ Gestazione + Lunghezza + Cranio +
Sesso_num + I(Gestazione^2) + I(Lunghezza^2) +
Gestazione:Cranio, data = Neonati_df_clean)
summary(lin_red_mod_clean)
##
## Call:
## lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso_num +
## I(Gestazione^2) + I(Lunghezza^2) + Gestazione:Cranio, data = Neonati_df_clean)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1163.23 -182.39 -13.11 165.10 1317.77
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -3.786e+02 1.086e+03 -0.349 0.7273
## Gestazione 1.586e+02 6.668e+01 2.379 0.0174 *
## Lunghezza -9.287e+00 5.089e+00 -1.825 0.0681 .
## Cranio -1.385e+01 5.949e+00 -2.327 0.0200 *
## Sesso_num 7.356e+01 1.091e+01 6.745 1.89e-11 ***
## I(Gestazione^2) -4.346e+00 1.031e+00 -4.216 2.58e-05 ***
## I(Lunghezza^2) 2.070e-02 5.183e-03 3.993 6.71e-05 ***
## Gestazione:Cranio 6.242e-01 1.538e-01 4.060 5.07e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 266.3 on 2491 degrees of freedom
## Multiple R-squared: 0.7431, Adjusted R-squared: 0.7424
## F-statistic: 1029 on 7 and 2491 DF, p-value: < 2.2e-16
cat("RMSE:", sqrt(mean((Neonati_df_clean$Peso - predict(lin_red_mod_clean))^2)), "\n")
## RMSE: 265.8958
# Plot dei grafici di analisi del modello
par(mfrow = c(2, 2))
plot(lin_red_mod_clean)
A questo punto il modello si può considerare ultimato. Le variabili prese in esame sono: Gestazione, Lunghezza, Cranio e Sesso_num. Inoltre sono significativi anche i seguenti effetti di secondo ordine: Gestazione^2, Lunghezza^2 e Gestazione-Cranio.
#3) Esempi di predizione del modello
Facciamo adesso delle previsioni pratiche fornendo un set di dati nuovi e vediamo cosa predice il modello.
# Creazione di un dataset con più osservazioni
new_data_batch <- data.frame(
Anni.madre = c(13, 25, 28, 32, 46),
N.gravidanze = c(0, 0, 1, 2, 3),
Fumatrici = c(0, 1, 0, 0, 1),
Gestazione = c(25, 38, 39, 40, 43),
Lunghezza = c(310, 480, 500, 510, 550),
Cranio = c(235, 330, 340, 350, 380),
Tipo.parto = c("Nat", "Ces", "Ces", "Nat", "Nat"),
Ospedale = c("osp1", "osp3", "osp2", "osp3", "osp2"),
Sesso = c("F", "M", "F", "M", "M"),
Tipo.parto_num = c(0, 1, 1, 0, 0),
Sesso_num = c(0, 1, 0, 1, 1)
)
# Previsioni
predicted_weights <- predict(lin_bic_ord2_mod, newdata = new_data_batch)
# Visualizzazione dei risultati
print(round(predicted_weights, 2))
## 1 2 3 4 5
## 370.00 2993.01 3291.43 3634.15 4601.28
Dagli esempi è evidente che più tendiamo a valori estremi, specialemnte limiti inferiori, più il modello tende ad avere predizioni improbabili (vedi punto il 1 che riporta un peso di 370 grammi).
#4) Visualizzazione delle relazioni del modello
Per concludere questo progetto è bene dare delle informazioni rapide e visive su come il modello opera. Per fare ciò eseguiremo delle rappresentazioni che mettono in relazioni le varaibili selezionate con la varaibile target peso.
par(mfrow = c(1, 3))
# Peso vs Gestazione
ggplot(data = Neonati_df_clean) +
geom_point(aes(x=Gestazione, y=Peso, col=Sesso)) +
geom_smooth(aes(x=Gestazione, y=Peso, col=Sesso),
method = "lm", formula = y ~ poly(x, 2)) +
labs(title = "Relazione tra Gestazione e Peso",
x = "Settimane di gestazione", y = "Peso (g)")
# Peso vs Lunghezza Neonato
ggplot(data = Neonati_df_clean) +
geom_point(aes(x=Lunghezza, y=Peso, col=Sesso)) +
geom_smooth(aes(x=Lunghezza, y=Peso, col=Sesso),
method = "lm", formula = y ~ poly(x, 2)) +
labs(title = "Relazione tra Lunghezza e Peso",
x = "Lunghezza misurata (mm)", y = "Peso (g)")
# Peso vs Dimensione Cranio
ggplot(data = Neonati_df_clean) +
geom_point(aes(x=Cranio, y=Peso, col=Sesso)) +
geom_smooth(aes(x=Cranio, y=Peso, col=Sesso),
method = "lm", formula = y ~ poly(x, 2)) +
labs(title = "Relazione tra Cranio e Peso",
x = "Dimensione Cranio misurata (mm)", y = "Peso (g)")
Da questi primi tre grafici sono più evidenti le relazioni non lineari delle varaibili e di come esse siano distinte in base al sesso del neonato. Inoltre si nota quello che avevamo ipotizzato durante gli esempi di predizione, cioè che il modello tende ad essere meno accurato in prossimità di valori estremi inferiori. questo è dovuto anche al fatto che la maggior parte delle nostre osservazioni con cui è stato costruito il modello era concentrato su dei valori medi.
Come ulteriore informazione da rappresentare, oltre a quelle già esposte è l’effetto composto tra Gestazione e Dimensione del Cranio come riporato all’interno del modello. Per fare si può sfruttare un grafico a contorno 2D distinto per entrambi i sessi in cui il valore della Lunghezza è fissata al valore medio.
# Griglia di valori
gest_range <- seq(25, 50, length.out = 50)
cran_range <- seq(200, 500, length.out = 50)
grid <- expand.grid(Gestazione = gest_range,
Cranio = cran_range,
Sesso_num = c(0, 1)) # 0 = femmina, 1 = maschio
# Aggiungi le altre variabili fisse
grid$Lunghezza <- median(Neonati_df_clean$Lunghezza)
grid$`I(Gestazione^2)` <- grid$Gestazione^2
grid$`I(Lunghezza^2)` <- grid$Lunghezza^2
grid$`Gestazione:Cranio` <- grid$Gestazione * grid$Cranio
# Calcola le predizioni
grid$Peso <- predict(lin_red_mod_clean, newdata = grid)
# Aggiungi etichetta per sesso
grid$Sesso <- ifelse(grid$Sesso_num == 1, "Maschio", "Femmina")
# Grafico a contorno con faceting
ggplot(grid, aes(x = Gestazione, y = Cranio, z = Peso)) +
geom_contour_filled() +
facet_wrap(~ Sesso) +
labs(title = "Peso previsto in funzione di Gestazione e Cranio per Sesso",
x = "Gestazione (sett.)", y = "Cranio (mm)", fill = "Peso (g)") +
theme_minimal()