1. Carga de datos y librerías

library(tidyverse)
library(gtsummary)
library(flextable)
library(ggplot2)
library(ggpubr)
library(scales)

2. Carga de base validada desde tabla cruzada

df_validado <- tibble::tibble(
  grupo_mfi5 = c(rep("0", 47), rep("1", 34), rep("2", 15), rep("3", 1)),
  sexo = c(
    rep("FEMENINO", 37), rep("MASCULINO", 10),
    rep("FEMENINO", 19), rep("MASCULINO", 15),
    rep("FEMENINO", 7), rep("MASCULINO", 8),
    "FEMENINO"
  ),
  edad = c(
    rep("60-70", 33), rep("71-80", 10), rep("81-90", 3), rep("MAS DE 90", 1),
    rep("60-70", 19), rep("71-80", 12), rep("81-90", 3),
    rep("60-70", 12), rep("71-80", 3),
    "71-80"
  ),
  asa = c(
    rep("I", 4), rep("II", 43),
    rep("III", 11), rep("II", 23),
    rep("III", 15),
    "III"
  )
)

3. Análisis general de características demográficas

df_validado %>%
  tbl_summary(
    statistic = list(all_categorical() ~ "{n} ({p}%)"),
    label = list(sexo ~ "Sexo", edad ~ "Edad", asa ~ "Clasificación ASA", grupo_mfi5 ~ "Índice de Fragilidad (mFI-5)")
  ) %>%
  add_n() %>%
  modify_caption("**Tabla 1. Características generales de la población (validada)**") %>%
  as_flex_table()
**Tabla 1. Características generales de la población (validada)**

Characteristic

N

N = 971

Índice de Fragilidad (mFI-5)

97

0

47 (48%)

1

34 (35%)

2

15 (15%)

3

1 (1.0%)

Sexo

97

FEMENINO

64 (66%)

MASCULINO

33 (34%)

Edad

97

60-70

64 (66%)

71-80

26 (27%)

81-90

6 (6.2%)

MAS DE 90

1 (1.0%)

Clasificación ASA

97

I

4 (4.1%)

II

66 (68%)

III

27 (28%)

1n (%)

4. Gráficos demográficos descriptivos

p1 <- df_validado %>%
  count(grupo_mfi5, sexo) %>%
  group_by(grupo_mfi5) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(x = grupo_mfi5, y = pct, fill = sexo)) +
  geom_col(position = "stack") +
  geom_text(aes(label = percent(pct, accuracy = 1)), 
            position = position_stack(vjust = 0.5), size = 3.5, color = "white") +
  labs(title = "Distribución de sexo por grupo mFI-5", x = "Grupo mFI-5", y = "%") +
  theme_minimal()

p2 <- df_validado %>%
  count(grupo_mfi5, edad) %>%
  group_by(grupo_mfi5) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(x = grupo_mfi5, y = pct, fill = edad)) +
  geom_col(position = "stack") +
  geom_text(aes(label = percent(pct, accuracy = 1)), 
            position = position_stack(vjust = 0.5), size = 3.5, color = "white") +
  labs(title = "Distribución de edad por grupo mFI-5", x = "Grupo mFI-5", y = "%") +
  theme_minimal()

p3 <- df_validado %>%
  count(grupo_mfi5, asa) %>%
  group_by(grupo_mfi5) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(x = grupo_mfi5, y = pct, fill = asa)) +
  geom_col(position = "stack") +
  geom_text(aes(label = percent(pct, accuracy = 1)), 
            position = position_stack(vjust = 0.5), size = 3.5, color = "white") +
  labs(title = "Clasificación ASA por grupo mFI-5", x = "Grupo mFI-5", y = "%") +
  theme_minimal()

p1; p2; p3

5. Tabla de asociación de características por grupo mFI-5

df_validado %>%
  tbl_summary(
    by = grupo_mfi5,
    statistic = list(all_categorical() ~ "{n} ({p}%)"),
    label = list(sexo ~ "Sexo", edad ~ "Edad", asa ~ "Clasificación ASA")
  ) %>%
  add_p(test = everything() ~ "fisher.test") %>%
  modify_caption("**Tabla 2. Asociación entre variables clínicas y grupos de fragilidad mFI-5**") %>%
  as_flex_table()
**Tabla 2. Asociación entre variables clínicas y grupos de fragilidad mFI-5**

Characteristic

0
N = 471

1
N = 341

2
N = 151

3
N = 11

p-value2

Sexo

0.033

FEMENINO

37 (79%)

19 (56%)

7 (47%)

1 (100%)

MASCULINO

10 (21%)

15 (44%)

8 (53%)

0 (0%)

Edad

0.4

60-70

33 (70%)

19 (56%)

12 (80%)

0 (0%)

71-80

10 (21%)

12 (35%)

3 (20%)

1 (100%)

81-90

3 (6.4%)

3 (8.8%)

0 (0%)

0 (0%)

