Codi
library(tidyverse)
library(car)
library(boot)
library(pROC)
library(gtsummary)Capítol 4: Models estadístics en biomedicina
library(tidyverse)
library(car)
library(boot)
library(pROC)
library(gtsummary)Un model estadístic és una representació matemàtica simplificada de la realitat que descriu com es relaciona una variable clínica de resposta (variable dependent, \(Y\)) amb un o diversos factors d’estudi o confusors (variables independents o predictors, \(X\)):
\[Y = f(X) + \varepsilon\]
On: * \(Y\): variable dependent o resposta clínica (ex: pressió arterial, mortalitat). * \(X\): variable o variables independents (predictors o factors de risc). * \(\varepsilon\): error aleatori o soroll biològic no explicat.
En l’àmbit biomèdic utilitzem els models amb tres finalitats essencials: 1. Explicar: Quantificar l’associació directa entre factors i desenllaços clínics. 2. Controlar confusió: Aïllar l’efecte d’una variable ajustant per la resta. 3. Predir: Estimar valors o probabilitats futures per ajudar en la presa de decisions.
Imaginem que volem estimar l’alçada d’un pacient que entra per la porta de consultes, però no disposem de cap informació clínica addicional.
En absència de qualsevol dada, el millor model possible és la mitjana mostral (\(\bar{Y}\)):
\[Y_i = \bar{Y} + \varepsilon_i\]
Aquest model elemental diu: “Com que no sé res més d’aquest subjecte, estimo que mesura exactament la mitjana del grup”.
set.seed(42)
alçades <- rnorm(100, mean = 170, sd = 10)
mitjana <- mean(alçades)
plot(alçades, rep(1, 100), pch = 16, col = "gray50", yaxt = "n",
xlab = "Alçada (cm)", ylab = "",
main = "La mitjana com a model (estimació basal constant)")
abline(v = mitjana, col = "firebrick", lwd = 2)
text(mitjana + 1, 1.2, paste("Mitjana =", round(mitjana, 1), "cm"),
col = "firebrick", pos = 4)Com és evident, pràcticament cap pacient mesurarà exactament la mitjana. La diferència individual entre el valor observat real de cada pacient (\(Y_i\)) i l’estimació del model (\(\hat{Y}_i = \bar{Y}\)) s’anomena residu o error:
\[\varepsilon_i = Y_i - \hat{Y}_i\]
El conjunt dels residus ens indica com d’allunyat està el món real del nostre model:
residus <- alçades - mitjana
hist(residus, col = "#BBDEFB", border = "white",
main = "Distribució dels residus respecte a la mitjana",
xlab = "Alçada real - Mitjana (cm)",
breaks = 10)
abline(v = 0, col = "firebrick", lwd = 2, lty = 2)La desviació estàndard (SD) és una mesura de la magnitud mitjana d’aquests errors. Com més gran sigui la dispersió dels residus, menor és la precisió del nostre model.
Ara imaginem que sabem una dada addicional de cada pacient: la seva edat. Ja no ens cal assumir una mitjana plana horitzontal constant, sinó que podem ajustar una recta amb pendent que s’adapti als canvis d’edat:
set.seed(123)
edat <- sample(20:80, 100, replace = TRUE)
alçada <- 150 + 0.4 * edat + rnorm(100, mean = 0, sd = 5)
dades <- data.frame(edat, alçada)
model_intro <- lm(alçada ~ edat, data = dades)
plot(dades$edat, dades$alçada, pch = 16, col = "gray40",
xlab = "Edat (anys)", ylab = "Alçada (cm)",
main = "Model de regressió lineal: adaptació segons predictor")
abline(h = mean(dades$alçada), col = "gray70", lty = 3, lwd = 1.5)
abline(model_intro, col = "#1976D2", lwd = 2.5)
legend("topleft", legend = c("Model mitjana", "Model lineal"),
col = c("gray70", "#1976D2"), lty = c(3, 1), lwd = c(1.5, 2.5), bty = "n")En analitzar la variabilitat tenim tres components: 1. Variabilitat total: Quant varien les respostes observades al voltant de la mitjana. 2. Variabilitat explicada: La fracció que aconsegueix capturar la recta de regressió a través del predictor. 3. Variabilitat no explicada: Els residus individuals que encara queden al voltant de la línia.
En la regressió lineal simple modelitzem la relació entre dues variables quantitatives:
\[Y = \beta_0 + \beta_1 X + \varepsilon\]
On: * \(Y\): variable dependent (resposta contínua). * \(X\): variable independent (predictora quantitativa). * \(\beta_0\): interceptació (valor esperat de \(Y\) quan \(X = 0\)). * \(\beta_1\): pendent o slope (canvi esperat en \(Y\) per cada unitat d’increment en \(X\)). * \(\varepsilon\): error aleatori o residu.
Analitzem la relació entre l’edat i la pressió arterial sistòlica (TAS) en una cohort simulada de 100 pacients:
set.seed(123)
n <- 100
edat <- sample(30:80, n, replace = TRUE)
pressio <- 90 + 0.7 * edat + rnorm(n, mean = 0, sd = 10)
dades <- data.frame(edat, pressio)
# Ajust del model de regressió lineal simple
model <- lm(pressio ~ edat, data = dades)
summary(model)
Call:
lm(formula = pressio ~ edat, data = dades)
Residuals:
Min 1Q Median 3Q Max
-26.3007 -5.9010 0.2208 5.8581 20.0910
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 86.94105 3.93131 22.11 <2e-16 ***
edat 0.75584 0.06857 11.02 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.241 on 98 degrees of freedom
Multiple R-squared: 0.5536, Adjusted R-squared: 0.549
F-statistic: 121.5 on 1 and 98 DF, p-value: < 2.2e-16
Perquè les conclusions del model siguin vàlides cal avaluar cinc supòsits essencials:
| Supòsit | Què avalua | Com es comprova |
|---|---|---|
| Linealitat | Relació lineal directa entre \(X\) i \(Y\) | Diagrama de dispersió i residus vs. ajustats |
| Independència | Observacions no aparellades ni agrupades | Disseny de l’estudi |
| Normalitat dels residus | Errors distribuïts segons una campana de Gauss | QQ-plot i shapiro.test() |
| Homocedasticitat | Variància constant dels residus al llarg dels valors | Residus vs. ajustats i Scale-Location |
| Absència de punts influents | Cap cas aïllat condiciona la recta | Distància de Cook (plot(model, which = 4)) |
ggplot(dades, aes(x = edat, y = pressio)) +
geom_point(color = "gray40", alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "#1976D2") +
labs(title = "Pressió arterial en funció de l'edat",
x = "Edat (anys)",
y = "Pressió sistòlica (mmHg)") +
theme_classic()Una observació amb una distància de Cook \(> 1\) pot exercir una influència desproporcionada sobre l’estimació dels paràmetres:
plot(model, which = 4, col = "#D32F2F")Executant plot(model) obtenim la bateria diagnòstica completa:
par(mfrow = c(2, 2))
plot(model)
par(mfrow = c(1, 1))Quantifica la fracció de variància de la variable dependent que queda explicada pel model:
summary(model)$r.squared # R2[1] 0.5535698
summary(model)$adj.r.squared # R2 ajustat[1] 0.5490144
En aquest model, el 55.4% de la variabilitat de la pressió arterial s’explica directament per l’edat del pacient.
L’AIC (Akaike Information Criterion) i el BIC (Bayesian Information Criterion) penalitzen la complexitat per evitar el sobreajust. Valors inferiors indiquen millor equilibri entre parsimònia i precisió:
AIC(model)[1] 732.4929
BIC(model)[1] 740.3084
Permet confrontar la concordança directa entre les prediccions del model i les observacions reals:
ggplot(data = dades, aes(x = fitted(model), y = pressio)) +
geom_point(color = "gray40", alpha = 0.7) +
geom_abline(intercept = 0, slope = 1, col = "red", linetype = "dashed", linewidth = 1) +
labs(title = "Valors observats vs. ajustats",
x = "Prediccions del model (mmHg)",
y = "Pressió observada (mmHg)") +
theme_classic()Quan tenim més d’un predictor que pot influir sobre una variable resposta quantitativa, utilitzem la regressió lineal múltiple.
Aquest model permet estimar l’efecte de cada variable predictora sobre la resposta, ajustant per les altres. És l’eina bàsica en biomedicina per avaluar associacions clíniques controlant per possibles factors de confusió o per construir sistemes de predicció.
Objectius del model múltiple: * Estimar l’efecte individual de cada predictor. * Controlar variables confusores (edat, comorbiditats, gravetat basal). * Fer prediccions ajustades a perfils concrets de pacients. * Comparar la contribució relativa de cada factor.
\[Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \dots + \beta_p X_p + \varepsilon\]
On: * \(Y\): variable dependent o desenllaç continu. * \(X_1, X_2, \dots, X_p\): variables independents o predictors (continus o categòrics). * \(\beta_0\): interceptació (valor esperat de \(Y\) quan tots els predictors valen 0). * \(\beta_i\): efecte estimat del predictor \(X_i\) mantenint constants tots els altres predictors. * \(\varepsilon\): error aleatori o residu.
Suposem que volem predir la pressió arterial sistòlica d’un pacient utilitzant l’edat, el pes i el consum de tabac (fuma).
set.seed(123)
n <- 100
edat <- sample(30:80, n, replace = TRUE)
pes <- rnorm(n, mean = 70, sd = 12)
fuma <- sample(c(0, 1), n, replace = TRUE)
pressio <- 90 + 0.4 * edat + 0.3 * pes + 5 * fuma + rnorm(n, mean = 0, sd = 10)
dades2 <- data.frame(
pressio = pressio,
edat = edat,
pes = pes,
fuma = factor(fuma, levels = c(0, 1), labels = c("No", "Sí"))
)Ajustem el model amb la funció lm() incloent els tres predictors sumats:
model_mult <- lm(pressio ~ edat + pes + fuma, data = dades2)
summary(model_mult)
Call:
lm(formula = pressio ~ edat + pes + fuma, data = dades2)
Residuals:
Min 1Q Median 3Q Max
-33.278 -7.769 1.992 7.352 28.922
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 96.33731 8.11307 11.874 < 2e-16 ***
edat 0.40038 0.08162 4.905 3.8e-06 ***
pes 0.21278 0.09973 2.134 0.0354 *
fumaSí 3.56173 2.22931 1.598 0.1134
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 10.95 on 96 degrees of freedom
Multiple R-squared: 0.2623, Adjusted R-squared: 0.2392
F-statistic: 11.38 on 3 and 96 DF, p-value: 1.89e-06
La interpretació en regressió múltiple és estrictament condicional: cada coeficient quantifica l’efecte d’aquella variable mantenint constants la resta de variables del model:
edat (\(\beta_1 = 0.40\) mmHg/any; \(p < 0.001\)): Per cada any d’edat addicional, la pressió arterial s’incrementa de mitjana en 0.40 mmHg, després d’ajustar per pes i consum de tabac.pes (\(\beta_2 = 0.21\) mmHg/kg; \(p = 0.035\)): Cada quilogram addicional de pes s’associa a un augment mitjà de 0.21 mmHg de pressió sistòlica, ajustant per edat i tabac.fumaSí (\(\beta_3 = 3.56\) mmHg; \(p = 0.113\)): Els individus fumadors presenten, de mitjana, 3.56 mmHg més de pressió arterial sistòlica que els no fumadors després de controlar per edat i pes. En aquesta mostra de 100 subjectes, aquesta diferència no assoleix la significació estadística al llindar estàndard de 0.05.summary(model_mult)$r.squared # R2 brut[1] 0.2622832
summary(model_mult)$adj.r.squared # R2 ajustat[1] 0.2392295
ggplot(data = dades2, aes(x = fitted(model_mult), y = pressio)) +
geom_point(color = "gray40", alpha = 0.7) +
geom_abline(intercept = 0, slope = 1, col = "red", linetype = "dashed", linewidth = 1) +
labs(title = "Valors observats vs. ajustats (model múltiple)",
x = "Prediccions del model (mmHg)",
y = "Pressió observada (mmHg)") +
theme_classic()Podem contrastar si la inclusió de pes i tabac aporta una millora substancial comparant criteris d’informació:
AIC(model) # Model simple (només edat)[1] 732.4929
AIC(model_mult) # Model múltiple (edat, pes, tabac)[1] 768.3199
| Supòsit | Què implica | Com es comprova |
|---|---|---|
| Linealitat | Relació lineal entre cada predictor i la resposta contínua | Gràfic de residus vs. ajustats |
| Normalitat dels residus | Errors distribuïts de forma gaussiana | QQ-plot i shapiro.test() |
| Homocedasticitat | Variància constant dels residus | Gràfic de residus vs. valors ajustats |
| Absència de punts influents | Cap observació té un pes excessiu sobre els coeficients | Distància de Cook (plot(model, which = 4)) |
| Absència de col·linealitat | Els predictors no estan fortament correlacionats entre ells | Factor d’inflació de la variància (car::vif()) |
par(mfrow = c(2, 2))
plot(model_mult)
par(mfrow = c(1, 1))shapiro.test(residuals(model_mult))
Shapiro-Wilk normality test
data: residuals(model_mult)
W = 0.98306, p-value = 0.2284
La col·linealitat apareix quan dos o més predictors estan fortament correlacionats entre ells, inflant els errors estàndard. La detectem amb el VIF (Variance Inflation Factor):
library(car)
vif(model_mult) edat pes fuma
1.009844 1.006812 1.003182
plot(model_mult, which = 4, col = "#D32F2F")En biomedicina és freqüent que l’efecte d’un factor depengui de la presència o nivell d’un altre (modificació de l’efecte o interacció).
La sintaxi edat * fuma avalua l’efecte principal de l’edat, l’efecte principal del tabac i el terme combinat d’interacció (edat:fuma):
model_inter <- lm(pressio ~ edat * fuma + pes, data = dades2)
summary(model_inter)
Call:
lm(formula = pressio ~ edat * fuma + pes, data = dades2)
Residuals:
Min 1Q Median 3Q Max
-33.421 -7.915 1.872 7.136 28.310
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 97.42428 9.16380 10.631 < 2e-16 ***
edat 0.38434 0.10268 3.743 0.000311 ***
fumaSí 1.07496 9.83126 0.109 0.913162
pes 0.20988 0.10083 2.081 0.040078 *
edat:fumaSí 0.04436 0.17075 0.260 0.795597
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 11 on 95 degrees of freedom
Multiple R-squared: 0.2628, Adjusted R-squared: 0.2318
F-statistic: 8.467 on 4 and 95 DF, p-value: 6.917e-06
ggplot(dades2, aes(x = edat, y = pressio, color = fuma)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE) +
scale_color_manual(values = c("No" = "#1976D2", "Sí" = "#D32F2F")) +
labs(title = "Interacció entre edat i consum de tabac sobre la pressió",
x = "Edat (anys)",
y = "Pressió arterial sistòlica (mmHg)",
color = "Fuma?") +
theme_classic()L’objectiu és trobar un model parsimoniós: el menor nombre de predictors capaços de retenir la màxima informació clínica.
model_tot <- lm(pressio ~ edat + pes + fuma + edat:fuma, data = dades2)
model_base <- lm(pressio ~ 1, data = dades2)
model_step <- step(
model_base,
scope = list(lower = model_base, upper = model_tot),
direction = "both",
trace = FALSE
)
summary(model_step)
Call:
lm(formula = pressio ~ edat + pes + fuma, data = dades2)
Residuals:
Min 1Q Median 3Q Max
-33.278 -7.769 1.992 7.352 28.922
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 96.33731 8.11307 11.874 < 2e-16 ***
edat 0.40038 0.08162 4.905 3.8e-06 ***
pes 0.21278 0.09973 2.134 0.0354 *
fumaSí 3.56173 2.22931 1.598 0.1134
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 10.95 on 96 degrees of freedom
Multiple R-squared: 0.2623, Adjusted R-squared: 0.2392
F-statistic: 11.38 on 3 and 96 DF, p-value: 1.89e-06
Criteri clínic: Els algorismes automàtics de selecció mai han de substituir el raonament fisiopatològic o la literatura prèvia. Un factor amb valor pronòstic reconegut o un confusor conegut s’ha de mantenir al model encara que el seu valor p no assoleixi la significació estadística a la mostra.
set.seed(123)
n_obs <- nrow(dades2)
idx <- sample(1:n_obs, size = 0.7 * n_obs)
training <- dades2[idx, ]
test <- dades2[-idx, ]
model_train <- lm(pressio ~ edat + pes + fuma, data = training)
pred_test <- predict(model_train, newdata = test)
# Càlcul de l'error quadràtic mitjà (RMSE)
rmse <- sqrt(mean((test$pressio - pred_test)^2))
rmse[1] 9.425799
library(boot)
model_glm <- glm(pressio ~ edat + pes + fuma, data = dades2)
cv_err <- cv.glm(dades2, model_glm, K = 10)
# Error estimat de predicció generalitzable (MSE)
cv_err$delta[2][1] 122.5808
boot_fn <- function(data, indices) {
coef(lm(pressio ~ edat + pes + fuma, data = data[indices, ]))
}
set.seed(123)
resultats_boot <- boot(data = dades2, statistic = boot_fn, R = 1000)
# Interval de confiança robust pel coeficient d'edat (índex 2)
boot.ci(resultats_boot, type = "bca", index = 2)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 1000 bootstrap replicates
CALL :
boot.ci(boot.out = resultats_boot, type = "bca", index = 2)
Intervals :
Level BCa
95% ( 0.2413, 0.5664 )
Calculations and Intervals on Original Scale
L’anàlisi de la variància (ANOVA) s’utilitza habitualment per comparar la mitjana d’una variable quantitativa contínua entre tres o més grups independents.
És exactament un cas particular del model lineal general (\(lm\)), on la variable predictora és un factor categòric:
\[Y = \beta_0 + \beta_1 X_{\text{grup 2}} + \beta_2 X_{\text{grup 3}} + \dots + \varepsilon\]
Analitzarem si existien diferències significatives en l’edat mitjana segons la classe de passatge (Primera, Segona o Tercera classe) a la cohort del Titanic.
titanic <- read_csv("titanic.csv")
dades_titanic <- titanic %>%
select(PassengerId, Survived, Pclass, Sex, Age) %>%
filter(!is.na(Age)) %>%
mutate(
Pclass = factor(Pclass, levels = c(1, 2, 3), labels = c("Primera", "Segona", "Tercera")),
Sex = factor(Sex, levels = c("female", "male"), labels = c("Dona", "Home")),
Survived = factor(Survived, levels = c(0, 1), labels = c("Mort", "Viu"))
)ggplot(dades_titanic, aes(x = Pclass, y = Age, fill = Pclass)) +
geom_boxplot(alpha = 0.7, outlier.alpha = 0.4) +
stat_summary(fun = mean, geom = "point", shape = 18, size = 3.5, color = "darkred") +
scale_fill_brewer(palette = "Blues") +
labs(title = "Distribució de l'edat segons la classe de passatge",
subtitle = "Els rombes vermells indiquen la mitjana de cada grup",
x = "Classe de passatge",
y = "Edat (anys)") +
theme_classic() +
theme(legend.position = "none")model_anova <- lm(Age ~ Pclass, data = dades_titanic)
anova(model_anova)| Supòsit | Què implica | Com es comprova |
|---|---|---|
| Linealitat | Garantida directament en ser un predictor categòric | — |
| Independència | Observacions no aparellades | Disseny de l’estudi |
| Normalitat dels residus | Els errors al voltant de les mitjanes segueixen una distribució normal | Gràfic Q-Q i shapiro.test() |
| Homocedasticitat | Variància de l’edat homogènia entre els tres grups | Residus vs. ajustats i car::leveneTest() |
shapiro.test(residuals(model_anova))
Shapiro-Wilk normality test
data: residuals(model_anova)
W = 0.99146, p-value = 0.0003856
plot(model_anova, which = 2)Criteri clínic davant mostres grans (\(n > 50\)):
El test de Shapiro-Wilk resulta estadísticament significatiu (\(p < 0.001\)) a causa de l’elevada potència de la mostra (\(n = 714\)). Com que al gràfic Q-Q la immensa majoria d’observacions s’alinea fidelment sobre la diagonal teòrica, el model és prou robust i es pot continuar amb la interpretació paramètrica indicant aquesta lleu desviació.
library(car)
leveneTest(Age ~ Pclass, data = dades_titanic)kruskal.test(Age ~ Pclass, data = dades_titanic)
Kruskal-Wallis rank sum test
data: Age by Pclass
Kruskal-Wallis chi-squared = 95.995, df = 2, p-value < 2.2e-16
summary)summary(model_anova)
Call:
lm(formula = Age ~ Pclass, data = dades_titanic)
Residuals:
Min 1Q Median 3Q Max
-37.313 -7.878 -0.878 7.859 48.859
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 38.2334 0.9897 38.633 < 2e-16 ***
PclassSegona -8.3558 1.4257 -5.861 7.04e-09 ***
PclassTercera -13.0928 1.2217 -10.717 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 13.5 on 711 degrees of freedom
Multiple R-squared: 0.1391, Adjusted R-squared: 0.1367
F-statistic: 57.44 on 2 and 711 DF, p-value: < 2.2e-16
PclassSegona (\(\beta_1 = -8.36\) anys; \(p < 0.001\)): Els passatgers de Segona classe tenen, de mitjana, 8.36 anys menys que els de Primera classe.PclassTercera (\(\beta_2 = -13.09\) anys; \(p < 0.001\)): Els passatgers de Tercera classe tenen, de mitjana, 13.09 anys menys que els de Primera classe.TukeyHSD(aov(model_anova)) Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = model_anova)
$Pclass
diff lwr upr p adj
Segona-Primera -8.355811 -11.704133 -5.007489 0.0000000
Tercera-Primera -13.092821 -15.962198 -10.223445 0.0000000
Tercera-Segona -4.737010 -7.676279 -1.797742 0.0004884
library(FSA)
dunnTest(Age ~ Pclass, data = dades_titanic, method = "holm") Comparison Z P.unadj P.adj
1 Primera - Segona 4.798551 1.598180e-06 3.196359e-06
2 Primera - Tercera 9.762001 1.638887e-22 4.916662e-22
3 Segona - Tercera 4.063515 4.833916e-05 4.833916e-05
Quan la variable dependent clínica és binària o dicotòmica (viu/mort, recidiva sí/no, èxit terapèutic sí/no), la regressió lineal clàssica no és aplicable.
La regressió logística transforma la probabilitat d’un esdeveniment mitjançant la funció logit, assegurant que les prediccions es mantinguin sempre estrictament dins l’interval \([0, 1]\).
| Model | Variable resposta (\(Y\)) | Resultat estimat (\(\hat{Y}\)) | Aplicació clínica típica |
|---|---|---|---|
| Regressió lineal | Quantitativa contínua | Valor mitjà | Pressió arterial sistòlica, colesterol LDL |
| Regressió logística | Binària (0/1) | Probabilitat (0 a 1) | Supervivència a 30 dies, risc d’ingrés a UCI |
\[\text{logit}(p) = \ln\left(\frac{p}{1 - p}\right) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \dots + \beta_k X_k\]
curva_logistica <- tibble(
x = seq(-6, 6, length.out = 200),
p = 1 / (1 + exp(-x))
)
ggplot(curva_logistica, aes(x = x, y = p)) +
geom_line(color = "#1976D2", linewidth = 1.5) +
geom_hline(yintercept = c(0, 1), linetype = "dotted", color = "gray50") +
labs(title = "Funció sigmoide de la regressió logística",
subtitle = "La probabilitat queda confinada asimptòticament entre 0 i 1",
x = "Combinació lineal de predictors (logit)",
y = "Probabilitat de l'esdeveniment (p)") +
theme_classic()glm()model_log <- glm(Survived ~ Sex + Pclass + Age,
data = dades_titanic,
family = binomial(link = "logit"))
summary(model_log)
Call:
glm(formula = Survived ~ Sex + Pclass + Age, family = binomial(link = "logit"),
data = dades_titanic)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 3.777013 0.401123 9.416 < 2e-16 ***
SexHome -2.522781 0.207391 -12.164 < 2e-16 ***
PclassSegona -1.309799 0.278066 -4.710 2.47e-06 ***
PclassTercera -2.580625 0.281442 -9.169 < 2e-16 ***
Age -0.036985 0.007656 -4.831 1.36e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 964.52 on 713 degrees of freedom
Residual deviance: 647.28 on 709 degrees of freedom
AIC: 657.28
Number of Fisher Scoring iterations: 5
\[\text{OR} = e^{\beta}\]
round(exp(coef(model_log)), 3) (Intercept) SexHome PclassSegona PclassTercera Age
43.685 0.080 0.270 0.076 0.964
round(exp(confint(model_log)), 3) 2.5 % 97.5 %
(Intercept) 20.379 98.386
SexHome 0.053 0.119
PclassSegona 0.155 0.462
PclassTercera 0.043 0.130
Age 0.949 0.978
SexHome (\(\text{OR} = 0.080\), IC 95%: \(0.053 - 0.119\); \(p < 0.001\)): Ajustant per classe i edat, els homes presenten una probabilitat de sobreviure un 92% inferior a la de les dones (\(\text{OR} = 0.08\)).PclassSegona (\(\text{OR} = 0.270\), IC 95%: \(0.155 - 0.462\); \(p < 0.001\)): Viatjar a Segona classe s’associa a una reducció del 73% en els odds de supervivència respecte a la Primera classe de referència.PclassTercera (\(\text{OR} = 0.076\), IC 95%: \(0.043 - 0.130\); \(p < 0.001\)): Viatjar a Tercera classe redueix els odds de sobreviure un 92.4% en comparació amb Primera classe.Age (\(\text{OR} = 0.964\), IC 95%: \(0.949 - 0.978\); \(p < 0.001\)): Per cada any addicional d’edat, els odds de supervivència disminueixen aproximadament un 3.6% (\(\text{OR} = 0.964\)).dades_titanic$prob_pred <- predict(model_log, type = "response")
dades_titanic <- dades_titanic %>%
mutate(logit = log(prob_pred / (1 - prob_pred)))
ggplot(dades_titanic, aes(x = Age, y = logit)) +
geom_point(alpha = 0.25, color = "gray30") +
geom_smooth(method = "loess", se = FALSE, color = "#D32F2F", linewidth = 1.2) +
labs(title = "Avaluació de la linealitat del logit respecte a l'edat",
x = "Edat (anys)",
y = "Logit predit") +
theme_classic()library(car)
vif(model_log) GVIF Df GVIF^(1/(2*Df))
Sex 1.072870 1 1.035794
Pclass 1.416574 2 1.090962
Age 1.333112 1 1.154605
library(pROC)
roc_obj <- roc(dades_titanic$Survived, dades_titanic$prob_pred, levels = c("Mort", "Viu"))
plot(roc_obj,
col = "#1976D2",
lwd = 2.5,
main = "Corba ROC: Model predictiu de supervivència",
xlab = "1 - Especificitat",
ylab = "Sensibilitat")
abline(a = 0, b = 1, lty = 2, col = "gray60")
auc(roc_obj)Area under the curve: 0.8523
ci.auc(roc_obj)95% CI: 0.8219-0.8827 (DeLong)
gtsummarymodel_log %>%
tbl_regression(
exponentiate = TRUE,
label = list(
Sex ~ "Sexe",
Pclass ~ "Classe de passatge",
Age ~ "Edat (anys)"
)
) %>%
bold_p(t = 0.05) %>%
bold_labels()| Characteristic | OR | 95% CI | p-value |
|---|---|---|---|
| Sexe | |||
| Dona | — | — | |
| Home | 0.08 | 0.05, 0.12 | <0.001 |
| Classe de passatge | |||
| Primera | — | — | |
| Segona | 0.27 | 0.16, 0.46 | <0.001 |
| Tercera | 0.08 | 0.04, 0.13 | <0.001 |
| Edat (anys) | 0.96 | 0.95, 0.98 | <0.001 |
| Abbreviations: CI = Confidence Interval, OR = Odds Ratio | |||
| Model | Funció a R | Resposta (\(Y\)) | Coeficients clau | Supòsits essencials |
|---|---|---|---|---|
| Regressió lineal simple | lm(y ~ x) |
Quantitativa contínua | Pendent (\(\beta_1\), canvi en unitats de \(Y\)) | Linealitat, normalitat residus, homocedasticitat |
| Regressió lineal múltiple | lm(y ~ x1 + x2) |
Quantitativa contínua | Efecte ajustat per confusors | Supòsits lineals + absència de col·linealitat (VIF) |
| ANOVA d’un factor | lm(y ~ factor) |
Quantitativa contínua | Diferència de mitjanes vs. grup de referència | Normalitat dels residus, homocedasticitat |
| Regressió logística | glm(..., family = binomial) |
Categòrica binària (0/1) | Odds Ratios (\(\text{OR} = e^\beta\)) | Linealitat del logit, independència, absència de col·linealitat |
iris, ajusta un model lineal per predir la longitud del pètal (Petal.Length) a partir de la seva amplada (Petal.Width). Interpreta el pendent obtingut, el contrast d’hipòtesi i el coeficient \(R^2\).plot(model)). Avalua visualment la linealitat, l’homocedasticitat i comprova formalment la normalitat dels residus amb shapiro.test().Petal.Length a partir de Petal.Width i Sepal.Length. Avalua si tots dos predictors aporten informació independent i comprova que no hi hagi col·linealitat mitjançant car::vif().Petal.Length) entre les tres espècies (Species) d’iris mitjançant lm() i anova(). Realitza les comparacions post-hoc amb TukeyHSD().data/titanic.csv), crea una variable binària per identificar si el bitllet va costar més de 30 dòlars (tarifa_alta = ifelse(Fare > 30, 1, 0)). Ajusta un model logístic amb glm() per predir tarifa_alta en funció de la classe (Pclass) i el sexe (Sex). Obtén i interpreta les Odds Ratios (OR) amb els seus intervals de confiança del 95%.pROC i calcula l’Àrea Sota la Corba (AUC) amb el seu IC 95%.gtsummary::tbl_regression() del model logístic anterior, mostrant les OR exponenciades i els noms de les variables personalitzats.library(tidyverse)
library(car)
library(pROC)
library(gtsummary)
# Exercici 1: Regressió lineal simple amb iris
model_iris_simple <- lm(Petal.Length ~ Petal.Width, data = iris)
summary(model_iris_simple)
Call:
lm(formula = Petal.Length ~ Petal.Width, data = iris)
Residuals:
Min 1Q Median 3Q Max
-1.33542 -0.30347 -0.02955 0.25776 1.39453
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.08356 0.07297 14.85 <2e-16 ***
Petal.Width 2.22994 0.05140 43.39 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.4782 on 148 degrees of freedom
Multiple R-squared: 0.9271, Adjusted R-squared: 0.9266
F-statistic: 1882 on 1 and 148 DF, p-value: < 2.2e-16
# Exercici 2: Diagnòstic de supòsits
par(mfrow = c(2, 2))
plot(model_iris_simple)par(mfrow = c(1, 1))
shapiro.test(residuals(model_iris_simple))
Shapiro-Wilk normality test
data: residuals(model_iris_simple)
W = 0.99011, p-value = 0.3753
# Exercici 3: Regressió lineal múltiple i VIF
model_iris_mult <- lm(Petal.Length ~ Petal.Width + Sepal.Length, data = iris)
summary(model_iris_mult)
Call:
lm(formula = Petal.Length ~ Petal.Width + Sepal.Length, data = iris)
Residuals:
Min 1Q Median 3Q Max
-1.15506 -0.21920 -0.02115 0.25986 1.35204
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.50714 0.33696 -4.473 1.54e-05 ***
Petal.Width 1.74810 0.07533 23.205 < 2e-16 ***
Sepal.Length 0.54226 0.06934 7.820 9.41e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.4032 on 147 degrees of freedom
Multiple R-squared: 0.9485, Adjusted R-squared: 0.9478
F-statistic: 1354 on 2 and 147 DF, p-value: < 2.2e-16
vif(model_iris_mult) Petal.Width Sepal.Length
3.021401 3.021401
# Exercici 4: ANOVA com a model lineal i Post-hoc
model_anova_iris <- lm(Petal.Length ~ Species, data = iris)
anova(model_anova_iris)TukeyHSD(aov(model_anova_iris)) Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = model_anova_iris)
$Species
diff lwr upr p adj
versicolor-setosa 2.798 2.59422 3.00178 0
virginica-setosa 4.090 3.88622 4.29378 0
virginica-versicolor 1.292 1.08822 1.49578 0
# Exercici 5: Regressió logística amb el Titanic
titanic <- read_csv("titanic.csv")
dades_ex5 <- titanic %>%
filter(!is.na(Fare), !is.na(Pclass), !is.na(Sex)) %>%
mutate(
tarifa_alta = ifelse(Fare > 30, 1, 0),
Pclass = factor(Pclass, levels = c(1, 2, 3), labels = c("Primera", "Segona", "Tercera")),
Sex = factor(Sex, levels = c("male", "female"), labels = c("Home", "Dona"))
)
model_log_ex5 <- glm(tarifa_alta ~ Pclass + Sex, data = dades_ex5, family = binomial)
round(exp(coef(model_log_ex5)), 3) (Intercept) PclassSegona PclassTercera SexDona
2.667 0.038 0.026 2.192
round(exp(confint(model_log_ex5)), 3) 2.5 % 97.5 %
(Intercept) 1.886 3.836
PclassSegona 0.022 0.066
PclassTercera 0.016 0.040
SexDona 1.448 3.338
# Exercici 6: Corba ROC i AUC
dades_ex5$probabilitat <- predict(model_log_ex5, type = "response")
roc_ex6 <- roc(dades_ex5$tarifa_alta, dades_ex5$probabilitat)
plot(roc_ex6, col = "#1976D2", lwd = 2, main = "Corba ROC - Predicció de Tarifa > 30$")auc(roc_ex6)Area under the curve: 0.8531
ci.auc(roc_ex6)95% CI: 0.8209-0.8853 (DeLong)
# Exercici 7: Taula de publicació amb gtsummary
model_log_ex5 %>%
tbl_regression(
exponentiate = TRUE,
label = list(
Pclass ~ "Classe de bitllet",
Sex ~ "Sexe"
)
) %>%
bold_p(t = 0.05) %>%
bold_labels()| Characteristic | OR | 95% CI | p-value |
|---|---|---|---|
| Classe de bitllet | |||
| Primera | — | — | |
| Segona | 0.04 | 0.02, 0.07 | <0.001 |
| Tercera | 0.03 | 0.02, 0.04 | <0.001 |
| Sexe | |||
| Home | — | — | |
| Dona | 2.19 | 1.45, 3.34 | <0.001 |
| Abbreviations: CI = Confidence Interval, OR = Odds Ratio | |||