library(tidyverse)
library(readxl)
library(janitor)
library(naniar)
library(gtsummary)
library(car)
library(pROC)
library(survival)
library(survminer)
library(epitools)Capítol 6: Exercicis Resolts i Cas Pràctic Integrador
Curs d’Estadística amb R per a Metges
Introducció al Capítol 6
Aquest capítol final recull una bateria d’exercicis resolts que repassen els conceptes i mètodes treballats al llarg del curs (Capítols 1 al 5), acompanyats d’un cas pràctic integrador final.
Per afavorir l’aprenentatge actiu, totes les solucions de codi es troben plegades per defecte. Intenta resoldre cada apartat al teu editor o consola abans de prémer el botó desplegable per comprovar la solució i les decisions metodològiques preses.
Exercicis Resolts per Blocs Temàtics
Bloc 1: Fonaments d’R, Tidyverse i Importació (Capítol 1)
Exercici 1.1 (Nivell Bàsic): Gestió de vectors de marcadors cardíacs
Enunciat: Donat el següent vector de valors de troponina I ultrasensible (ng/L) mesurats a urgències: c(14, 52, 110, 8, 250, 42, 18, 550, 31, 95): 1. Calcula la mitjana, la mediana i la desviació estàndard. 2. Identifica quins pacients superen el límit de referència de la normalitat (> 14 ng/L). 3. Quants pacients presenten troponina elevada?
Mostra la solució de l’Exercici 1.1
tropo <- c(14, 52, 110, 8, 250, 42, 18, 550, 31, 95)
# 1. Paràmetres descriptius bàsics
mean(tropo)[1] 117
Mostra la solució de l’Exercici 1.1
median(tropo)[1] 47
Mostra la solució de l’Exercici 1.1
sd(tropo)[1] 168.4069
Mostra la solució de l’Exercici 1.1
# 2. Vector lògic de positivitat
elevada <- tropo > 14
elevada [1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE
Mostra la solució de l’Exercici 1.1
# 3. Recompte de casos positius (TRUE suma 1)
sum(elevada)[1] 8
Exercici 1.2 (Nivell Intermedi): Pipeline de depuració amb dplyr
Enunciat: Amb el conjunt de dades starwars (disponible a dplyr), filtra els personatges de gènere femení o masculí amb valors vàlids d’alçada (height) i massa (mass), selecciona les columnes rellevants, crea la columna imc (\(IMC = \frac{massa}{(alçada/100)^2}\)) arrodonida a un decimal i ordena els registres de major a menor IMC.
Mostra la solució de l’Exercici 1.2
starwars %>%
filter(!is.na(height), !is.na(mass), gender %in% c("masculine", "feminine")) %>%
select(name, height, mass, species) %>%
mutate(imc = round(mass / ((height / 100)^2), 1)) %>%
arrange(desc(imc)) %>%
head(5)# A tibble: 5 × 5
name height mass species imc
<chr> <int> <dbl> <chr> <dbl>
1 Jabba Desilijic Tiure 175 1358 Hutt 443.
2 Dud Bolt 94 45 Vulptereen 50.9
3 Yoda 66 17 Yoda's species 39
4 Owen Lars 178 120 Human 37.9
5 IG-88 200 140 Droid 35
Bloc 2: Estadística Descriptiva i Exploració de Dades (Capítol 2)
Exercici 2.1 (Nivell Bàsic): Freqüències creuades i totals marginals amb janitor
Enunciat: Utilitzant la cohort docent del Titanic, genera una taula de freqüències creuades de la classe de passatge (Pclass) segons el sexe (Sex), expressant els recomptes amb els percentatges per files i afegint els totals marginals tant per fila com per columna.
Mostra la solució de l’Exercici 2.1
# Cohort docent de control per reproduir els exemples
set.seed(42)
titanic_docent <- tibble(
Sex = sample(c("female", "male"), 200, replace = TRUE, prob = c(0.55, 0.45)),
Pclass = sample(c(1, 2, 3), 200, replace = TRUE, prob = c(0.25, 0.30, 0.45)),
Survived = sample(c(0, 1), 200, replace = TRUE, prob = c(0.55, 0.45)),
Age = round(rnorm(200, mean = 30, sd = 14)),
Fare = round(rexp(200, rate = 1/35) + 5, 1)
)
# Ordre correcte: adorn_totals es calcula abans de convertir les cel·les a text
titanic_docent %>%
tabyl(Sex, Pclass) %>%
adorn_totals(c("row", "col")) %>%
adorn_percentages("row") %>%
adorn_pct_formatting(digits = 1) %>%
adorn_ns(position = "front") Sex 1 2 3 Total
female 23 (23.2%) 26 (26.3%) 50 (50.5%) 99 (100.0%)
male 24 (23.8%) 28 (27.7%) 49 (48.5%) 101 (100.0%)
Total 47 (23.5%) 54 (27.0%) 99 (49.5%) 200 (100.0%)
Exercici 2.2 (Nivell Intermedi): Auditoria de missings i resum robust
Enunciat: Audita els valors absents de la base de dades clínica lung (paquet survival) amb naniar identificant les variables amb més d’un 5% de pèrdua, i resumeix la tarifa (Fare) de la cohort docent mitjançant mediana i quartils segons la classe de cabina.
Mostra la solució de l’Exercici 2.2
data(lung, package = "survival")
# 1. Auditoria de dades absents
lung %>%
miss_var_summary() %>%
filter(pct_miss > 5)# A tibble: 2 × 3
variable n_miss pct_miss
<chr> <int> <num>
1 meal.cal 47 20.6
2 wt.loss 14 6.14
Mostra la solució de l’Exercici 2.2
# 2. Resum robust de tarifa per asimetria
titanic_docent %>%
group_by(Pclass) %>%
summarise(
n = n(),
mediana_tarifa = median(Fare, na.rm = TRUE),
q25 = quantile(Fare, 0.25, na.rm = TRUE),
q75 = quantile(Fare, 0.75, na.rm = TRUE),
.groups = "drop"
)# A tibble: 3 × 5
Pclass n mediana_tarifa q25 q75
<dbl> <int> <dbl> <dbl> <dbl>
1 1 47 31.7 14.5 56.3
2 2 54 29.9 16.9 56.1
3 3 99 28.3 16.0 44.7
Exercici 2.3 (Nivell Avançat): Taula 1 automatitzada amb gtsummary
Enunciat: Genera una taula basal publicable de la cohort docent estratificada per supervivència (Survived), mostrant mitjana (DE) per a variables simètriques, mediana [RIC] per a variables asimètriques, n (%) per a factors, i p-valors bivariats.
Mostra la solució de l’Exercici 2.3
titanic_docent %>%
select(Survived, Age, Sex, Pclass, Fare) %>%
tbl_summary(
by = Survived,
statistic = list(
Age ~ "{mean} ({sd})",
Fare ~ "{median} ({p25}, {p75})",
all_categorical() ~ "{n} ({p}%)"
),
label = list(
Age ~ "Edat (anys)",
Sex ~ "Sexe",
Pclass ~ "Classe de bitllet",
Fare ~ "Tarifa ($)"
),
missing = "no"
) %>%
add_overall() %>%
add_p() %>%
bold_labels()| Characteristic | Overall N = 2001 |
0 N = 1231 |
1 N = 771 |
p-value2 |
|---|---|---|---|---|
| Edat (anys) | 29 (13) | 29 (13) | 30 (14) | 0.5 |
| Sexe | 0.3 | |||
| female | 99 (50%) | 57 (46%) | 42 (55%) | |
| male | 101 (51%) | 66 (54%) | 35 (45%) | |
| Classe de bitllet | 0.8 | |||
| 1 | 47 (24%) | 29 (24%) | 18 (23%) | |
| 2 | 54 (27%) | 35 (28%) | 19 (25%) | |
| 3 | 99 (50%) | 59 (48%) | 40 (52%) | |
| Tarifa ($) | 29 (16, 51) | 30 (18, 55) | 25 (14, 46) | 0.11 |
| 1 Mean (SD); n (%); Median (Q1, Q3) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Bloc 3: Inferència i Contrast d’Hipòtesis (Capítol 3)
Exercici 3.1 (Nivell Bàsic): Taula 2x2, Chi-quadrat i Odds Ratio
Enunciat: Comprova si la diferència de proporcions de supervivència segons el sexe és estadísticament significativa mitjançant el test de Chi-quadrat i calcula l’Odds Ratio amb el seu interval de confiança del 95%.
Mostra la solució de l’Exercici 3.1
taula_2x2 <- table(titanic_docent$Sex, titanic_docent$Survived)
taula_2x2
0 1
female 57 42
male 66 35
Mostra la solució de l’Exercici 3.1
# Contrast d'independència de Pearson
chisq.test(taula_2x2)
Pearson's Chi-squared test with Yates' continuity correction
data: taula_2x2
X-squared = 0.96795, df = 1, p-value = 0.3252
Mostra la solució de l’Exercici 3.1
# Odds Ratio i interval de confiança del 95%
oddsratio(taula_2x2)$measure odds ratio with 95% C.I.
estimate lower upper
female 1.000000 NA NA
male 0.721339 0.4048352 1.278793
Exercici 3.2 (Nivell Intermedi): Contrast de dues mitjanes i comprovació de supòsits
Enunciat: Avalua si existeixen diferències d’edat segons el sexe comprovant prèviament la normalitat per grups (Shapiro-Wilk) i la igualtat de variàncies (Bartlett) per justificar la tria del contrast.
Mostra la solució de l’Exercici 3.2
# 1. Comprovació de normalitat per subgrups
shapiro.test(titanic_docent$Age[titanic_docent$Sex == "male"])
Shapiro-Wilk normality test
data: titanic_docent$Age[titanic_docent$Sex == "male"]
W = 0.9826, p-value = 0.2053
Mostra la solució de l’Exercici 3.2
shapiro.test(titanic_docent$Age[titanic_docent$Sex == "female"])
Shapiro-Wilk normality test
data: titanic_docent$Age[titanic_docent$Sex == "female"]
W = 0.99341, p-value = 0.9143
Mostra la solució de l’Exercici 3.2
# 2. Homocedasticitat
bartlett.test(Age ~ Sex, data = titanic_docent)
Bartlett test of homogeneity of variances
data: Age by Sex
Bartlett's K-squared = 2.6193, df = 1, p-value = 0.1056
Mostra la solució de l’Exercici 3.2
# 3. Test de t de Welch (ajust per variàncies heterogènies per defecte)
t.test(Age ~ Sex, data = titanic_docent)
Welch Two Sample t-test
data: Age by Sex
t = 0.71393, df = 191.67, p-value = 0.4761
alternative hypothesis: true difference in means between group female and group male is not equal to 0
95 percent confidence interval:
-2.376093 5.071963
sample estimates:
mean in group female mean in group male
30.07071 28.72277
Bloc 4: Models de Regressió Lineal i Logística (Capítol 4)
Exercici 4.1 (Nivell Intermedi): Regressió lineal múltiple i col·linealitat
Enunciat: Ajusta un model de regressió lineal múltiple per estimar la pressió sistòlica en funció de l’edat, el pes i el consum de tabac. Avalua la presència de multicol·linealitat mitjançant el VIF.
Mostra la solució de l’Exercici 4.1
# Dades sintètiques del Capítol 4
set.seed(123)
n_reg <- 100
edat_r <- sample(30:80, n_reg, replace = TRUE)
pes_r <- rnorm(n_reg, mean = 70, sd = 12)
fuma_r <- factor(sample(c(0, 1), n_reg, replace = TRUE), labels = c("No", "Sí"))
pressio_r <- 90 + 0.4 * edat_r + 0.3 * pes_r + 5 * (fuma_r == "Sí") + rnorm(n_reg, 0, 10)
dades_reg <- data.frame(pressio = pressio_r, edat = edat_r, pes = pes_r, fuma = fuma_r)
# Ajust del model lineal
mod_lin <- lm(pressio ~ edat + pes + fuma, data = dades_reg)
summary(mod_lin)
Call:
lm(formula = pressio ~ edat + pes + fuma, data = dades_reg)
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
Mostra la solució de l’Exercici 4.1
# Factor d'inflació de la variància (VIF < 5 descarta col·linealitat)
vif(mod_lin) edat pes fuma
1.009844 1.006812 1.003182
Exercici 4.2 (Nivell Avançat): Regressió logística multivariable i corba ROC
Enunciat: Modela la probabilitat de sobreviure al Titanic en funció del sexe, la classe i l’edat. Presenta els coeficients com a Odds Ratios exponenciades amb el seu IC 95% i avalua la capacitat predictiva amb l’àrea sota la corba ROC (AUC).
Mostra la solució de l’Exercici 4.2
mod_log <- glm(Survived ~ Sex + Pclass + Age, data = titanic_docent, family = binomial)
# Odds Ratios i intervals de confiança
exp(cbind(OR = coef(mod_log), confint(mod_log))) OR 2.5 % 97.5 %
(Intercept) 0.4955786 0.1447972 1.645785
Sexmale 0.7275627 0.4087511 1.289145
Pclass 1.0849235 0.7592859 1.559873
Age 1.0070255 0.9852214 1.029509
Mostra la solució de l’Exercici 4.2
# Predicció de probabilitats i corba ROC
titanic_docent$prob_pred <- predict(mod_log, type = "response")
roc_obj <- roc(titanic_docent$Survived, titanic_docent$prob_pred)
plot(roc_obj, col = "#1565C0", lwd = 2, main = "Corba ROC - Model Logístic")Mostra la solució de l’Exercici 4.2
auc(roc_obj)Area under the curve: 0.5527
Bloc 5: Anàlisi de Supervivència (Capítol 5)
Exercici 5.1 (Nivell Intermedi): Kaplan-Meier i contrast de Log-Rank
Enunciat: Amb les dades de càncer de pulmó (lung), codifica l’objecte Surv(), calcula la mediana de supervivència global i compara les corbes segons el sexe amb el test de log-rank.
Mostra la solució de l’Exercici 5.1
lung_prep <- lung %>%
mutate(
status_ev = ifelse(status == 2, 1, 0),
sexe = factor(sex, levels = c(1, 2), labels = c("Home", "Dona"))
)
surv_pulmo <- Surv(time = lung_prep$time, event = lung_prep$status_ev)
# Mediana global
km_global <- survfit(surv_pulmo ~ 1, data = lung_prep)
summary(km_global)$table["median"]median
310
Mostra la solució de l’Exercici 5.1
# Contrast de Log-Rank
survdiff(surv_pulmo ~ sexe, data = lung_prep)Call:
survdiff(formula = surv_pulmo ~ sexe, data = lung_prep)
N Observed Expected (O-E)^2/E (O-E)^2/V
sexe=Home 138 112 91.6 4.55 10.3
sexe=Dona 90 53 73.4 5.68 10.3
Chisq= 10.3 on 1 degrees of freedom, p= 0.001
Exercici 5.2 (Nivell Avançat): Model de Cox i riscos proporcionals
Enunciat: Ajusta un model de riscos proporcionals de Cox per estimar l’Hazard Ratio associat al sexe i a l’edat. Comprova formalment el supòsit de riscos proporcionals mitjançant els residus de Schoenfeld.
Mostra la solució de l’Exercici 5.2
mod_cox <- coxph(surv_pulmo ~ sexe + age, data = lung_prep)
summary(mod_cox)Call:
coxph(formula = surv_pulmo ~ sexe + age, data = lung_prep)
n= 228, number of events= 165
coef exp(coef) se(coef) z Pr(>|z|)
sexeDona -0.513219 0.598566 0.167458 -3.065 0.00218 **
age 0.017045 1.017191 0.009223 1.848 0.06459 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef) exp(-coef) lower .95 upper .95
sexeDona 0.5986 1.6707 0.4311 0.8311
age 1.0172 0.9831 0.9990 1.0357
Concordance= 0.603 (se = 0.025 )
Likelihood ratio test= 14.12 on 2 df, p=9e-04
Wald test = 13.47 on 2 df, p=0.001
Score (logrank) test = 13.72 on 2 df, p=0.001
Mostra la solució de l’Exercici 5.2
# Test de residus de Schoenfeld
zph_test <- cox.zph(mod_cox)
print(zph_test) chisq df p
sexe 2.608 1 0.11
age 0.209 1 0.65
GLOBAL 2.771 2 0.25
Mostra la solució de l’Exercici 5.2
# Diagnòstic visual
par(mfrow = c(1, 2))
plot(zph_test)Mostra la solució de l’Exercici 5.2
par(mfrow = c(1, 1))Cas Pràctic Integrador: Cohort de Risc Cardiovascular
Escenari Clínic
Es disposa d’un registre prospectiu hospitalari de seguiment cardiovascular (\(N = 1.200\)). Les dades en brut recollides presenten anomalies reals: valors biològicament implausibles, codis numèrics residuals d’error de picatge, cadenes de text no normalitzades i valors absents (missing data).
Preguntes Clau a Respondre:
- Auditoria i Neteja: Quina taxa de dades absents tenim i com esmenem els registres anòmals?
- Característiques Basals: Quines diferències clíniques presenten els pacients que pateixen un esdeveniment coronari respecte als que no (Taula 1)?
- Inferència Bivariada: Difereix la pressió arterial sistòlica segons el diagnòstic de diabetis un cop validats els supòsits?
- Modelització de Risc: Quin és l’efecte independent dels factors clàssics sobre l’esdeveniment coronari i quina AUC assoleix el model?
- Supervivència: Quina és la supervivència lliure d’esdeveniments a llarg termini i es manté l’assumpció de riscos proporcionals de Cox?
Pas 1: Generació de la Cohort Clínica Bruta
Aquest bloc reprodueix la base de dades amb les incidències habituals de recollida de dades.
Codi de simulació de la cohort bruta
set.seed(2026)
n_cohort <- 1200
raw_data <- tibble(
`ID PACIENT` = 1001:(1000 + n_cohort),
`Edat_Anys` = round(rnorm(n_cohort, mean = 55, sd = 10)),
`SEX` = sample(c(1, 2, NA), n_cohort, replace = TRUE, prob = c(0.48, 0.49, 0.03)),
`TAS mmHg` = round(rnorm(n_cohort, mean = 132, sd = 18)),
`TAD mmHg` = round(rnorm(n_cohort, mean = 82, sd = 10)),
`Colesterol mg/dL` = round(rnorm(n_cohort, mean = 215, sd = 42)),
`Fuma` = sample(c(0, 1, 99), n_cohort, replace = TRUE, prob = c(0.55, 0.40, 0.05)),
`Diabetis` = sample(c("No", "Si", "SI", "si", NA), n_cohort, replace = TRUE, prob = c(0.78, 0.08, 0.05, 0.05, 0.04)),
`Seguiment_dies` = round(runif(n_cohort, min = 30, max = 7300)),
`Exitus_CV` = sample(c(0, 1), n_cohort, replace = TRUE, prob = c(0.75, 0.25))
)
# Inserció deliberada d'inconsistències de registre
raw_data$`Edat_Anys`[c(15, 88)] <- c(185, -5)
raw_data$`TAS mmHg`[c(42, 102)] <- c(950, 12)
raw_data$`Colesterol mg/dL`[raw_data$`Colesterol mg/dL` > 320] <- NAPas 2: Pregunta 1 - Neteja, Estandardització i Auditoria de Missings
Filtra els valors fisiològicament impossibles, normalitza la variable de diabetis, recodifica els codis residuals de tabac i quantifica els patrons de pèrdua amb naniar.
Mostra la solució del Pas 1: Neteja i auditoria
cohort_clean <- raw_data %>%
clean_names() %>%
mutate(
# Filtre de límits fisiològics raonables
edat_anys = ifelse(edat_anys < 18 | edat_anys > 110, NA, edat_anys),
tas_mm_hg = ifelse(tas_mm_hg < 60 | tas_mm_hg > 300, NA, tas_mm_hg),
tad_mm_hg = ifelse(tad_mm_hg < 30 | tad_mm_hg > 150, NA, tad_mm_hg),
# 99 corresponia a registre desconegut
fuma = ifelse(fuma == 99, NA, fuma),
fuma = factor(fuma, levels = c(0, 1), labels = c("No", "Sí")),
# Estandardització de cadenes de text
diabetis = str_to_lower(str_trim(diabetis)),
diabetis = case_when(
diabetis %in% c("si", "sí") ~ "Sí",
diabetis == "no" ~ "No",
TRUE ~ NA_character_
),
diabetis = factor(diabetis, levels = c("No", "Sí")),
# Factorització del sexe
sex = factor(sex, levels = c(1, 2), labels = c("Home", "Dona")),
# Conversió a anys per facilitar la interpretació temporal
seguiment_anys = round(seguiment_dies / 365.25, 2)
) %>%
rename(
edat = edat_anys,
sexe = sex,
tas = tas_mm_hg,
tad = tad_mm_hg,
colesterol = colesterol_mg_d_l
)
# Resum d'auditoria de valors absents
miss_var_summary(cohort_clean)# A tibble: 11 × 3
variable n_miss pct_miss
<chr> <int> <num>
1 fuma 78 6.5
2 diabetis 45 3.75
3 sexe 27 2.25
4 colesterol 10 0.833
5 edat 2 0.167
6 tas 2 0.167
7 id_pacient 0 0
8 tad 0 0
9 seguiment_dies 0 0
10 exitus_cv 0 0
11 seguiment_anys 0 0
Mostra la solució del Pas 1: Neteja i auditoria
# Selecció de casos complets per a la modelització inferencial
df_estudi <- cohort_clean %>%
filter(!is.na(edat), !is.na(sexe), !is.na(tas), !is.na(colesterol), !is.na(fuma), !is.na(diabetis))Pas 3: Pregunta 2 - Característiques Basals de la Cohort (Taula 1)
Construeix la Taula 1 estratificant per l’esdeveniment cardiovascular (exitus_cv) i seleccionant l’estadístic més adequat per a cada tipus de variable.
Mostra la solució del Pas 2: Taula 1
df_estudi %>%
select(exitus_cv, edat, sexe, tas, tad, colesterol, fuma, diabetis, seguiment_anys) %>%
tbl_summary(
by = exitus_cv,
statistic = list(
all_continuous() ~ "{mean} ({sd})",
colesterol ~ "{median} ({p25}, {p75})",
all_categorical() ~ "{n} ({p}%)"
),
label = list(
edat ~ "Edat (anys)",
sexe ~ "Sexe",
tas ~ "TAS (mmHg)",
tad ~ "TAD (mmHg)",
colesterol ~ "Colesterol Total (mg/dL)",
fuma ~ "Tabaquisme actiu",
diabetis ~ "Diabetis Mellitus",
seguiment_anys ~ "Seguiment (anys)"
),
digits = all_continuous() ~ 1,
missing = "no"
) %>%
add_overall(col_label = "**Global** (N = {N})") %>%
add_p(test = list(all_continuous() ~ "t.test", colesterol ~ "wilcox.test")) %>%
bold_labels() %>%
modify_spanning_header(c("stat_1", "stat_2") ~ "**Esdeveniment Cardiovascular**")| Characteristic | Global (N = 1038)1 |
Esdeveniment Cardiovascular
|
p-value2 | |
|---|---|---|---|---|
| 0 N = 7561 |
1 N = 2821 |
|||
| Edat (anys) | 55.3 (10.0) | 55.4 (10.1) | 54.9 (9.6) | 0.5 |
| Sexe | 0.3 | |||
| Home | 529 (51%) | 378 (50%) | 151 (54%) | |
| Dona | 509 (49%) | 378 (50%) | 131 (46%) | |
| TAS (mmHg) | 131.5 (17.4) | 130.8 (17.2) | 133.1 (17.6) | 0.063 |
| TAD (mmHg) | 81.9 (9.8) | 82.1 (9.9) | 81.5 (9.7) | 0.4 |
| Colesterol Total (mg/dL) | 215.0 (187.0, 241.0) | 215.0 (187.0, 239.0) | 215.5 (188.0, 247.0) | 0.5 |
| Tabaquisme actiu | 0.089 | |||
| No | 611 (59%) | 457 (60%) | 154 (55%) | |
| Sí | 427 (41%) | 299 (40%) | 128 (45%) | |
| Diabetis Mellitus | 0.3 | |||
| No | 830 (80%) | 610 (81%) | 220 (78%) | |
| Sí | 208 (20%) | 146 (19%) | 62 (22%) | |
| Seguiment (anys) | 10.0 (5.8) | 9.9 (5.8) | 10.2 (5.8) | 0.4 |
| 1 Mean (SD); n (%); Median (Q1, Q3) | ||||
| 2 Welch Two Sample t-test; Pearson’s Chi-squared test; Wilcoxon rank sum test | ||||
Pas 4: Pregunta 3 - Inferència Bivariada sobre la Pressió Arterial Sistòlica
Avalua si la TAS difereix segons el diagnòstic de diabetis verificant prèviament la normalitat visual (Q-Q plot) i l’homocedasticitat (test de Levene).
Mostra la solució del Pas 3: Inferència bivariada
# 1. Comprovació visual de normalitat per grup
par(mfrow = c(1, 2))
qqPlot(tas ~ diabetis, data = df_estudi)Mostra la solució del Pas 3: Inferència bivariada
par(mfrow = c(1, 1))
# 2. Test d'homocedasticitat
leveneTest(tas ~ diabetis, data = df_estudi)Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 1 0.0169 0.8966
1036
Mostra la solució del Pas 3: Inferència bivariada
# 3. Contrast de dues mitjanes independents (t-test de Welch)
t.test(tas ~ diabetis, data = df_estudi)
Welch Two Sample t-test
data: tas by diabetis
t = 0.29121, df = 306.63, p-value = 0.7711
alternative hypothesis: true difference in means between group No and group Sí is not equal to 0
95 percent confidence interval:
-2.332419 3.142683
sample estimates:
mean in group No mean in group Sí
131.5349 131.1298
Pas 5: Pregunta 4 - Modelització Multivariable i Discriminació (AUC)
Ajusta una regressió logística binària multivariable sobre l’esdeveniment cardiovascular, avalua el VIF i genera la corba ROC amb el càlcul de l’AUC.
Mostra la solució del Pas 4: Model logístic i corba ROC
# 1. Ajust del model logístic
mod_cv <- glm(
exitus_cv ~ edat + sexe + tas + colesterol + fuma + diabetis,
data = df_estudi,
family = binomial(link = "logit")
)
tbl_regression(mod_cv, exponentiate = TRUE) %>%
bold_labels() %>%
bold_p(t = 0.05)| Characteristic | OR | 95% CI | p-value |
|---|---|---|---|
| edat | 1.00 | 0.98, 1.01 | 0.5 |
| sexe | |||
| Home | — | — | |
| Dona | 0.86 | 0.65, 1.13 | 0.3 |
| tas | 1.01 | 1.00, 1.02 | 0.064 |
| colesterol | 1.00 | 1.00, 1.00 | 0.5 |
| fuma | |||
| No | — | — | |
| Sí | 1.26 | 0.96, 1.66 | 0.10 |
| diabetis | |||
| No | — | — | |
| Sí | 1.18 | 0.84, 1.65 | 0.3 |
| Abbreviations: CI = Confidence Interval, OR = Odds Ratio | |||
Mostra la solució del Pas 4: Model logístic i corba ROC
# 2. Diagnòstic de multicol·linealitat
vif(mod_cv) edat sexe tas colesterol fuma diabetis
1.000729 1.003019 1.001830 1.002316 1.000459 1.002016
Mostra la solució del Pas 4: Model logístic i corba ROC
# 3. Corba ROC i àrea sota la corba
df_estudi$prob_pred_cv <- predict(mod_cv, type = "response")
roc_cv <- roc(df_estudi$exitus_cv, df_estudi$prob_pred_cv)
plot(
roc_cv,
col = "#1565C0",
lwd = 3,
main = paste0("Corba ROC - Risc Cardiovascular (AUC = ", round(auc(roc_cv), 3), ")"),
xlab = "1 - Especificitat",
ylab = "Sensibilitat"
)
abline(a = 0, b = 1, lty = 2, col = "grey50")Pas 6: Pregunta 5 - Anàlisi de Supervivència i Riscos Proporcionals
Construeix l’objecte Surv(), representa les corbes de Kaplan-Meier amb taula de risc per estatus diabètic, ajusta el model de Cox i valida els residus de Schoenfeld.
Mostra la solució del Pas 5: Supervivència i model de Cox
# 1. Objecte de supervivència Surv(temps, esdeveniment)
surv_cv <- Surv(time = df_estudi$seguiment_anys, event = df_estudi$exitus_cv)
# 2. Corbes de Kaplan-Meier
km_diab <- survfit(surv_cv ~ diabetis, data = df_estudi)
ggsurvplot(
km_diab,
data = df_estudi,
risk.table = TRUE,
pval = TRUE,
conf.int = TRUE,
palette = c("#2E7D32", "#C62828"),
title = "Supervivència Lliure d'Esdeveniments segons Diabetis",
xlab = "Seguiment (anys)",
ylab = "Probabilitat de supervivència",
risk.table.height = 0.25
)Mostra la solució del Pas 5: Supervivència i model de Cox
# 3. Model de riscos proporcionals de Cox
mod_cox_cv <- coxph(
surv_cv ~ edat + sexe + tas + fuma + diabetis,
data = df_estudi
)
tbl_regression(mod_cox_cv, exponentiate = TRUE) %>%
bold_labels() %>%
bold_p(t = 0.05)| Characteristic | HR | 95% CI | p-value |
|---|---|---|---|
| edat | 0.99 | 0.98, 1.00 | 0.2 |
| sexe | |||
| Home | — | — | |
| Dona | 0.94 | 0.74, 1.18 | 0.6 |
| tas | 1.00 | 1.00, 1.01 | 0.2 |
| fuma | |||
| No | — | — | |
| Sí | 1.25 | 0.99, 1.58 | 0.062 |
| diabetis | |||
| No | — | — | |
| Sí | 1.07 | 0.80, 1.42 | 0.6 |
| Abbreviations: CI = Confidence Interval, HR = Hazard Ratio | |||
Mostra la solució del Pas 5: Supervivència i model de Cox
# 4. Verificació de riscos proporcionals (Schoenfeld)
test_schoenfeld <- cox.zph(mod_cox_cv)
print(test_schoenfeld) chisq df p
edat 0.01752 1 0.89
sexe 0.95818 1 0.33
tas 0.00469 1 0.95
fuma 0.71047 1 0.40
diabetis 0.96020 1 0.33
GLOBAL 2.63453 5 0.76
Mostra la solució del Pas 5: Supervivència i model de Cox
par(mfrow = c(2, 2))
plot(test_schoenfeld)Mostra la solució del Pas 5: Supervivència i model de Cox
par(mfrow = c(1, 1))Síntesi Metodològica Final
- Gestió de la qualitat de dades: La neteja prèvia elimina biaixos per valors extrems impossibles i codis numèrics oblidats (com el valor 99). L’auditoria de valors absents garanteix la transparència del procés.
- Presentació de dades: La Taula 1 automatitzada amb
gtsummarypermet complir les directrius internacionals de publicació biomèdica (STROBE/CONSORT). - Validació dels models: Tant en models logístics com en models de temps fins a l’esdeveniment (Cox), l’avaluació de supòsits (VIF i residus de Schoenfeld) és el pas indispensable abans d’interpretar els paràmetres clínics d’associació (OR i HR).