MAS DE 90

1 (2.1%)

0 (0%)

0 (0%)

0 (0%)

Clasificación ASA

<0.001

I

4 (8.5%)

0 (0%)

0 (0%)

0 (0%)

II

43 (91%)

23 (68%)

0 (0%)

0 (0%)

III

0 (0%)

11 (32%)

15 (100%)

1 (100%)

1n (%)

2Fisher's exact test

6. Análisis clínico postoperatorio

complicaciones <- tibble::tribble(
  ~complicacion, ~mFI_0, ~mFI_1, ~mFI_2, ~mFI_3,
  "ESTANCIA INTRA HOSPITALARIA PROLONGADA", 3, 3, 2, 0,
  "PROBLEMAS RESPIRATORIOS", 1, 2, 1, 0,
  "INSUFICIENCIA RENAL", 0, 1, 0, 0,
  "INFECCIÓN DE SITIO QUIRURGICO", 1, 1, 0, 0,
  "CHOQUE HIPOVOLÉMICO", 0, 1, 0, 0
)

complicaciones_long <- complicaciones %>%
  pivot_longer(cols = starts_with("mFI_"), names_to = "grupo_mFI5", values_to = "frecuencia") %>%
  group_by(grupo_mFI5) %>%
  mutate(porcentaje = frecuencia / sum(frecuencia)) %>%
  filter(frecuencia > 0)

complicaciones_long$grupo_mFI5 <- factor(complicaciones_long$grupo_mFI5, levels = c("mFI_0", "mFI_1", "mFI_2", "mFI_3"))

ggplot(complicaciones_long, aes(x = grupo_mFI5, y = porcentaje, fill = complicacion)) +
  geom_col() +
  geom_text(aes(label = percent(porcentaje, accuracy = 1)), 
            position = position_stack(vjust = 0.5), size = 4, color = "white") +
  labs(title = "Complicaciones por grupo de fragilidad mFI-5", x = "Grupo mFI-5", y = "% del total por grupo") +
  scale_y_continuous(labels = percent_format()) +
  theme_minimal()

7. Análisis adicional: cálculo de OR mFI-5 ≥ 1 vs 0 con gtsummary

library(dplyr)
library(tibble)
library(knitr)

odds_data <- tibble::tribble(
  ~complicacion, ~mFI_0, ~mFI_01,
  "Sexo: Femenino", 37, 27,
  "Sexo: Masculino", 10, 23,
  "Edad: 60–70", 33, 31,
  "Edad: 71–80", 10, 17,
  "Edad: 81–90", 3, 3,
  "Edad: >90", 1, 0,
  "ASA: I", 4, 0,
  "ASA: II", 43, 23,
  "ASA: III", 0, 27,
  "ESTANCIA INTRA HOSPITALARIA PROLONGADA", 3, 5,
  "PROBLEMAS RESPIRATORIOS", 1, 3,
  "INSUFICIENCIA RENAL", 0, 1,
  "INFECCIÓN DE SITIO QUIRURGICO", 1, 1,
  "CHOQUE HIPOVOLÉMICO", 0, 1,
)

total_mFI0 <- 47
total_mFI01 <- 50

# Calcular OR y valor p sin errores
or_tabla <- odds_data %>%
  rowwise() %>%
  mutate(
    a = mFI_01,
    b = total_mFI01 - mFI_01,
    c = mFI_0,
    d = total_mFI0 - mFI_0,
    fisher_result = list(fisher.test(matrix(c(a, b, c, d), nrow = 2))),
    OR = round(fisher_result$estimate[[1]], 2),
    p = signif(fisher_result$p.value, 4)
  ) %>%
  ungroup() %>%
  select(Complicación = complicacion, `Odds Ratio (OR)` = OR, `Valor p` = p)

knitr::kable(or_tabla, caption = "**Tabla 3. Odds Ratio de complicaciones y variables clínicas asociadas con mFI-5 ≥1 vs mFI-5=0**")
Tabla 3. Odds Ratio de complicaciones y variables clínicas asociadas con mFI-5 ≥1 vs mFI-5=0
Complicación Odds Ratio (OR) Valor p
Sexo: Femenino 0.32 0.0176800
Sexo: Masculino 3.11 0.0176800
Edad: 60–70 0.69 0.5204000
Edad: 71–80 1.89 0.1811000
Edad: 81–90 0.94 1.0000000
Edad: >90 0.00 0.4845000
ASA: I 0.00 0.0514800
ASA: II 0.08 0.0000011
ASA: III Inf 0.0000000
ESTANCIA INTRA HOSPITALARIA PROLONGADA 1.62 0.7158000
PROBLEMAS RESPIRATORIOS 2.91 0.6178000
INSUFICIENCIA RENAL Inf 1.0000000
INFECCIÓN DE SITIO QUIRURGICO 0.94 1.0000000
CHOQUE HIPOVOLÉMICO Inf 1.0000000