Separación de datos binomiales

Autor/a

Tamara Ricardo

Introducción

En los modelos de regresión logística, se analizan las relaciones entre una variable dependiente de tipo binaria y una o más variables independientes que pueden ser de distintos tipos (numéricas continuas, numéricas discretas, categóricas, binarias, etc.). Sin embargo, en algunas situaciones, la relación entre las variables independientes y la variable respuesta puede derivar en problemas de separación de datos, afectando las estimaciones del modelo.

Tipos de separación de datos

Separación total

También conocida como separación perfecta o completa, ocurre cuando un predictor o combinación lineal de predictores clasifica perfectamente las observaciones en una de las dos categorías de la variable respuesta. En estos casos, las probabilidades predichas para una de las categorías son 0 o 1, y los coeficientes de regresión para las variables involucradas se vuelven indefinidos. Esto provoca un fallo en la convergencia del modelo, ya que no se pueden calcular los logaritmos para odds ratios (OR) iguales a 0 o a infinito (\(\infty\)), generando un mensaje de error.

Separación parcial

La separación parcial se presenta cuando un predictor o combinación lineal de predictores predice perfectamente la variable respuesta solo en algunos niveles de las variables independientes. Es decir, separa en gran medida las categorías de la variable respuesta. Esto puede llevar a la estimación de coeficientes y errores estándar extremadamente grandes, con intervalos de confianza muy amplios, lo que hace que las inferencias realizadas sean poco fiables.

Tomemos como ejemplo la base “covid_cancer.txt”, que contiene datos de un estudio de casos y controles realizado en la ciudad de Santa Fe, Argentina en pacientes con cáncer hospitalizados por formas severas de COVID-191.

Cargamos paquetes necesarios:

Cargamos datos:

# Carga datos
datos <- read_delim("datos/covid_cancer.txt")

# Explora datos
glimpse(datos)
Rows: 79
Columns: 13
$ fallecido         <dbl> 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0…
$ sexo              <chr> "M", "M", "F", "F", "F", "M", "F", "M", "F", "M", "M…
$ edad              <dbl> 60, 65, 70, 73, 60, 67, 81, 35, 79, 47, 58, 70, 57, …
$ comorbilidades    <chr> "Sí", "Sí", "Sí", "Sí", "No", "Sí", "Sí", "No", "Sí"…
$ disnea            <chr> NA, "Sí", "Sí", "No", "No", NA, "No", NA, "Sí", NA, …
$ neumonia_severa   <chr> "Sí", "Sí", "Sí", "No", "No", "No", "No", "No", "No"…
$ inf_secundaria    <chr> "Sí", NA, "Sí", "No", "No", "No", "Sí", "No", "No", …
$ complicaciones    <chr> "Sí", NA, "Sí", "No", "No", "No", "Sí", "No", "Sí", …
$ infil_bilateral   <chr> "Sí", "Sí", "Sí", "No", "No", "No", "No", "No", "No"…
$ asist_resp        <chr> "Sí", "Sí", "Sí", "No", "No", "No", "No", "No", "No"…
$ cancer_tipo       <chr> "Sólido", "Sólido", "Sólido", "Sólido", "Sólido", "S…
$ tr_quimioterapia  <chr> "Sí", "Sí", "Sí", "Sí", "Sí", "Sí", "Sí", "Sí", "Sí"…
$ tr4_quimioterapia <chr> "Sí", "Sí", "Sí", "Sí", "Sí", "Sí", "No", "No", "No"…

Las variables de interés incluyen:

  • fallecido: fallecimiento por COVID-19 (No: 0, Sí: 1)

  • sexo: sexo biológico del/a paciente (M: masculino, F: femenino)

  • edad: edad en años al momento de la hospitalización

  • comorbilidades: presencia de comorbilidades (Sí, No)

  • disnea: dificultad para respirar (Sí, No)

  • neumonia_severa: paciente con neumonía severa (Sí, No)

  • inf_secundaria: presencia de infección secundaria (Sí, No)

  • complicaciones: complicaciones del COVID-19 (Sí, No)

  • infil_bilateral: infiltración bilateral en radiografía (Sí, No)

  • asist_resp: paciente que recibió asistencia respiratoria (Sí, No)

  • cancer_tipo: tipo de cáncer (Sólido, Hematológico)

  • tr_quimioterapia: paciente que recibió quimioterapia (Sí, No)

  • tr4_quimioterapia: paciente que recibió quimioterapia en el último mes (Sí, No)

