È stata aggiunta una variabile al dataset, ovvero l’Indice Ponderale, derivato dal Peso e dalla Lunghezza. Questa variabile è data da Peso/Lunghezza^3 e aggiunge informazioni significative sulle proporzioni del neonato. Per le variabili analizzate è stato usato il Q-Q Plot per verificare quanto le distribuzioni si discostano da una distribuzione normale teorica e assumere che rnorm è il modo corretto per visualizzare la distribuzione delle variabili.
L’obiettivo principale è identificare quali di queste variabili sono più predittive del peso alla nascita, con un focus particolare sull’impatto del fumo materno e delle settimane di gestazione, che potrebbero indicare nascite premature.
#' Crea una tabella HTML formattata per la visualizzazio = e di dati statistici
#'
#' @param data Data frame o oggetto statistico da visualizzare
#' @param show_head_only Se TRUE, mostra solo le prime righe del data frame
#' @param show_data_types Se TRUE, mostra i tipi di dati nelle intestazioni delle colonne
#'
#' @return Una tabella HTML formattata con kableExtra
#' @export
#'
#' @examples
#' # create_table(risultati_test)
#' # create_table(dati, show_head_only = FALSE)
create_table <- function(data, show_head_only = TRUE, show_data_types = TRUE) {
if (inherits(data, "htest")) {
print(data)
entries <- list()
# Aggiungi statistiche (possono essere più di una)
if (!is.null(data$statistic)) {
for (i in seq_along(data$statistic)) {
entries[[length(entries) + 1]] <- list(Statistic = names(data$statistic)[i], Value = data$statistic[[i]])
}
}
entries[[length(entries) + 1]] <- list(Statistic = "p.value", Value = data$p.value)
if (!is.null(data$parameter)) {
for (i in seq_along(data$parameter)) {
entries[[length(entries) + 1]] <- list(Statistic = names(data$parameter)[i], Value = data$parameter[[i]])
}
}
if (!is.null(data$estimate)) {
for (i in seq_along(data$estimate)) {
entries[[length(entries) + 1]] <- list(Statistic = names(data$estimate)[i], Value = data$estimate[[i]])
}
}
if (!is.null(data$conf.int)) {
entries[[length(entries) + 1]] <- list(Statistic = "conf.int_lower", Value = data$conf.int[1])
entries[[length(entries) + 1]] <- list(Statistic = "conf.int_upper", Value = data$conf.int[2])
}
data <- do.call(rbind, lapply(entries, as.data.frame))
} else if (inherits(data, "summary.lm")) {
coeffs <- data$coefficients
coeffs_df <- data.frame(
Term = rownames(coeffs),
Estimate = coeffs[, "Estimate"],
Std_Error = coeffs[, "Std. Error"],
t_value = coeffs[, "t value"],
p_value = coeffs[, "Pr(>|t|)"],
row.names = NULL
)
metrics <- data.frame(
Term = c("Residual Std. Error", "Multiple R-squared", "Adjusted R-squared", "F-statistic", "Model DF", "p-value"),
Estimate = c(data$sigma,
data$r.squared,
data$adj.r.squared,
data$fstatistic[1],
paste(data$fstatistic[2], "and", data$fstatistic[3]),
pf(data$fstatistic[1], data$fstatistic[2], data$fstatistic[3], lower.tail = FALSE)),
Std_Error = NA, t_value = NA, p_value = NA
)
data <- rbind(coeffs_df, metrics)
} else if (inherits(data, "aov")) {
summary_data <- summary(data)[[1]]
data <- data.frame(
Statistic = c("F value", "p-value", "num Df", "denom Df"),
Value = c(
summary_data$`F value`[1],
summary_data$`Pr(>F)`[1],
summary_data$Df[1],
summary_data$Df[2]
),
row.names = NULL
)
} else if (inherits(data, "summaryDefault") || inherits(data, "table")) {
data <- data.frame(
Statistic = names(data),
Value = as.numeric(data),
row.names = NULL
)
} else {
if (is.numeric(data) && !is.null(names(data))) {
data <- data.frame(Variable = names(data), VIF = unname(data), row.names = NULL)
} else if (is.matrix(data) && !is.null(rownames(data))) {
data <- data.frame(Variable = rownames(data), data, row.names = NULL)
} else {
data <- as.data.frame(data)
}
}
column_names <- names(data)
column_types <- sapply(data, class)
if (!show_data_types) {
column_headers <- column_names
} else {
column_headers <- paste0(column_names, " (", column_types, ")")
}
row_length <- if (show_head_only) head(data) else data
knitr::kable(row_length,
digits = 4,
format = "html",
row.names = TRUE,
col.names = column_headers) %>%
kableExtra::kable_styling(bootstrap_options = c("striped", "hover"),
full_width = TRUE,
font_size = 15,
position = "left")
}
#' Crea una tabella di contingenza stilizzata
#'
#' @param var1 Variabile per le righe della tabella
#' @param var2 Variabile per le colonne della tabella
#' @param caption Titolo della tabella (opzionale)
#' @param gruppo_colonne Etichetta per il gruppo di colonne (opzionale)
#' @param colore_totali Colore di sfondo per le celle dei totali (opzionale)
#' @param includi_totali Booleano che indica se includere i totali (opzionale, default TRUE)
#'
#' @return Un oggetto tabella kableExtra
#' @export
#'
#' @examples
#' # Esempio di utilizzo:
#' # crea_tabella_contingenza_stilizzata(Ospedale, Tipo.parto, "Distribuzione dei parti per ospedale")
crea_tabella_contingenza_stilizzata <- function(var1, var2,
caption = NULL,
gruppo_colonne,
colore_totali = "#E8E8E8",
includi_totali = TRUE) {
tabella_contingenza <- table(var1, var2)
if (includi_totali) {
tabella_con_totali <- cbind(tabella_contingenza, Totale = rowSums(tabella_contingenza))
tabella_con_totali <- rbind(tabella_con_totali,
Totale = colSums(tabella_con_totali))
tabella_finale <- tabella_con_totali
} else {
tabella_finale <- tabella_contingenza
}
tabella_stilizzata <- kable(tabella_finale,
caption = caption,
format = "html",
align = "c") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = TRUE,
font_size = 16)
if (includi_totali) {
tabella_stilizzata <- tabella_stilizzata %>%
row_spec(nrow(tabella_finale) - 1, bold = TRUE, background = colore_totali)
tabella_stilizzata <- tabella_stilizzata %>%
column_spec(ncol(tabella_finale), bold = TRUE, background = colore_totali)
}
num_colonne_var2 <- ncol(tabella_contingenza)
header_spec <- c(" " = 1)
header_spec <- c(header_spec, setNames(num_colonne_var2, gruppo_colonne))
if (includi_totali) {
header_spec <- c(header_spec, " " = 1)
}
tabella_stilizzata <- tabella_stilizzata %>%
add_header_above(header_spec)
return(tabella_stilizzata)
}
#' Calcola indici di forma di una distribuzione
#'
#' @param var_name Vettore numerico da analizzare
#' @return Un data frame contenente l'indice di Gini, l'indice di asimmetria e l'indice di curtosi
#' @export
#'
#' @examples
#' # evaluate_shape_indeces(dati$variabile)
evaluate_shape_indeces <- function(var_name){
gini_value <- gini.index(var_name)
skew_value <- skewness(var_name)
kurt_value <- kurtosis(var_name)-3
results <- data.frame(
Statistic = c("Gini_Index", "Skewness", "Kurtosis"),
Value = c(gini_value, skew_value, kurt_value)
)
return(results)
}
#' Calcola l'indice di Gini normalizzato
#'
#' @param x Vettore di dati da analizzare
#' @return L'indice di Gini normalizzato
#' @export
#'
#' @examples
#' # gini.index(dati$variabile)
gini.index <- function(x) {
ni=table(x)
fi=ni/length(x)
fi2=fi^2
J = length(table(x))
gini = 1-sum(fi2)
gini.normalizzato = gini/((J-1)/J)
return(gini.normalizzato)
}
#' Calcola il coefficiente di variazione
#'
#' @param x Vettore numerico per cui calcolare il CV
#' @return Il coefficiente di variazione espresso in percentuale
#' @export
#'
#' @examples
#' # CV(dati$variabile)
CV <- function(x) {
return(sd(x)/mean(x)*100)
}
#' Sposta una variabile in modo che tutti i valori siano positivi
#'
#' @param var Vettore numerico da spostare
#' @return Vettore con valori spostati in modo che il minimo sia 1
#' @export
#'
#' @examples
#' # var_shifted_to_positive(dati$variabile_con_negativi)
var_shifted_to_positive <- function(var) {
return( var + abs(min(var)) + 1 )
}
#' Crea un grafico della variazione di una variabile nel tempo per gruppi diversi
#'
#' @param data Data frame contenente i dati
#' @param x_var Nome della variabile per l'asse x
#' @param y_var Nome della variabile per l'asse y
#' @param group_var Nome della variabile di raggruppamento
#' @param x_label Etichetta per l'asse x
#' @param y_label Etichetta per l'asse y
#' @param title Titolo del grafico
#' @param lower_limit Valore limite inferiore da rappresentare con linea tratteggiata
#' @param upper_limit Valore limite superiore da rappresentare con linea tratteggiata
#' @param lower_text Testo da mostrare vicino al limite inferiore
#' @param upper_text Testo da mostrare vicino al limite superiore
#' @param y_min Valore minimo per l'asse y
#' @param y_max Valore massimo per l'asse y
#'
#' @return Un oggetto ggplot
#' @export
#'
#' @examples
#' # plot_variation_by_group(dati, "settimana", "peso", "gruppo",
#' # lower_limit = 2500, upper_limit = 4000,
#' # lower_text = "Peso minimo", upper_text = "Peso massimo")
plot_variation_by_group <- function(data, x_var, y_var, group_var,
x_label = "Settimane di gestazione",
y_label = "Indice ponderale",
title = "Variazione della variabile nel tempo",
lower_limit = NULL,
upper_limit = NULL,
lower_text = NULL,
upper_text = NULL,
y_min = NULL,
y_max = NULL) {
x_sym <- sym(x_var)
y_sym <- sym(y_var)
group_sym <- sym(group_var)
data[[as_string(group_sym)]] <- as.factor(data[[as_string(group_sym)]])
p <- ggplot(data, aes(x = !!x_sym, y = !!y_sym, color = !!group_sym)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE, linewidth = 1.2) +
labs(x = x_label, y = y_label, title = title, color = "Gruppo") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
x_min <- min(data[[x_var]], na.rm = TRUE)
if (!is.null(lower_limit)) {
p <- p +
geom_hline(yintercept = lower_limit, color = "blue", linetype = "dashed") +
annotate("text", x = x_min, y = lower_limit, label = lower_text,
vjust = -1, hjust = 0, color = "blue")
}
if (!is.null(upper_limit)) {
p <- p +
geom_hline(yintercept = upper_limit, color = "red", linetype = "dashed") +
annotate("text", x = x_min, y = upper_limit, label = upper_text,
vjust = -1, hjust = 0, color = "red")
}
# Per impostare dei limiti di visualizzazione
if (!is.null(y_min) && !is.null(y_max)) {
p <- p + coord_cartesian(ylim = c(y_min, y_max))
}
return(p)
}
#' Esegue un test Z per un campione
#'
#' @param x Vettore di dati del campione
#' @param mu0 Media ipotizzata sotto H0
#' @param stdev Deviazione standard della popolazione
#' @param alfa Livello di significativitÃ
#'
#' @return Una lista contenente: media campionaria, statistica test, valori soglia, p-value, intervallo di confidenza e grafico
#' @export
#'
#' @examples
#' # z_test(dati$variabile, 100, 15, 0.05)
z_test <- function(x,mu0, stdev, alfa){
x = na.omit(x)
mu_cap = mean(x)
n = length(x)
Z = (mu_cap-mu0) / (stdev / sqrt(n))
valori.soglia = qnorm(c(alfa/2, 1-alfa/2))
CI = c(mu_cap+qnorm(alfa/2)*(stdev/sqrt(n)),
mu_cap-qnorm(alfa/2)*(stdev/sqrt(n)))
return(
list(
media.campionaria = mu_cap,
stat.test = Z,
valori.soglia = valori.soglia,
pvalue = 2*pnorm(-abs(Z)),
Int.Conf=CI,
grafico = plot(density(rnorm(1000000,0,1))),
abline(v=valori.soglia,col=2)
)
)
}
#' Crea una tabella di frequenze assolute e relative
#'
#' @param var Vettore di dati da analizzare
#' @return Un data frame contenente frequenze assolute, relative e cumulate
#' @export
#'
#' @examples
#' # tab_frequenze(dati$categoria)
tab_frequenze <- function(var){
n <- length(var)
distr_freq <- as.data.frame(
cbind(
ni=table(var),
fi=table(var)/n,
Ni=cumsum(table(var)),
Fi=cumsum(table(var))/n
)
)
}
#' Pannello per visualizzare la correlazione in una coppia di variabili
#'
#' Funzione pensata da usare all'interno di una matrice di scatterplot (es. pairs()),
#' per scrivere il coefficiente di correlazione tra due variabili in una casella.
#'
#' @param x Primo vettore numerico da correlare
#' @param y Secondo vettore numerico da correlare
#' @param digits Numero di cifre decimali da visualizzare (default 2)
#' @param prefix Prefisso da aggiungere prima del valore di correlazione (default "")
#' @param cex.cor Fattore di scala per la dimensione del testo (calcolato automaticamente se non specificato)
#' @param ... Altri parametri grafici opzionali
#'
#' @return Nessun valore ritornato. Disegna direttamente il coefficiente di correlazione sulla grafica.
#'
#' @examples
#' # pairs(mtcars, lower.panel = panel.cor)
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
{
par(usr = c(0, 1, 0, 1))
r <- abs(cor(x, y))
txt <- format(c(r, 0.123456789), digits = digits)[1]
txt <- paste0(prefix, txt)
if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
text(0.5, 0.5, txt, cex = cex.cor * r)
}
#' Seleziona variabili numeriche con più di 2 valori distinti dal dataset
#'
#' @param data Data frame contenente i dati
#'
#' @return Data frame contenente solo le variabili numeriche con più di 2 valori distinti
#' @export
#'
#' @examples
#' # vars_num <- seleziona_variabili_numeriche(dati)
seleziona_variabili_numeriche <- function(data) {
data[, sapply(data, function(x) is.numeric(x) && length(unique(x)) > 2)]
}
#' Disegna una matrice di correlazione per le variabili numeriche del dataset
#'
#' @param data Data frame contenente i dati
#'
#' @return Un grafico a matrice con correlazioni nella parte inferiore e curve di dispersione nella parte superiore
#' @export
#'
#' @examples
#' # disegna_matrice_correlazione(dati)
disegna_matrice_correlazione <- function(data) {
numeric_vars <- seleziona_variabili_numeriche(data)
pairs(numeric_vars, lower.panel = panel.cor, upper.panel = panel.smooth)
}
#' Confronta le varianze tra gruppi per due variabili rispetto alla variabile 'Peso'
#'
#' @param data Data frame contenente i dati
#' @param var1 Nome della prima variabile da testare
#' @param var2 Nome della seconda variabile da testare
#'
#' @return Output dei test di varianza per le due variabili specificate
#' @export
#'
#' @examples
#' # confronta_varianze(dati, "Sesso", "Tipo.parto")
confronta_varianze <- function(data, var1, var2) {
cat("\n\n\n\n<b>Test confronto varianza: Peso ~ ", var1, "</b>\n")
cat("\nLa varianza tra il peso e il sesso risulta essere uguale. Questo è stato considerato nei test statistici\n")
print(create_table(var.test(as.formula(paste("Peso ~", var1)), data = data),F,F))
cat("<b>Test confronto varianza: Peso ~ ", var2, "</b>\n")
cat("\nLa varianza tra il peso e l'essere fumatrici risulta essere uguale. Questo è stato considerato nei test statistici\n")
print(create_table(var.test(as.formula(paste("Peso ~", var2)), data = data), F, F))
}
#' Crea una sequenza di modelli lineari ridotti rimuovendo variabili una dopo l'altra
#'
#' @param data Data frame contenente i dati
#'
#' @return Lista di modelli lineari, partendo dal modello completo fino al più ridotto
#' @export
#'
#' @examples
#' # modelli <- crea_modelli_ridotti(dati)
crea_modelli_ridotti <- function(data) {
# Creare una copia del dataframe per non modificare l'originale
data_mod <- data
# Codifica Sesso come variabile dummy (0 per M, 1 per F)
data_mod$Sesso <- ifelse(data_mod$Sesso == "F", 1, 0)
# Creare i modelli con il dataset modificato
modelli <- list()
modelli[[1]] <- lm(I(sqrt(Peso)) ~ ., data = data_mod)
modelli[[2]] <- update(modelli[[1]], ~ . - Ospedale - Lunghezza - Cranio + Lunghezza:Cranio)
modelli[[3]] <- update(modelli[[2]], ~ . - Tipo.parto)
modelli[[4]] <- update(modelli[[3]], ~ . - Anni.madre)
names(modelli) <- paste0("mod", 1:4)
return(modelli)
}
#' Rimuove gli outlier in modo iterativo basandosi sulla distanza di Cook. Vengono eseguite due iterazioni perchè nella prima vengono identificati gli outlier rispetto al modello originale, mentre nella seconda, dopo aver rimosso gli outlier, il modello viene ricalcoalto e potrebbe avere nuovi outlier che prima non erano rilevabili
#'
#' @param modello Modello lineare iniziale
#' @param data Data frame contenente i dati
#' @param formula_modello Formula del modello per ricalcolare dopo la rimozione degli outlier
#'
#' @return Data frame pulito senza gli outlier identificati in due iterazioni
#' @export
#'
#' @examples
#' # dati_puliti <- rimuovi_outliers_iterativi(modello, dati, log(Peso) ~ .)
rimuovi_outliers_iterativi <- function(modello, data, formula_modello) {
soglia <- 4 / nrow(data)
cook_distanze <- cooks.distance(modello)
outlier_indices <- which(cook_distanze > soglia)
cat("<b>Rimozione Outlier</b> \n\n")
cat("È stato rimosso circa il 7% delle osservazioni rilevate come outlier statisticamente significativi. Gli outlier più influenti statisticamente sono stati identificati attraverso un valore soglia scelto empiricamente con 4/n e da cui è stata calcolata la distanza di Cook. Vengono eseguite due iterazioni perchè nella prima vengono identificati gli outlier rispetto al modello originale, mentre nella seconda, dopo aver rimosso gli outlier, il modello viene ricalcolato e potrebbe avere nuovi outlier che prima non erano rilevabili. Infatti, è stato scelto di rimuovere ulteriori 4 outlier singificativi per migliorare il modello. \n\n")
total_removed <- 0
if (length(outlier_indices) > 0) {
cat("Rimosse<b>", length(outlier_indices), "</b>osservazioni (prima iterazione)\n\n")
data <- data[-outlier_indices, ]
total_removed <- total_removed + length(outlier_indices)
}
modello_pulito <- lm(formula_modello, data = data)
soglia2 <- 4 / nrow(data)
cook_distanze2 <- cooks.distance(modello_pulito)
outlier_indices2 <- which(cook_distanze2 > soglia2)
outliers_specifici <- c(684, 1188, 1712, 1130)
outliers_specifici_presenti <- outliers_specifici[outliers_specifici %in% as.numeric(rownames(data))]
outlier_indices2 <- unique(c(outlier_indices2, which(rownames(data) %in% outliers_specifici_presenti)))
if (length(outlier_indices2) > 0) {
cat("Rimosse <b>", length(outlier_indices2), "</b> osservazioni (seconda iterazione)\n\n")
data <- data[-outlier_indices2, ]
total_removed <- total_removed + length(outlier_indices2)
}
cat("Totale osservazioni rimosse: <b>", total_removed ,"</b>\n\n" )
return(data)
}
#' Esegue una diagnostica completa di un modello lineare
#'
#' @param modello Modello lineare da diagnosticare
#'
#' @return Grafici diagnostici e risultati di test vari (eteroschedasticità , autocorrelazione, VIF, outlier)
#' @export
#'
#' @examples
#' # diagnostica_modello(modello_finale)
diagnostica_modello <- function(modello) {
par(mfrow=c(1,1))
cat("\n\n<b> VIF</b>\n")
cat("\nI VIF del mod4 sono inferiori a 5 per tutti i regressori indica che non ci sono problemi di multicollinearità , ovvero di correlazione tra i regressori ")
vif_values <- car::vif(modello, type = "predictor")
var_names <- names(coef(modello))[-1]
if (is.matrix(vif_values)) {
vif_vector <- vif_values[, 1]
} else {
vif_vector <- vif_values
}
names(vif_vector) <- var_names
y_max <- 5
vif_vector_no_last <- vif_vector[-length(vif_vector)]
bp <- barplot(vif_vector_no_last,
las = 2,
col = "skyblue",
main = "Variance Inflation Factor (VIF)",
cex.names = 0.6,
ylim = c(0, y_max))
text(x = bp,
y = vif_vector_no_last + max(vif_vector_no_last)*0.05,
labels = round(vif_vector_no_last, 2),
cex = 0.8)
par(mfrow=c(2,2))
cat("\n\n\n<b>Analisi dei residui mod4</b>\n\n")
cat("Dai seguenti plot dei residui è emerso che:\n\n- <b>Residuals vs Fitted</b>: Questo grafico mostra i residui rispetto ai valori predetti. I residui si distribuiscono casualmente attorno alla linea orrizontale zero, senza pattern evidenti. La variabilità risulta essere costante\n- <b>Q-Q Residuals</b>: Questo grafico verifica la normalità dei residui. I residui seguono la linea tratteggiata obliqua, anche se le code sono leggermente più pesanti\n- <b>Scale Location</b>: Questo grafico mostra la radice quadrata dei residui standardizzati rispetto ai valori predetti, ed è utile per verificare l'omoschedasticità . Nel grafico risulta una leggere eteroschedasticità . Tuttavia, con il BP test è stata accettata l'ipotesi di omoschedasticità \n- <b>Residuals vs Leverage</b>: Questo grafico aiuta a identificare punti influenti nel modello poichè i punti che si trovano lontano dal gruppo principale e hanno un alto leverage potrebbero influenzare significativamente i risultati del modello. Si nota come ci siano dei punti outlier che potrebbero essere significativi e da poter valutare e rimuovere se necessario")
plot(modello)
plot(residuals(modello), main="Residui del Modello di Regressione", xlab="Osservazioni", ylab="Residui")
abline(h=mean(residuals(modello)), col=2)
plot(density(residuals(modello)))
cat("\n\n<b>Breusch-Pagan Test</b>\n\n")
cat("Un p-value molto alto indica che si può accettare l'ipotesi nulla di omoschedasticità \n")
print(create_table(lmtest::bptest(modello),F,F))
cat("\n\n<b>Durbin-Watson Test</b>\n")
cat("\n il p-value molto alto indica che si può accettare l'ipotesi nulla di non correlazione tra i regressori.")
print(create_table(lmtest::dwtest(modello),F,F))
cat("\n<b>Outlier Test</b>\n\n")
cat("\nValutazione del residuo standardizzato usato per identificare i possibili outliers significativi in un modello di regressione. In questo caso il Largest r-student e l'unadjusted p-value suggeriscono che ci sia almeno un outlier significativo. Tuttavia, con il criterio Bonferroni(tiene conto del numero di test effettuati) e il relativo calcolo del p-value,che in questo caso si attesta all'incirca su 0.07, si potrebbe escludere l'ipotesi che ci sia almeno un outlier significativo. Questo punto non incide significativamente sulle prestazioni del modello.\n")
outlier_results <- car::outlierTest(modello)
outlier_data <- outlier_results
outlier_df <- data.frame(
"Row" = names(outlier_data$rstudent),
"Rstudent" = outlier_data$rstudent,
"P_value" = outlier_data$p,
"Bonferroni_P_value" = outlier_data$bonf.p,
"Significant" = outlier_data$signif,
"Cutoff" = outlier_data$cutoff
)
print(kable(outlier_df, caption = "Outlier Test Results"))
leverage <- hatvalues(modello)
leverage_threshold <- 2 * mean(leverage)
plot(leverage, main="Leverage Points", xlab="Osservazioni", ylab="Leverage", pch=19, col=ifelse(leverage > leverage_threshold, "red", "blue"))
abline(h = leverage_threshold, col = 2, lwd = 2)
leverage_high <- which(leverage > leverage_threshold)
cat("\n<b>Distanza di Cook, Residui e Leverage</b>\n")
cat("\nI residui si distribuiscono normalmente e risultano avere varianza costante. Non sono presenti punti di leverage significativi. Il punto con massima distanza di cook ha distanza molto inferiore a 0.5")
cook <- cooks.distance(modello)
cook <- cooks.distance(modello)
max_cook_index <- which.max(cook)
plot(cook, main="Cook's Distance", xlab="Osservazioni", ylab="Cook's Distance", pch=19)
abline(h = max(cook), col = 2, lwd = 2, lty = 2)
points(max_cook_index, cook[max_cook_index], col = "red", pch = 19, cex = 1.5)
text(max_cook_index, cook[max_cook_index], labels = "Max Distance", pos = 4, col = "red", cex = 0.8)
cat("\n\n<b>Max Cook's Distance at index", max_cook_index, ": </b>", round(cook[max_cook_index], 4), "\n")
}
#' Confronta modelli tramite BIC e ANOVA
#'
#' @param modelli Lista di modelli lineari da confrontare
#'
#' @return Output del confronto BIC e del test ANOVA tra gli ultimi due modelli
#' @export
#'
#' @examples
#' # confronta_modelli(lista_modelli)
confronta_modelli <- function(modelli) {
bic_values <- sapply(1:length(modelli), function(i) BIC(modelli[[i]]))
names(bic_values) <- paste0("mod", 1:length(modelli))
cat("\n<b>ANOVA confronto tra gli ultimi due modelli</b>\n\n")
cat("\nI valori del test statistico evidenziano come il mod4 abbia una capacità più elevata di spiegare la variabilità nei dati.
Dunque, il parametro che è stato rimosso dal modello 3 al modello 4 non è statisticamente significativo.
Questo rende il modello 4 più semplice e statisticamente rilevante.\n\n")
anova_result <- anova(modelli[[length(modelli)-1]], modelli[[length(modelli)]])
print(create_table(anova_result,F,F))
mse_values <- numeric(length(modelli))
rmse_values <- numeric(length(modelli))
for (i in 1:length(modelli)) {
residui <- residuals(modelli[[i]])
MSE <- sum(residui^2) / (length(Peso) - length(coef(modelli[[i]])))
RMSE <- sqrt(MSE)
mse_values[i] <- MSE
rmse_values[i] <- RMSE
}
names(mse_values) <- paste0("mod", 1:length(modelli))
names(rmse_values) <- paste0("mod", 1:length(modelli))
cat("<b>Confronto tra modelli: BIC, MSE, RMSE</b>\n\n")
cat("L'MSE e l'RMSE non cambia tra tutti i modelli. Questo indica che semplificando il modello la variabilità nei dati non peggiora significativamente. Pertanto, è adatto scegliere l'ultimo modello che risulta essere meno complesso. Inoltre, Il BIC più basso di 7 punti risulta essere significativo poichè indica che la qualità dell'adattamento ai dati è migliore.\n\n")
df_metrics <- tibble(
Modello = rep(names(bic_values), 3),
Valore = c(bic_values, mse_values, rmse_values),
Statistica = factor(rep(c("BIC", "MSE", "RMSE"), each = length(modelli)),
levels = c("BIC", "MSE", "RMSE"))
)
ggplot(df_metrics, aes(x = Modello, y = Valore, fill = Statistica)) +
geom_bar(stat = "identity", position = "dodge") +
facet_wrap(~Statistica, scales = "free_y") +
labs(title = "Confronto tra i modelli", y = "Valore", x = "Modello") +
theme_minimal() +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Set2") +
geom_text(aes(label = round(Valore, 1)), vjust = -0.5, size = 3.5)
}
generate_random_data <- function(original_data, n = 2) {
data.frame(
Gestazione = sample(min(original_data$Gestazione):max(original_data$Gestazione), n, replace = TRUE),
Lunghezza = runif(n, min = min(original_data$Lunghezza), max = max(original_data$Lunghezza)),
Cranio = runif(n, min = min(original_data$Cranio), max = max(original_data$Cranio)),
Sesso = sample(min(original_data$Sesso):max(original_data$Sesso), n, replace = TRUE),
N.gravidanze = sample(min(original_data$N.gravidanze):max(original_data$N.gravidanze), n, replace = TRUE),
Fumatrici = sample(min(original_data$Fumatrici):max(original_data$Fumatrici), n, replace = TRUE),
Indice_Ponderale = runif(n, min = min(original_data$Indice_Ponderale), max = max(original_data$Indice_Ponderale))
)
}
lunghezza_cm_converted <- Lunghezza/10
dati_neonati <- dati_neonati %>%
mutate(Indice_Ponderale = (Peso * 100) / (lunghezza_cm_converted^3))
create_table(dati_neonati, TRUE, TRUE)
Anni.madre (integer) | N.gravidanze (integer) | Fumatrici (integer) | Gestazione (integer) | Peso (integer) | Lunghezza (integer) | Cranio (integer) | Tipo.parto (character) | Ospedale (character) | Sesso (character) | Indice_Ponderale (numeric) | |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 26 | 0 | 0 | 42 | 3380 | 490 | 325 | Nat | osp3 | M | 2.8730 |
2 | 21 | 2 | 0 | 39 | 3150 | 490 | 345 | Nat | osp1 | F | 2.6775 |
3 | 34 | 3 | 0 | 38 | 3640 | 500 | 375 | Nat | osp2 | M | 2.9120 |
4 | 28 | 1 | 0 | 41 | 3690 | 515 | 365 | Nat | osp2 | M | 2.7015 |
5 | 20 | 0 | 0 | 38 | 3700 | 480 | 335 | Nat | osp3 | F | 3.3456 |
6 | 32 | 0 | 0 | 40 | 3200 | 495 | 340 | Nat | osp2 | F | 2.6384 |
La variabile Anni.madre si distribuisce normalmente e la classe modale è quella tra i 25-29 anni ed ha eterogeneità elevata.
anni_madre_classi_cliniche <- cut(
Anni.madre,
breaks = c(-Inf, 14, 19, 24, 29, 34, 39, 44, Inf),
labels = c(
"< 15 anni",
"15–19 anni",
"20–24 anni",
"25–29 anni",
"30–34 anni",
"35–39 anni",
"40–44 anni",
"45+ anni"
),
right = TRUE,
include.lowest = TRUE
)
freq_anni_madre_cl <- tab_frequenze(anni_madre_classi_cliniche)
create_table(freq_anni_madre_cl, FALSE, FALSE)
ni | fi | Ni | Fi | |
---|---|---|---|---|
< 15 anni | 5 | 0.0020 | 5 | 0.0020 |
15–19 anni | 106 | 0.0424 | 111 | 0.0444 |
20–24 anni | 486 | 0.1944 | 597 | 0.2388 |
25–29 anni | 907 | 0.3628 | 1504 | 0.6016 |
30–34 anni | 712 | 0.2848 | 2216 | 0.8864 |
35–39 anni | 236 | 0.0944 | 2452 | 0.9808 |
40–44 anni | 46 | 0.0184 | 2498 | 0.9992 |
45+ anni | 2 | 0.0008 | 2500 | 1.0000 |
create_table(summary(Anni.madre), FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Min. | 0.000 |
2 | 1st Qu. | 25.000 |
3 | Median | 28.000 |
4 | Mean | 28.164 |
5 | 3rd Qu. | 32.000 |
6 | Max. | 46.000 |
# Shape indeces
anni.madre_shape_indeces <- evaluate_shape_indeces(Anni.madre)
create_table(anni.madre_shape_indeces, FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Gini_Index | 0.9727 |
2 | Skewness | 0.0428 |
3 | Kurtosis | 0.3804 |
#graphics
# verifico la presenza di valori nulli nel dataset
sum(is.na(dati_neonati))
## [1] 0
n <- nrow(dati_neonati)
distr_anni_madre <- rnorm(n, mean(Anni.madre), sd(Anni.madre))
hist(distr_anni_madre,freq = F, breaks = 100)
lines(density(distr_anni_madre),col=2, lwd=3)
qqnorm(distr_anni_madre)
qqline(distr_anni_madre)
La maggior parte delle donne non ha mai avuto gravidanze. Tuttavia,il valore mediano risulta essere 1 gravidanza. Quindi, bisogna considerare che tra le donne che hanno avuto gravidanze, quelle più presenti nei 3 ospedali hanno avuta una sola gravidanza pregressa. Dunque, è meno probabile che ci siano in futuro donne con 2+ gravidanze. Questa ipotesi andrà saggiata eventualmente con dei test. È possibile notare:
In conclusione per poter rappresentare i dati come una normale è stato usato il logaritmo poichè con il Q-Q Plot ottenuto dalla distribuzione normale troncata a 0, era evidente che i dati avessero un’andamento logaritmico. Dunque, è stato opportuno trasformare i dati in scala logaritmica. Il nuovo Q-Q Plot derivato dalla trasformazione logaritmica dei dati indica che la distribuzione dei dati è molto lontana dalla normale avendo delle code, rispetto alla linea, molto ampie.
n_gravidanze_cl <- cut(
N.gravidanze,
breaks = c(-Inf, 0, 1, 3, Inf),
labels = c(
"0 gravidanze",
"1 gravidanza",
"2-3 gravidanze",
"4+ gravidanze"
),
right = TRUE,
include.lowest = TRUE
)
freq_n_gravidanze_cl <- tab_frequenze(n_gravidanze_cl)
create_table(freq_n_gravidanze_cl, FALSE, FALSE)
ni | fi | Ni | Fi | |
---|---|---|---|---|
0 gravidanze | 1096 | 0.4384 | 1096 | 0.4384 |
1 gravidanza | 818 | 0.3272 | 1914 | 0.7656 |
2-3 gravidanze | 490 | 0.1960 | 2404 | 0.9616 |
4+ gravidanze | 96 | 0.0384 | 2500 | 1.0000 |
create_table(summary(N.gravidanze), FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Min. | 0.0000 |
2 | 1st Qu. | 0.0000 |
3 | Median | 1.0000 |
4 | Mean | 0.9812 |
5 | 3rd Qu. | 1.0000 |
6 | Max. | 12.0000 |
#shape indeces
n_gravidanze_shape_indeces <- evaluate_shape_indeces(N.gravidanze)
create_table(n_gravidanze_shape_indeces, FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Gini_Index | 0.7347 |
2 | Skewness | 2.5143 |
3 | Kurtosis | 10.9894 |
distr_n_gravidanze <- rnorm(n, mean(N.gravidanze), sd(N.gravidanze))
#Per spostare la distribuzione e ottenre valori positivi. Si somma 1 per evitare il valore 0 per il logaritmo poichè non è definito
distr_n_gravidanze_shifted <- var_shifted_to_positive(distr_n_gravidanze)
distr_n_gravidanze_log <- log(distr_n_gravidanze_shifted)
hist(distr_n_gravidanze_log,freq = F, breaks = 100)
lines(density(distr_n_gravidanze_log),col=2, lwd=3)
qqnorm(distr_n_gravidanze_log)
qqline(distr_n_gravidanze_log)
L’asimmetria negativa indica che sono presenti più modalità alte e la curtosi positiva indica un allungamento della curva. La distribuzione tra i valori ha eterogeneità massima. Il Q-Q Plot indica che la distribuzione per questa variabile non si discosta molto da quella di una normale. La distribuzione presenta una coda più spessa per modalità basse e una coda più stretta per modalità alte. Questo è coerente con la misura dell’assimetria negativa
peso_cl <- cut(
Peso,
breaks = c(-Inf, 900, 1500, 2499, 4499, Inf),
labels = c(
"< 900 grammi [Estremamente basso]",
"901-1500 grammi [Molto basso]",
"1501–2499 grammi [Basso]",
"2500-4499 grammi [Normale]",
"4500+ grammi [Alto]"
),
right = TRUE,
include.lowest = TRUE
)
freq_peso_cl <- tab_frequenze(peso_cl)
create_table(freq_peso_cl, FALSE, FALSE)
ni | fi | Ni | Fi | |
---|---|---|---|---|
< 900 grammi [Estremamente basso] | 2 | 0.0008 | 2 | 0.0008 |
901-1500 grammi [Molto basso] | 22 | 0.0088 | 24 | 0.0096 |
1501–2499 grammi [Basso] | 113 | 0.0452 | 137 | 0.0548 |
2500-4499 grammi [Normale] | 2345 | 0.9380 | 2482 | 0.9928 |
4500+ grammi [Alto] | 18 | 0.0072 | 2500 | 1.0000 |
create_table(summary(Peso), FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Min. | 830.000 |
2 | 1st Qu. | 2990.000 |
3 | Median | 3300.000 |
4 | Mean | 3284.081 |
5 | 3rd Qu. | 3620.000 |
6 | Max. | 4930.000 |
# Shape indeces
peso_shape_indeces <- evaluate_shape_indeces(Peso)
create_table(peso_shape_indeces, FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Gini_Index | 0.9962 |
2 | Skewness | -0.6470 |
3 | Kurtosis | 2.0315 |
distr_peso <- rnorm(n, mean(Peso), sd(Peso))
hist(distr_peso,freq = F, breaks = 100)
lines(density(distr_peso),col=2, lwd=3)
qqnorm(distr_peso)
qqline(distr_peso)
La modalità più frequente è quella del termine precoce (37-39 settimane). Sarà necessario comprendere con opportuni test il perchè di questo dato. La gestazione nel Q-Q Plot ci indica che non si discosta molto da una normale teorica nonostante la kurtosi elevata e l’asimmetria negativa. Tuttavia, nell’istogramma è evidente come a sinistra ci sia una coda più stretta e a destra una coda più spessa. Approfondire con opportuni test statistici
gestazione_cl <- cut(
Gestazione,
breaks = c(-Inf, 28, 32, 37, 39, 40, 42, Inf),
labels = c(
"Estremamente prematuro (<28 settimane)",
"Molto prematuro (28-32 settimane)",
"Prematuro moderato (32-37 settimane)",
"Termine precoce (37-39 settimane)",
"Termine medio (39-40 settimane)",
"Termine tardivo (40-42 settimane)",
"Post-termine (>42 settimane)"
),
right = TRUE,
include.lowest = TRUE
)
freq_gestazione_cl <- tab_frequenze(gestazione_cl)
create_table(freq_gestazione_cl, FALSE, FALSE)
ni | fi | Ni | Fi | |
---|---|---|---|---|
Estremamente prematuro (<28 settimane) | 8 | 0.0032 | 8 | 0.0032 |
Molto prematuro (28-32 settimane) | 25 | 0.0100 | 33 | 0.0132 |
Prematuro moderato (32-37 settimane) | 321 | 0.1284 | 354 | 0.1416 |
Termine precoce (37-39 settimane) | 1018 | 0.4072 | 1372 | 0.5488 |
Termine medio (39-40 settimane) | 741 | 0.2964 | 2113 | 0.8452 |
Termine tardivo (40-42 settimane) | 385 | 0.1540 | 2498 | 0.9992 |
Post-termine (>42 settimane) | 2 | 0.0008 | 2500 | 1.0000 |
create_table(summary(Gestazione), FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Min. | 25.0000 |
2 | 1st Qu. | 38.0000 |
3 | Median | 39.0000 |
4 | Mean | 38.9804 |
5 | 3rd Qu. | 40.0000 |
6 | Max. | 43.0000 |
# Shape indeces
gestazione_shape_indeces <- evaluate_shape_indeces(Gestazione)
create_table(gestazione_shape_indeces, FALSE, FALSE)
Statistic | Value | |
---|---|---|
1 | Gini_Index | 0.8476 |
2 | Skewness | -2.0653 |
3 | Kurtosis | 8.2581 |
distr_gestazione <- rnorm(n, mean(Gestazione), sd(Gestazione))
hist(distr_gestazione,freq = F, breaks = 100)
lines(density(distr_gestazione),col=2, lwd=3)
qqnorm(distr_gestazione)
qqline(distr_gestazione)
Le variabili non presentano un’alta variabilità . Tuttavia, le variabili con più variabilità risultano essere il peso e gli anni della madre. Questa caratteristica è da considerare durante i test statistici.
lista_cv <- sort(c(
"Anni madre" = CV(Anni.madre),
"Settimane gestazione" = CV(Gestazione),
"Peso" = CV(Peso),
"Lunghezza" = CV(Lunghezza),
"Cranio" = CV(Cranio)
)
)
create_table(lista_cv, FALSE, FALSE)
Variable | VIF | |
---|---|---|
1 | Settimane gestazione | 4.7938 |
2 | Cranio | 4.8306 |
3 | Lunghezza | 5.3202 |
4 | Peso | 15.9874 |
5 | Anni madre | 18.7245 |
Dal BoxPlot è evidente che più aumentano gli anni della madre e più aumenta il numero di gravidanze. Ci sono molti valori outlier per le donne che hanno avuto 0 figli. Le donne che hanno 28 anni (valore mediano) hanno avuto in media 1-2 figli
boxplot(Anni.madre~N.gravidanze)
In tutti e 3 gli ospedali la fascia di età più presente è quella tra i 25 e i 29 anni. Nella distribuzione delle fasce di età nei 3 ospedali non c’è una sostanziale differenza.
ggplot(data=dati_neonati)+
geom_bar(aes(x=anni_madre_classi_cliniche,
fill=Ospedale),
position = "dodge",
stat = "count",
col = "black")+
labs(title = "Distribuzione delle fasce di età ",
x="Età delle madri in classi, anni",
y="Frequenze assolute")+
theme_classic()+
theme(legend.position = "bottom")
Il numero di parti nei diversi ospedali è distribuito più o meno equamente tra gli ospedali. L’ospedale numero 2 risulta essere quello dove si sono verificati più parti cesarei. Il tipo di parto più frequente è quello naturale per tutti e 3 gli ospedali. Inoltre, i parti molto prematuri (nella fascia 28-32) risultano essere più frequenti per neonati di sesso femminile. Approfondire questa relazione con opportuni test statistici.
ggplot(data=dati_neonati)+
geom_bar(aes(x=Tipo.parto,
fill=Ospedale),
position = "dodge",
stat = "count",
col = "black")+
labs(title = "Distribuzione della tipologia di parto negli ospedali",
x="Tipologia di parto",
y="Frequenze assolute")+
theme_classic()+
theme(legend.position = "bottom")
Dal boxplot emerge che, fino alla 35ª settimana di gestazione, i neonati di sesso femminile tendono ad avere un peso maggiore rispetto ai neonati di sesso maschile a parità di settimane di gestazione. A partire da questa settimana, però, il peso dei neonati di sesso maschile supera quello delle femmine, sempre a parità di settimane di gestazione. Inoltre, il peso dei neonati femminili appare più variabile rispetto a quello dei maschili, evidenziando una maggiore dispersione dei valori per le femmine rispetto ai maschi, sempre nello stesso intervallo di settimane di gestazione.
ggplot(dati_neonati, aes(x = factor(Gestazione), y = Peso, fill = Sesso)) +
geom_boxplot() +
labs(
title = "Distribuzione del peso per sesso e settimane di gestazione",
x = "Settimane di Gestazione",
y = "Peso (grammi)",
fill = "Sesso"
) +
theme_minimal()
Dal box plot e dal violin plot si può capire che l’essere fumatrice non incide sul numero di settimane di gestazione. Inoltre, bisogna considerare che per le donne non fumatrici ci sono molti valori outlier bassi. Per valutare questa tesi, saranno condotti opportuni test statistici.
boxplot(Gestazione ~ Fumatrici,
data = dati_neonati,
names = c("Non fumatrice", "Fumatrice"),
xlab = "Fumatrice",
ylab = "Settimane di gestazione",
main = "Distribuzione settimane di gestazione per stato di fumo",
col = c("lightblue", "salmon"))
ggplot(dati_neonati, aes(x = factor(Fumatrici), y = Gestazione, fill = factor(Fumatrici))) +
geom_violin(trim = FALSE) +
geom_boxplot(width = 0.1, fill = "white") +
scale_x_discrete(labels = c("Non fumatrice", "Fumatrice")) +
labs(x = "Fumatrice", y = "Settimane di gestazione",
title = "Violin plot: Settimane di gestazione per stato di fumo") +
theme_minimal()
Il box plot e il violin plot indicano che il peso dei neonati tendenzialmente è più basso nelle donne fumatrici. Tuttavia, sono presenti molti outlier per le donne non fumatrici. Dunque, saranno necessari opportuni test statistici per approfondire questa relazione e valutare la tesi che le donne fumatrici partoriscono neonati con un peso inferiore.
boxplot(Peso ~ Fumatrici,
data = dati_neonati,
names = c("Non fumatrice", "Fumatrice"),
xlab = "Fumatrice",
ylab = "Peso",
main = "Distribuzione Peso per stato di fumo",
col = c("lightblue", "salmon"))
ggplot(dati_neonati, aes(x = factor(Fumatrici), y = Peso, fill = factor(Fumatrici))) +
geom_violin(trim = FALSE) +
geom_boxplot(width = 0.1, fill = "white") +
scale_x_discrete(labels = c("Non fumatrice", "Fumatrice")) +
labs(x = "Fumatrice", y = "Peso",
title = "Violin plot: Peso per stato di fumo") +
theme_minimal()
Da questi dati emerge che sembra non esserci una differenza sostanziale tra le misure antropometriche tra i due sessi. Questi ipotesi andrà saggiata con dei test
#grafico indice Ponderale
plot_variation_by_group(
data = dati_neonati,
x_var = "Gestazione",
y_var = "Indice_Ponderale",
group_var = "Sesso",
lower_limit = 2.32,
upper_limit = 2.85,
lower_text = "2.32 - Normale",
upper_text = "2.85 - Normale",
y_min = 0,
y_max = 5.5
)
## `geom_smooth()` using formula = 'y ~ x'
ggplot(dati_neonati, aes(x = factor(Gestazione), y = Indice_Ponderale, fill = Sesso)) +
geom_boxplot() +
labs(
title = "Distribuzione dell'indice ponderale per sesso e settimane di gestazione",
x = "Settimane di Gestazione",
y = "Indice Ponderale",
fill = "Sesso"
) +
coord_cartesian(ylim = c(0, 6)) +
theme_minimal()
distr_Indice_Ponderale <- rnorm(n, mean(dati_neonati$Indice_Ponderale), sd(dati_neonati$Indice_Ponderale))
ylim <- c(0, max(density(dati_neonati$Indice_Ponderale)$y) + 0.02)
hist(distr_Indice_Ponderale, freq = F, breaks = 100, ylim=ylim, main="Histogram of Indice Ponderale")
lines(density(dati_neonati$Indice_Ponderale), col=2, lwd=3)
qqnorm(distr_Indice_Ponderale)
qqline(distr_Indice_Ponderale)
Il peso non sembra cambiare sostanzialmente tra gli ospedali. Sono presenti molti outlier e l’ipotesi che il peso sia uguale tra gli ospedali dovrà essere saggiata con opportuni test.
boxplot(Peso ~ Ospedale, main="Peso ~ Ospedale")
La variabile Sesso è stata resa una variabile dummy dove M=0 e F=1. La variabile dipendente Peso è stata trasformata, ottenendo la radice quadrata del peso. Dunque, quando sono state effettuate le previsioni si è tenuto conto di questo elevando al quadrato la previsione del peso. Per scegliere il modello, partendo da quello con tutte le variabili del dataset, si sono eliminate una ad una le variabili meno significative del modello. L’accettazione dell’ipotesi di significatività di una variabile nel modello è data dal p-value calcolato nel modello lineare e dalla diminuizione del bic. L’età della madre è stata inizialmente inclusa come variabile predittiva del peso del neonato, ma: Il p-value = 0.29 indica assenza di significatività statistica. La rimozione ha migliorato il BIC di 7 punti, suggerendo un modello più efficiente. In Conclusione l’età materna è stata esclusa dal modello finale per favorire semplicità e performance.
Si è scelto il modello 4 poichè risulta essere quello con il BIC più basso e quello più efficiente per spiegare la variabilità nei dati. Di seguito, sono state confrontate e analizzate tutte le statistiche adeguate per poter scegliere il miglior modello.
Le variabili maggiormente correlate risultano essere:
# --- Step 1: Costruisci modelli iniziali
modelli_iniziali <- crea_modelli_ridotti(dati_neonati)
# --- Step 2: Pulizia outlier su mod1
dati_puliti <- rimuovi_outliers_iterativi(
modello = modelli_iniziali[["mod1"]],
data = dati_neonati,
formula_modello = sqrt(Peso) ~ .
)
Rimozione Outlier
È stato rimosso circa il 7% delle osservazioni rilevate come outlier statisticamente significativi. Gli outlier più influenti statisticamente sono stati identificati attraverso un valore soglia scelto empiricamente con 4/n e da cui è stata calcolata la distanza di Cook. Vengono eseguite due iterazioni perchè nella prima vengono identificati gli outlier rispetto al modello originale, mentre nella seconda, dopo aver rimosso gli outlier, il modello viene ricalcolato e potrebbe avere nuovi outlier che prima non erano rilevabili. Infatti, è stato scelto di rimuovere ulteriori 4 outlier singificativi per migliorare il modello.
Rimosse 53 osservazioni (prima iterazione)
Rimosse 131 osservazioni (seconda iterazione)
Totale osservazioni rimosse: 184
# --- Step 3: Matrice di correlazione
disegna_matrice_correlazione(dati_puliti)
# --- Step 4: Confronto varianze
confronta_varianze(dati_puliti, "Sesso", "Fumatrici")
Test confronto varianza: Peso ~ Sesso
La varianza tra il peso e il sesso risulta essere uguale. Questo è stato considerato nei test statistici
F test to compare two variances
data: Peso by Sesso F = 1.0078, num df = 1160, denom df = 1154, p-value = 0.8943 alternative hypothesis: true ratio of variances is not equal to 1 95 percent confidence interval: 0.8980836 1.1310049 sample estimates: ratio of variances 1.007847
Statistic | Value | |
---|---|---|
1 | F | 1.0078 |
2 | p.value | 0.8943 |
3 | num df | 1160.0000 |
4 | denom df | 1154.0000 |
5 | ratio of variances | 1.0078 |
6 | conf.int_lower | 0.8981 |
7 | conf.int_upper | 1.1310 |
Test confronto varianza: Peso ~ Fumatrici
La varianza tra il peso e l’essere fumatrici risulta essere uguale. Questo è stato considerato nei test statistici
F test to compare two variances
data: Peso by Fumatrici F = 1.1598, num df = 2221, denom df = 93, p-value = 0.3568 alternative hypothesis: true ratio of variances is not equal to 1 95 percent confidence interval: 0.8463379 1.5264613 sample estimates: ratio of variances 1.159774
Statistic | Value | |
---|---|---|
1 | F | 1.1598 |
2 | p.value | 0.3568 |
3 | num df | 2221.0000 |
4 | denom df | 93.0000 |
5 | ratio of variances | 1.1598 |
6 | conf.int_lower | 0.8463 |
7 | conf.int_upper | 1.5265 |
# --- Step 5: Ricostruisci i modelli sui dati puliti
modelli_finali <- crea_modelli_ridotti(dati_puliti)
# --- Step 6: Confronti BIC/ANOVA
confronta_modelli(modelli_finali)
ANOVA confronto tra gli ultimi due modelli
I valori del test statistico evidenziano come il mod4 abbia una capacità più elevata di spiegare la variabilità nei dati. Dunque, il parametro che è stato rimosso dal modello 3 al modello 4 non è statisticamente significativo. Questo rende il modello 4 più semplice e statisticamente rilevante.
Res.Df | RSS | Df | Sum of Sq | F | Pr(>F) | |
---|---|---|---|---|---|---|
1 | 2308 | 5048.828 | NA | NA | NA | NA |
2 | 2309 | 5051.365 | -1 | -2.5373 | 1.1599 | 0.2816 |
Confronto tra modelli: BIC, MSE, RMSE
L’MSE e l’RMSE non cambia tra tutti i modelli. Questo indica che semplificando il modello la variabilità nei dati non peggiora significativamente. Pertanto, è adatto scegliere l’ultimo modello che risulta essere meno complesso. Inoltre, Il BIC più basso di 7 punti risulta essere significativo poichè indica che la qualità dell’adattamento ai dati è migliore.
# --- Step 7: Diagnostica finale
diagnostica_modello(modelli_finali[["mod4"]])
VIF
I VIF del mod4 sono inferiori a 5 per tutti i regressori indica che non ci sono problemi di multicollinearità , ovvero di correlazione tra i regressori
## VIFs computed for predictors
Analisi dei residui mod4
Dai seguenti plot dei residui è emerso che:
Breusch-Pagan Test
Un p-value molto alto indica che si può accettare l’ipotesi nulla di omoschedasticitÃ
studentized Breusch-Pagan test
data: modello BP = 9.8274, df = 6, p-value = 0.1321
Statistic | Value | |
---|---|---|
1 | BP | 9.8274 |
BP | p.value | 0.1321 |
11 | df | 6.0000 |
Durbin-Watson Test
il p-value molto alto indica che si può accettare l’ipotesi nulla di non correlazione tra i regressori. Durbin-Watson test
data: modello DW = 2.0223, p-value = 0.7043 alternative hypothesis: true autocorrelation is greater than 0
Statistic | Value | |
---|---|---|
1 | DW | 2.0223 |
2 | p.value | 0.7043 |
Outlier Test
Valutazione del residuo standardizzato usato per identificare i possibili outliers significativi in un modello di regressione. In questo caso il Largest r-student e l’unadjusted p-value suggeriscono che ci sia almeno un outlier significativo. Tuttavia, con il criterio Bonferroni(tiene conto del numero di test effettuati) e il relativo calcolo del p-value,che in questo caso si attesta all’incirca su 0.07, si potrebbe escludere l’ipotesi che ci sia almeno un outlier significativo. Questo punto non incide significativamente sulle prestazioni del modello.
Row | Rstudent | P_value | Bonferroni_P_value | Significant | Cutoff | |
---|---|---|---|---|---|---|
1395 | 1395 | 4.163168 | 3.25e-05 | 0.0753583 | FALSE | 0.05 |
Distanza di Cook, Residui e Leverage
I residui si distribuiscono normalmente e risultano avere varianza
costante. Non sono presenti punti di leverage significativi. Il punto
con massima distanza di cook ha distanza molto inferiore a
0.5
Max Cook’s Distance at index 2244 : 0.0098
# --- Step 8: Summary finale
cat("\n\n<b>Summary mod4</b>\n\n")
Summary mod4
cat("\nIl modello 4 risulta avere una capacità di spiegare la variabilità nei dati di 0.86, ovvero corrisponde al valore dell'adjusted R-Squared. Tutte le variabili del modello risultano essere siginificative. Anche se i regressori Fumatrici e N.gravidanze risultano avere un p-value vicino a 0.05, si è scelto di mantenerle nel modello perchè potrebbero essere utili ai fine della previsione del peso. In conclusione, il p-value vicino a 0 indica che le variabili sono statisticamente significative nel predire la variabile dipendente")
Il modello 4 risulta avere una capacità di spiegare la variabilità nei dati di 0.86, ovvero corrisponde al valore dell’adjusted R-Squared. Tutte le variabili del modello risultano essere siginificative. Anche se i regressori Fumatrici e N.gravidanze risultano avere un p-value vicino a 0.05, si è scelto di mantenerle nel modello perchè potrebbero essere utili ai fine della previsione del peso. In conclusione, il p-value vicino a 0 indica che le variabili sono statisticamente significative nel predire la variabile dipendente
create_table(summary(modelli_finali[["mod4"]]),F,F)
Term | Estimate | Std_Error | t_value | p_value | |
---|---|---|---|---|---|
1 | (Intercept) | -12.595889779907 | 0.8776 | -14.3533 | 0.0000 |
2 | N.gravidanze | -0.051592409271825 | 0.0259 | -1.9887 | 0.0469 |
3 | Fumatrici | -0.307155415736831 | 0.1565 | -1.9623 | 0.0499 |
4 | Gestazione | 0.287015071056442 | 0.0230 | 12.4814 | 0.0000 |
5 | Sesso | -0.378238192074649 | 0.0631 | -5.9917 | 0.0000 |
6 | Indice_Ponderale | 6.93676561968473 | 0.1392 | 49.8326 | 0.0000 |
7 | Lunghezza:Cranio | 0.000237931283916777 | 0.0000 | 84.2637 | 0.0000 |
8 | Residual Std. Error | 1.47908259237042 | NA | NA | NA |
9 | Multiple R-squared | 0.858807802497925 | NA | NA | NA |
10 | Adjusted R-squared | 0.858440910689777 | NA | NA | NA |
11 | F-statistic | 2340.76581549836 | NA | NA | NA |
12 | Model DF | 6 and 2309 | NA | NA | NA |
13 | p-value | 0 | NA | NA | NA |
In questa fase saranno condotti test per saggiare le seguenti ipotesi:
È risultato statisticamente rilevante rimuovere all’incirca il 7% dei valori outlier poichè questi alteravano gli esiti dei test. Infatti, è stato notato che nel test per verificare l’incidenza dell’essere fumatrici sul peso, in caso di presenza di outlier il test induceva ad accettare l’ipotesi nulla di media peso fumatrici - media peso non fumatrici = 0. Al contrario, in caso di assenza degli outlier, il test ha portato al rifiuto dell’ipotesi nulla. Dunque, la presenza di outlier ha influenzato questo test.
Per saggiare questa ipotesi è corretto usare il test del chi quadrato sulla tabella di contingenza, tra Ospedale e tipo_parto, perchè il sistema di ipotesi è costruito nel seguente modo:
Dal test è emerso che:
tabella_contingenza <- table(dati_puliti$Ospedale, dati_puliti$Tipo.parto)
# Visualizzare la tabella di contingenza
tabella_risultante <- crea_tabella_contingenza_stilizzata(
dati_puliti$Ospedale,
dati_puliti$Tipo.parto,
caption = "Distribuzione dei parti per ospedale",
gruppo_colonne = "Tipo di parto",
)
tabella_risultante
Ces | Nat | Totale | |
---|---|---|---|
osp1 | 223 | 527 | 750 |
osp2 | 234 | 557 | 791 |
osp3 | 216 | 559 | 775 |
Totale | 673 | 1643 | 2316 |
# Eseguire il test del chi-quadro
test_chi_quadro <- chisq.test(tabella_contingenza)
#per valutare la forza dell'associazione si può usare l'indice di Kramer
k <- min(nrow(tabella_contingenza), ncol(tabella_contingenza))
kramer_index <- (sqrt(test_chi_quadro$statistic))/(n*(k-1))
test_chi_quadro_values <- c("Test Value" = test_chi_quadro$statistic, "p-value" = test_chi_quadro$p.value, "Degrees of Freedom" = test_chi_quadro$parameter, "Kramer Index" = kramer_index)
test_chi_quadro_df <- data.frame(
Statistic = names(test_chi_quadro_values),
Value = as.numeric(test_chi_quadro_values)
)
# Visualizzare i risultati del test
cat("<b>chi-quadro-test Summary</b><br>")
chi-quadro-test Summary
create_table(test_chi_quadro_df, F, F)
Statistic | Value | |
---|---|---|
1 | Test Value.X-squared | 0.8013 |
2 | p-value | 0.6699 |
3 | Degrees of Freedom.df | 2.0000 |
4 | Kramer Index.X-squared | 0.0004 |
# Interpretazione dei risultati
if (test_chi_quadro$p.value < 0.05) {
cat("C'è una differenza significativa nella proporzione di parti cesarei tra gli ospedali.\n")
} else {
cat("Non c'è una differenza significativa nella proporzione di parti cesarei tra gli ospedali.\n")
}
Non c’è una differenza significativa nella proporzione di parti cesarei tra gli ospedali.
Questo test ha l’obiettivo di verificare l’ipotesi che la media del peso della popolazione di neonati, uguale a 3300 grammi, sia uguale alla media del peso. Dunque, è corretto utilizzare lo z-test dove il sistema di ipotesi corrisponde a:
Dal test sono emerse le seguenti considerazioni:
population_weight_mean <- 3300
alpha_weight_test_value <- 0.95
z_test_peso <- z.test(x = dati_puliti$Peso,
mu = population_weight_mean,
sigma.x = sd(dati_puliti$Peso),
alternative = "two.sided",
conf.level = alpha_weight_test_value)
weight_mean_estimate_and_alpha <- c("Alpha" = alpha_weight_test_value, "Weight Mean" = mean(dati_puliti$Peso))
cat("<b>General Results</b><br>")
General Results
sink(tempfile())
create_table(weight_mean_estimate_and_alpha, F, F)
Variable | VIF | |
---|---|---|
1 | Alpha | 0.95 |
2 | Weight Mean | 3305.83 |
sink()
cat("<b>z-test Summary</b><br>")
z-test Summary
sink(tempfile())
create_table(z_test_peso, F, F)
Statistic | Value | |
---|---|---|
1 | z | 0.6284 |
2 | p.value | 0.5298 |
3 | mean of x | 3305.8299 |
4 | conf.int_lower | 3287.6457 |
5 | conf.int_upper | 3324.0141 |
sink()
Il sistema di ipotesi per questo test è la seguente:
È stato usato un valore di alpha molto piccolo, ovvero 0.005 per coda. La media della popolazione non si trova all’interno dell’intervallo di confidenza.
Dal test sono emerse le seguenti considerazioni:
population_length_mean <- 500
alpha_length_test_value <- 0.99
z_test_lunghezza <- z.test(x = dati_puliti$Lunghezza,
mu = population_length_mean,
sigma.x = sd(dati_puliti$Lunghezza),
alternative = "two.sided",
conf.level = alpha_length_test_value)
length_mean_estimate_and_alpha <- c("Alpha" = alpha_length_test_value, "Weight Mean" = mean(dati_puliti$Lunghezza))
cat("<b>General Results</b><br>")
General Results
sink(tempfile())
create_table(length_mean_estimate_and_alpha, F, F)
Variable | VIF | |
---|---|---|
1 | Alpha | 0.9900 |
2 | Weight Mean | 496.3493 |
sink()
cat("<b>t-test Summary</b><br>")
t-test Summary
sink(tempfile())
create_table(z_test_lunghezza, F, F)
Statistic | Value | |
---|---|---|
1 | z | -8.4666 |
2 | p.value | 0.0000 |
3 | mean of x | 496.3493 |
4 | conf.int_lower | 495.2386 |
5 | conf.int_upper | 497.4600 |
sink()
Test per saggiare l’ipotesi che le misure antropometriche differiscono tra i due sessi
Per confrontare le misure antropometriche tra maschi e femmine, è stato eseguito un t-test per campioni indipendenti. La numerosità del campione (n > 30 per ciascun gruppo) consente di applicare il test in modo robusto grazie al teorema del limite centrale, anche senza una perfetta normalità . Il test di Shapiro-Wilk non è stato considerato determinante, in quanto la sua elevata sensibilità tende a far rifiutare l’ipotesi di normalità anche in presenza di lievi deviazioni in grandi campioni (>500).
Sono state formulate le seguenti ipotesi:
Sono stati eseguiti sia t-test che test di Wilcoxon per verificare la coerenza tra metodi parametrici e non parametrici.
Risultati:
Conclusione:
I test evidenziano che esistono differenze significative tra i sessi per quanto riguarda sia il peso sia l’indice ponderale. Pertanto, le misure antropometriche dipendono dal sesso.
# t-test
cat("<b>Peso - Sesso Two Samples t-test</b><br>")
Peso - Sesso Two Samples t-test
sink(tempfile())
create_table(t.test(dati_puliti$Peso ~ dati_puliti$Sesso, var.equal = T), F,F)
Statistic | Value | |
---|---|---|
1 | t | -12.6972 |
2 | p.value | 0.0000 |
3 | df | 2314.0000 |
4 | mean in group F | 3192.1981 |
5 | mean in group M | 3420.0519 |
6 | conf.int_lower | -263.0441 |
7 | conf.int_upper | -192.6636 |
sink()
cat("<b>Indice Ponderale - Sesso Two Samples t-test</b><br>")
Indice Ponderale - Sesso Two Samples t-test
sink(tempfile())
create_table(t.test(dati_puliti$Indice_Ponderale ~ dati_puliti$Sesso, var.equal = TRUE), F,F)
Statistic | Value | |
---|---|---|
1 | t | -4.7905 |
2 | p.value | 0.0000 |
3 | df | 2314.0000 |
4 | mean in group F | 2.6721 |
5 | mean in group M | 2.7165 |
6 | conf.int_lower | -0.0625 |
7 | conf.int_upper | -0.0262 |
sink()
cat("<b>Lunghezza - Sesso Two Samples t-test</b><br>")
Lunghezza - Sesso Two Samples t-test
sink(tempfile())
create_table(t.test(dati_puliti$Lunghezza ~ dati_puliti$Sesso, var.equal = T), F,F)
Statistic | Value | |
---|---|---|
1 | t | -10.3268 |
2 | p.value | 0.0000 |
3 | df | 2314.0000 |
4 | mean in group F | 492.0060 |
5 | mean in group M | 500.7152 |
6 | conf.int_lower | -10.3629 |
7 | conf.int_upper | -7.0553 |
sink()
cat("<b>Cranio - Sesso Two Samples t-test</b><br>")
Cranio - Sesso Two Samples t-test
sink(tempfile())
create_table(t.test(dati_puliti$Cranio ~ dati_puliti$Sesso, var.equal = TRUE), F,F)
Statistic | Value | |
---|---|---|
1 | t | -7.0990 |
2 | p.value | 0.0000 |
3 | df | 2314.0000 |
4 | mean in group F | 338.8053 |
5 | mean in group M | 342.9506 |
6 | conf.int_lower | -5.2904 |
7 | conf.int_upper | -3.0002 |
sink()
# Wilcox test
cat("<b>Indice Ponderale - Sesso Wilcox Test</b><br>")
Indice Ponderale - Sesso Wilcox Test
sink(tempfile())
create_table(wilcox.test(dati_puliti$Indice_Ponderale ~ dati_puliti$Sesso),F,F)
Statistic | Value | |
---|---|---|
1 | W | 589400 |
2 | p.value | 0 |
sink()
cat("<b>Peso - Sesso Wilcox Test</b><br>")
Peso - Sesso Wilcox Test
sink(tempfile())
create_table(wilcox.test(dati_puliti$Peso ~ dati_puliti$Sesso),F,F)
Statistic | Value | |
---|---|---|
1 | W | 459573.5 |
2 | p.value | 0.0 |
sink()
cat("<b>Lunghezza - Sesso Wilcox Test</b><br>")
Lunghezza - Sesso Wilcox Test
sink(tempfile())
create_table(wilcox.test(dati_puliti$Lunghezza ~ dati_puliti$Sesso),F,F)
Statistic | Value | |
---|---|---|
1 | W | 503296 |
2 | p.value | 0 |
sink()
cat("<b>Cranio - Sesso Wilcox Test</b><br>")
Cranio - Sesso Wilcox Test
sink(tempfile())
create_table(wilcox.test(dati_puliti$Cranio ~ dati_puliti$Sesso),F,F)
Statistic | Value | |
---|---|---|
1 | W | 554632 |
2 | p.value | 0 |
sink()
Il sistema di ipotesi è costruito nel seguente modo:
Dal test è emerso che possiamo rifiutare H0 poichè è stato ottenuto un p-value vicino a 0. Questo indica che l’essere fumatrici influenza il peso del neonato
cat("<b>Peso - Fumatrici Two Samples t-test</b><br>")
Peso - Fumatrici Two Samples t-test
sink(tempfile())
create_table(t.test(dati_puliti$Peso ~ dati_puliti$Fumatrici, var.equal = TRUE), F,F)
Statistic | Value | |
---|---|---|
1 | t | 2.8600 |
2 | p.value | 0.0043 |
3 | df | 2314.0000 |
4 | mean in group 0 | 3311.2790 |
5 | mean in group 1 | 3177.0213 |
6 | conf.int_lower | 42.2017 |
7 | conf.int_upper | 226.3138 |
sink()
L’anova test per il confronto tra medie è quello più appropriato perchè il peso è una variabile quantitativa e si vuole confrontare questa variabile con la variabila categorica legata all’ospedale che possiede più di due valori possibili. Se i valori possibili fossero stati due si sarebbe potuto usare un t-test. Questo test è utile per verificare se sono presenti ospedali dove la media del peso di un neonato risulta essere anomala, creando cosi una difficoltà nella gestione dei parti a rischio. L’anova test consente di confrontare la variabilità interna tra i gruppi e la variabilità tra i gruppi. Il valore della statistica è dato dal rapporto della variabilità tra gruppi e quella della variabilità interna dei gruppi.
Il sistema di ipotesi è costruito nel seguente modo:
Dal test è emerso un valore della statistica F vicino ad un 1 ed un p-value maggiore di 0.05. Questo indica che accettiamo l’ipotesi nulla H0 affermando che le medie del peso sono uguali tra gli ospedali. Questo indica che negli ospedali non vi è una differenza sostanziale dei valori anomali nel peso dei neonati.
cat("<b>Anova Test Summary - Media Peso negli Ospedali</b><br>")
Anova Test Summary - Media Peso negli Ospedali
anova_test <- aov(dati_puliti$Peso ~ dati_puliti$Ospedale, data = dati_puliti)
create_table(anova_test, F,F)
Statistic | Value | |
---|---|---|
1 | F value | 1.1026 |
2 | p-value | 0.3322 |
3 | num Df | 2.0000 |
4 | denom Df | 2313.0000 |
Vengono generati dei valori randomici in base al minimo e al massimo dei regressori per poter effettuare delle previsioni. Il risultato delle previsioni viene elevato al quadrato perchè durante la costruzione del modello di regressione è stata eseguita la radice quadrata del peso.
data_mod <- dati_puliti
data_mod$Sesso <- ifelse(data_mod$Sesso == "F", 1, 0)
nuovi_dati_casuali <- generate_random_data(data_mod, n=2)
previsioni <- predict(modelli_finali[["mod4"]], newdata = nuovi_dati_casuali)
nuovi_dati_casuali$predict_value <- previsioni^2
create_table(nuovi_dati_casuali, F, F)
Gestazione | Lunghezza | Cranio | Sesso | N.gravidanze | Fumatrici | Indice_Ponderale | predict_value | |
---|---|---|---|---|---|---|---|---|
1 | 38 | 413.3945 | 357.3331 | 0 | 12 | 0 | 2.9605 | 2848.901 |
2 | 40 | 462.2450 | 345.8696 | 1 | 10 | 0 | 2.4391 | 2803.647 |
Il fumo risulta avere un’incidenza negativa sul peso del neonato anche a parità di settimane di gestazione e di tutte le altre variabili. Tranne per la lunghezza, dove non vi è una grande differenza di peso dei neonati tra donne fumatrici e non fumatrici. La retta nello scatterplot del numero gravidanze in relazione alle settimane di gestazione è quasi orizzontale. Questo indica che la variabile X non è un buon predittore di Y e che probabilmente non c’è una relazione lineare significativa tra le due variabili. Tuttavia, si è deciso di includere questa variabile nelle predizioni perchè l’avere gravidanze potrebbe incidere sul peso del bambino poichè le gravidanze precedenti potrebbero aver influenzato la salute della madre. Invece se una donna non ha mai avuto gravidanze i fattori genetici e nutrizionali potrebbero incidere sul peso del bambino
ggplot(dati_puliti, aes(x = as.factor(Fumatrici), y = Peso)) +
geom_boxplot(fill = c("#66c2a5", "#fc8d62")) +
labs(x = "Fumo materno (0 = No, 1 = Sì)", y = "Peso del neonato (g)",
title = "Effetto del fumo sul peso del neonato") +
theme_minimal()
ggplot(dati_puliti, aes(x = Gestazione, y = Peso, color = as.factor(Fumatrici))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE) +
labs(x = "Settimane di gestazione", y = "Peso del neonato (g)",
color = "Fumatrici",
title = "Peso in funzione delle settimane di gestazione") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
ggplot(dati_puliti, aes(x = N.gravidanze, y = Peso)) +
geom_point(alpha = 0.6, color = "#8da0cb") +
geom_smooth(method = "lm", se = TRUE, color = "black") +
labs(x = "N.gravidanze", y = "Peso del neonato (g)",
title = "Relazione tra N.gravidanze e Peso") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
ggplot(dati_puliti, aes(x = Sesso, y = Peso, fill = as.factor(Fumatrici))) +
geom_violin(trim = FALSE, alpha = 0.6) +
labs(x = "Sesso", y = "Peso del neonato (g)", fill = "Fumatrici",
title = "Relazione tra Sesso e Peso (con distinzione Fumatrici)") +
theme_minimal()
ggplot(dati_puliti, aes(x = Lunghezza, y = Peso, color = as.factor(Fumatrici))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, aes(color = as.factor(Fumatrici))) +
labs(x = "Lunghezza", y = "Peso del neonato (g)", color = "Fumatrici",
title = "Relazione tra Lunghezza e Peso (per Fumatrici)") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
ggplot(dati_puliti, aes(x = Cranio, y = Peso, color = as.factor(Fumatrici))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, aes(color = as.factor(Fumatrici))) +
labs(x = "Cranio", y = "Peso del neonato (g)", color = "Fumatrici",
title = "Relazione tra Cranio e Peso (per Fumatrici)") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'