Nuestra variable dependiente es si el/la paciente falleció a causa del COVID-19. Comenzaremos por ajustar modelos de regresión logística univariados para evaluar qué variables explicativas están asociadas significativamente a la variable dependiente:

# Regresiones logísticas simples
datos |> 
  tbl_uvregression(y = fallecido,
                   method = glm,
                   method.args = list(family = binomial),
                   exponentiate = T) |> 
  bold_p()
Characteristic N OR 95% CI p-value
sexo 78


    F

    M
1.95 0.61, 6.56 0.3
edad 74 1.04 0.99, 1.10 0.12
comorbilidades 53


    No

    Sí
0.93 0.27, 3.30 >0.9
disnea 51


    No

    Sí
9.19 2.18, 49.2 0.004
neumonia_severa 79


    No

    Sí
8.30 2.37, 31.2 0.001
inf_secundaria 36


    No

    Sí
8.33 1.38, 59.1 0.023
complicaciones 36


    No

    Sí
1,139,380,581 0.00,
>0.9
infil_bilateral 40


    No

    Sí
1.56 0.42, 5.98 0.5
asist_resp 39


    No

    Sí
6.00 1.26, 44.4 0.039
cancer_tipo 74


    Hematológico

    Sólido
3,844,194 0.00,
>0.9
tr_quimioterapia 49


    No

    Sí
5.20 0.85, 101 0.14
tr4_quimioterapia 39


    No

    Sí
1.40 0.33, 6.48 0.7
Abbreviations: CI = Confidence Interval, OR = Odds Ratio

Al ejecutar este código aparecerá el siguiente mensaje de advertencia: Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred. Esto indica que todas o alguna de las variables explicativas provoca separación parcial de los datos.

Al observar la tabla generada, notamos que las variables disnea, neumonia_severa, inf_secundaria, asist_resp y tr_quimioterapia presentan intervalos de confianza excesivamente amplios, lo que sugiere la presencia de separación parcial. Si generamos tablas de 2x2 de cada variable explicativa en función de la variable fallecido, podemos visualizar este fenómeno:

# disnea
datos |> 
  tabyl(disnea, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 disnea     0     1
     No 91.2%  8.8%
     Sí 52.9% 47.1%
# neumonía severa
datos |> 
  tabyl(neumonia_severa, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 neumonia_severa     0     1
              No 90.3%  9.7%
              Sí 52.9% 47.1%
# infección secundaria
datos |> 
  tabyl(inf_secundaria, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 inf_secundaria     0     1
             No 86.2% 13.8%
             Sí 42.9% 57.1%
# asistencia respiratoria
datos |> 
  tabyl(asist_resp, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 asist_resp     0     1
         No 88.9% 11.1%
         Sí 57.1% 42.9%
# quimioterapia
datos |> 
  tabyl(tr_quimioterapia, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 tr_quimioterapia     0     1
               No 92.9%  7.1%
               Sí 71.4% 28.6%

Estos análisis muestran que variables como disnea, neumonia_severa, inf_secundaria, asist_resp y tr_quimioterapia explican casi completamente la mortalidad por COVID-19 severo.

Si evaluamos las variables complicaciones y cancer_tipo:

# complicaciones
datos |> 
  tabyl(complicaciones, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
 complicaciones      0     1
             No 100.0%  0.0%
             Sí  42.9% 57.1%
# tipo de cáncer
datos |> 
  tabyl(cancer_tipo, fallecido, 
        show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
  cancer_tipo      0     1
 Hematológico 100.0%  0.0%
       Sólido  80.3% 19.7%

Observamos que todos los pacientes sin complicaciones o con cáncer hematológico sobrevivieron, lo cual también indica separación parcial de los datos.

Ahora, ajustemos un modelo para evaluar el efecto de la interacción entre el tipo de cáncer y haber recibido quimioterapia en los 30 días previos, controlando por la presencia de neumonía severa y asistencia respiratoria:

fit1 <- glm(fallecido ~ cancer_tipo * tr4_quimioterapia + 
              neumonia_severa + asist_resp, 
            data = datos, family = binomial)


summary(fit1)

Call:
glm(formula = fallecido ~ cancer_tipo * tr4_quimioterapia + neumonia_severa + 
    asist_resp, family = binomial, data = datos)

Coefficients: (1 not defined because of singularities)
                                       Estimate Std. Error z value Pr(>|z|)  
(Intercept)                            -19.4976  2399.5450  -0.008   0.9935  
cancer_tipoSólido                       16.7936  2399.5448   0.007   0.9944  
tr4_quimioterapiaSí                      0.8431     1.0135   0.832   0.4055  
neumonia_severaSí                        2.6769     1.3791   1.941   0.0522 .
asist_respSí                             0.2547     1.3683   0.186   0.8523  
cancer_tipoSólido:tr4_quimioterapiaSí        NA         NA      NA       NA  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 38.024  on 31  degrees of freedom
Residual deviance: 26.599  on 27  degrees of freedom
  (47 observations deleted due to missingness)
AIC: 36.599

Number of Fisher Scoring iterations: 15

El resumen del modelo muestra el mensaje: Coefficients: (1 not defined because of singularities), indicando que los errores estándar para el intercepto y cancer_tipo son muy grandes. Además, no se muestran los coeficientes ni el p-valor para la interacción. Esto se puede visualizar tabulando los coeficientes:

tbl_regression(fit1, exponentiate = T)
Characteristic OR 95% CI p-value
cancer_tipo


    Hematológico
    Sólido 19,650,260 0.00,
>0.9
tr4_quimioterapia


    No
    Sí 2.32 0.33, 21.1 0.4
neumonia_severa


    No
    Sí 14.5 1.30, 416 0.052
asist_resp


    No
    Sí 1.29 0.05, 17.7 0.9
cancer_tipo * tr4_quimioterapia


    Sólido * Sí


Abbreviations: CI = Confidence Interval, OR = Odds Ratio

Recibirás advertencias como: Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred y Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm) : collapsing to unique 'x' values, lo que sugiere separación parcial de los datos.

Para evaluar separación de estos datos, podemos crear una nueva variable que represente el cruce entre tipo de cáncer y quimioterapia usando la función fct_cross() de tidyverse:

datos |> 
  mutate(tipo_ca_quim = fct_cross(cancer_tipo, tr4_quimioterapia)) |> 
  tabyl(tipo_ca_quim, fallecido, show_na = F) |> 
  adorn_percentages() |> 
  adorn_pct_formatting()
    tipo_ca_quim      0     1
 Hematológico:No 100.0%  0.0%
       Sólido:No  76.5% 23.5%
       Sólido:Sí  71.4% 28.6%

Este análisis muestra que ninguno de los pacientes con cáncer hematológico recibió quimioterapia en los últimos 30 días, lo que también contribuye a la separación parcial.

Finalmente, para evaluar la separación de datos de manera más formal, utilizamos el paquete detectseparation (Kosmidis, Schumacher, y Schwendinger 2022). Ajustamos nuevamente el modelo con el argumento method = "detect_separation":

library(detectseparation)

# Tipo de cáncer
glm(fallecido ~ cancer_tipo, data = datos, family = binomial,
    method = "detect_separation")
Implementation: ROI | Solver: lpsolve 
Separation: TRUE 
Existence of maximum likelihood estimates
      (Intercept) cancer_tipoSólido 
             -Inf               Inf 
0: finite value, Inf: infinity, -Inf: -infinity
# Neumonía severa
glm(fallecido ~ neumonia_severa, data = datos, family = binomial,
    method = "detect_separation")
Implementation: ROI | Solver: lpsolve 
Separation: FALSE 
Existence of maximum likelihood estimates
      (Intercept) neumonia_severaSí 
                0                 0 
0: finite value, Inf: infinity, -Inf: -infinity
# Modelo con interacción
glm(fallecido ~ cancer_tipo * tr4_quimioterapia + 
              neumonia_severa + asist_resp, 
            data = datos, 
            family = binomial,
            method = "detect_separation")
Implementation: ROI | Solver: lpsolve 
Separation: TRUE 
Existence of maximum likelihood estimates
                          (Intercept)                     cancer_tipoSólido 
                                 -Inf                                   Inf 
                  tr4_quimioterapiaSí                     neumonia_severaSí 
                                    0                                     0 
                         asist_respSí cancer_tipoSólido:tr4_quimioterapiaSí 
                                    0                                    NA 
0: finite value, Inf: infinity, -Inf: -infinity

En el caso de separación completa, un nivel de la variable explicativa debería modelar perfectamente los “Sí” y el otro los “No”, y al intentar correr el modelo nos encontraríamos con un error de convergencia que impediría continuar con el análisis.

Soluciones a la separación de datos

Remuestreo, recolección o submuestreo

Siempre que fuera posible, recolectar más datos puede mitigar la separación total al introducir más variabilidad en los datos. En su defecto, se puede considerar el submuestreo o el sobremuestreo para balancear las clases. Sin embargo, estas técnicas deben aplicarse con cuidado para evitar sesgos.

Eliminación o recategorización de variables

En casos de separación parcial, una opción es simplificar el modelo eliminando las variables causantes de la separación. Si esas variables son esenciales, otra alternativa es agrupar niveles de las variables categóricas para reducir la separación. Esto puede hacer que el modelo sea más robusto al reducir la complejidad y mejorar la estabilidad de las estimaciones.

Uso de modelos alternativos a la regresión logística

Los modelos de pseudo-likelihood, como la regresión logística condicional y la máxima verosimilitud penalizada, permiten ajustar el modelo incluso cuando la separación de datos impide la convergencia con métodos tradicionales. Los modelos penalizados, que incluyen las regresiones Ridge, Lasso y Elastic Net, añaden un término de penalización que controla el crecimiento excesivo de los coeficientes en casos de separación parcial, facilitando la convergencia y mejorando la estabilidad del modelo. Por otro lado, los modelos bayesianos introducen distribuciones previas sobre los coeficientes, permitiendo una mayor flexibilidad para manejar la separación de datos y ofreciendo estimaciones más estables y confiables. Sin embargo, el ajuste e interpretación de estos modelos es complejo y escapa al alcance de este curso, por lo que no serán abordados en detalle.

Volver arriba

Referencias

Agresti, Alan. 2015. Foundations of Linear and Generalized Linear Models. Wiley Series En Probability y Statistics. Hoboken, New Jersey: John Wiley & Sons, Inc.
Firke, Sam. 2024. «janitor: Simple Tools for Examining and Cleaning Dirty Data». https://CRAN.R-project.org/package=janitor.
Kosmidis, Ioannis, Dirk Schumacher, y Florian Schwendinger. 2022. «detectseparation: Detect and Check for Separation and Infinite Maximum Likelihood Estimates». https://CRAN.R-project.org/package=detectseparation.
Wickham, Hadley, Mara Averick, Jennifer Bryan, Winston Chang, Lucy D’Agostino McGowan, Romain François, Garrett Grolemund, et al. 2019. «Welcome to the tidyverse» 4: 1686. https://doi.org/10.21105/joss.01686.

Notas

  1. Gastiazoro MP, Cardozo MA, Ricardo T, Ramos JG, Ballina A, Maillo M, et al. Clinical features in cancer patients with COVID-19 in Santa Fe and Buenos Aires, Argentina. J Clin Images Med Case Rep [Internet]. el 14 de febrero de 2023 [citado el 9 de agosto de 2024];4(2). Disponible en: https://jcimcr.org/articles/JCIMCR-v4-2285.html↩︎

Reutilización