El presente análisis cuantitativo se centra en la evaluación de la satisfacción del paciente, un indicador clave de la calidad en la gestión sanitaria. Se busca ir más allá de los factores subjetivos, incorporando métricas funcionales objetivas.
El estudio se centra en la Evaluación de la Satisfacción del Paciente utilizando un enfoque cuantitativo. El objetivo es identificar los determinantes más significativos y robustos de la satisfacción del paciente (medida en una escala de 1 a 10) a partir de una combinación de variables clínicas objetivas y factores demográficos/subjetivos, con el fin de informar la toma de decisiones administrativas en salud.
¿Qué combinación de métricas funcionales objetivas (frecuencia mínima de pasos y ángulo articular) y factores demográficos/subjetivos (edad, frecuencia de visita y calidad de vida) maximiza la precisión y la robustez en la predicción de la satisfacción del paciente, y cuáles son los umbrales clínicos óptimos para la intervención?
Los datos utilizados provienen de la base de datos de evaluación de
salud comunitaria de Kaggle:
community_health_evaluation_dataset.csv. Fuente de
Datos: Community Health Evaluation Dataset (proveniente de
Kaggle).
El script comienza cargando los paquetes y realizando la limpieza inicial de la base de datos, estandarizando nombres y convirtiendo variables categóricas a factores para el análisis.
# =======================
# Paquetes
# =======================
library(tidyverse)
library(janitor)
library(MASS)
library(rpart)
library(rpart.plot)
# =======================
# Cargar datos
# =======================
data_raw <- read_csv("community_health_evaluation_dataset.csv")
# =======================
# Limpieza y preparación
# =======================
data_cleaned <- data_raw %>%
clean_names() %>%
mutate(
patient_satisfaction = as.numeric(patient_satisfaction_1_10),
age = as.numeric(age),
quality_of_life_score = as.numeric(quality_of_life_score),
gender = as.factor(gender),
ses = as.factor(ses),
service_type = as.factor(service_type),
visit_frequency = as.factor(visit_frequency),
emg_activity = as.factor(emg_activity)
)
glimpse(data_cleaned)
## Rows: 347
## Columns: 13
## $ participant_id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1…
## $ age <dbl> 56, 69, 46, 32, 60, 25, 38, 56, 36, 40, 28, …
## $ gender <fct> F, M, M, F, M, F, F, F, F, F, M, M, F, F, M,…
## $ ses <fct> 4, 1, 4, 1, 3, 1, 3, 1, 3, 1, 2, 1, 1, 2, 4,…
## $ service_type <fct> Rehab, Preventive, Rehab, Consultation, Prev…
## $ visit_frequency <fct> Weekly, Yearly, Yearly, Weekly, Weekly, Year…
## $ step_frequency_steps_min <dbl> 85, 80, 81, 66, 73, 74, 66, 68, 67, 82, 88, …
## $ stride_length_m <dbl> 0.54, 0.70, 0.57, 0.78, 0.84, 0.90, 0.60, 0.…
## $ joint_angle <dbl> 17.99, 13.05, 29.85, 28.54, 20.80, 26.84, 20…
## $ emg_activity <fct> Low, Moderate, Moderate, Moderate, High, Hig…
## $ patient_satisfaction_1_10 <dbl> 1, 8, 4, 9, 5, 3, 9, 9, 4, 4, 8, 2, 9, 6, 9,…
## $ quality_of_life_score <dbl> 57, 94, 66, 66, 98, 82, 81, 57, 68, 87, 52, …
## $ patient_satisfaction <dbl> 1, 8, 4, 9, 5, 3, 9, 9, 4, 4, 8, 2, 9, 6, 9,…
# =======================
# Verificación
# =======================
str(data_cleaned)
## tibble [347 × 13] (S3: tbl_df/tbl/data.frame)
## $ participant_id : num [1:347] 1 2 3 4 5 6 7 8 9 10 ...
## $ age : num [1:347] 56 69 46 32 60 25 38 56 36 40 ...
## $ gender : Factor w/ 2 levels "F","M": 1 2 2 1 2 1 1 1 1 1 ...
## $ ses : Factor w/ 4 levels "1","2","3","4": 4 1 4 1 3 1 3 1 3 1 ...
## $ service_type : Factor w/ 3 levels "Consultation",..: 3 2 3 1 2 1 2 1 3 3 ...
## $ visit_frequency : Factor w/ 3 levels "Monthly","Weekly",..: 2 3 3 2 2 3 1 2 3 1 ...
## $ step_frequency_steps_min : num [1:347] 85 80 81 66 73 74 66 68 67 82 ...
## $ stride_length_m : num [1:347] 0.54 0.7 0.57 0.78 0.84 0.9 0.6 0.58 0.55 0.82 ...
## $ joint_angle : num [1:347] 18 13.1 29.9 28.5 20.8 ...
## $ emg_activity : Factor w/ 3 levels "High","Low","Moderate": 2 3 3 3 1 1 3 1 2 2 ...
## $ patient_satisfaction_1_10: num [1:347] 1 8 4 9 5 3 9 9 4 4 ...
## $ quality_of_life_score : num [1:347] 57 94 66 66 98 82 81 57 68 87 ...
## $ patient_satisfaction : num [1:347] 1 8 4 9 5 3 9 9 4 4 ...
head(data_cleaned)
## # A tibble: 6 × 13
## participant_id age gender ses service_type visit_frequency
## <dbl> <dbl> <fct> <fct> <fct> <fct>
## 1 1 56 F 4 Rehab Weekly
## 2 2 69 M 1 Preventive Yearly
## 3 3 46 M 4 Rehab Yearly
## 4 4 32 F 1 Consultation Weekly
## 5 5 60 M 3 Preventive Weekly
## 6 6 25 F 1 Consultation Yearly
## # ℹ 7 more variables: step_frequency_steps_min <dbl>, stride_length_m <dbl>,
## # joint_angle <dbl>, emg_activity <fct>, patient_satisfaction_1_10 <dbl>,
## # quality_of_life_score <dbl>, patient_satisfaction <dbl>
Estadísticas descriptivas de las variables clave
summary(data_cleaned %>%dplyr::select(patient_satisfaction,age,quality_of_life_score,step_frequency_steps_min))
## patient_satisfaction age quality_of_life_score
## Min. : 1.000 Min. :18.00 Min. :50.0
## 1st Qu.: 3.000 1st Qu.:31.00 1st Qu.:62.0
## Median : 5.000 Median :43.00 Median :74.0
## Mean : 5.213 Mean :43.37 Mean :74.2
## 3rd Qu.: 8.000 3rd Qu.:56.00 3rd Qu.:86.0
## Max. :10.000 Max. :69.00 Max. :99.0
## step_frequency_steps_min
## Min. :60.00
## 1st Qu.:71.00
## Median :81.00
## Mean :80.12
## 3rd Qu.:90.00
## Max. :99.00
# Visualización de la variable dependiente
ggplot(data_cleaned, aes(x = patient_satisfaction)) +
geom_bar(fill = "steelblue") +
labs(title = "Distribución de la Satisfacción del Paciente (1-10)", x = "Puntuación de Satisfacción", y = "Frecuencia") +
theme_minimal()
El histograma de frecuencias muestra la distribución de las puntuaciones de satisfacción del paciente en una escala de 1 a 10. La frecuencia en el eje Y representa el número de pacientes que reportaron una puntuación específica eje X. La distribución de la satisfacción del paciente es irregular y multimodal, lo cual refleja que no existe un consenso claro en relación a un nivel de satisfacción único que domine en la población. La existencia de múltiples picos y la alta frecuencia de puntuaciones bajas (1.0 y 2.5) indican que la variable dependiente (patient_satisfaction) es difícil de predecir porque no sigue una tendencia única.
Se incorporan el Modelo de Regresión Lineal Múltiple y la Regresión Logística Ordinal[cite: 7, 8].
Objetivo: Explorar una relación lineal inicial entre la satisfacción y un conjunto de variables demográficas y subjetivas.
\[\text{Satisfacción} = \beta_0 + \beta_1 (\text{Age}) + \beta_2 (\text{SES}) + \beta_3 (\text{Service\_Type}) + \beta_4 (\text{Visit\_Frequency}) + \beta_5 (\text{EMG\_Activity}) + \epsilon\]
# Modelo 1: Regresión Lineal Múltiple
modelo_satisfaccion <- lm(
patient_satisfaction ~ age + ses + service_type + visit_frequency + emg_activity,
data = data_cleaned
)
# Resumen del modelo
summary(modelo_satisfaccion)
##
## Call:
## lm(formula = patient_satisfaction ~ age + ses + service_type +
## visit_frequency + emg_activity, data = data_cleaned)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.7346 -2.2597 -0.1771 2.4607 5.2696
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.128147 0.646808 7.928 3.31e-14 ***
## age 0.001289 0.010208 0.126 0.900
## ses2 0.207698 0.421612 0.493 0.623
## ses3 0.108648 0.420740 0.258 0.796
## ses4 0.439753 0.434187 1.013 0.312
## service_typePreventive -0.030452 0.375404 -0.081 0.935
## service_typeRehab -0.371745 0.380520 -0.977 0.329
## visit_frequencyWeekly 0.343736 0.381664 0.901 0.368
## visit_frequencyYearly -0.058679 0.384988 -0.152 0.879
## emg_activityLow -0.055399 0.382910 -0.145 0.885
## emg_activityModerate -0.265952 0.374001 -0.711 0.478
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.859 on 336 degrees of freedom
## Multiple R-squared: 0.01135, Adjusted R-squared: -0.01808
## F-statistic: 0.3856 on 10 and 336 DF, p-value: 0.9526
# Gráficos de diagnóstico
par(mfrow = c(2, 2))
plot(modelo_satisfaccion)
par(mfrow = c(1, 1))
Los gráficos Residuals vs Fitted y Normal Q-Q sugieren que los supuestos de linealidad, homocedasticidad y normalidad de los residuos se cumplen.
data_with_predictions <- data_cleaned %>%
mutate(
Predicted_Satisfaction = predict(modelo_satisfaccion),
Residuals = residuals(modelo_satisfaccion)
)
plot_prediccion <- ggplot(data_with_predictions, aes(x = patient_satisfaction, y = Predicted_Satisfaction)) +
geom_point(alpha = 0.6, color = "darkred") +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "blue", linewidth = 1) +
labs(
title = "Predicción del Modelo de Satisfacción del Paciente (MLR)",
x = "Satisfacción Real (Observada) (1-10)",
y = "Satisfacción Ajustada (Predicha por el Modelo)"
) +
coord_cartesian(xlim = c(1, 10), ylim = c(1, 10)) +
theme_minimal()
print(plot_prediccion)
###Evaluación de Capacidad Predictiva (Modelo 1): Dado que los puntos están dispersos lejos de la línea azul diagonal, el modelo no tiene un alto poder predictivo, justificando el uso de modelos más fuertes.
Se utiliza este modelo porque la variable dependiente (patient_satisfaction) es una escala ordinal (1 a 10).
data_cleaned <- data_cleaned %>%
mutate(patient_satisfaction_ord = factor(patient_satisfaction, ordered = TRUE))
modelo_ordinal <- polr(patient_satisfaction_ord ~ age + ses + service_type + visit_frequency + emg_activity,
data = data_cleaned,
Hess = TRUE)
summary(modelo_ordinal)
## Call:
## polr(formula = patient_satisfaction_ord ~ age + ses + service_type +
## visit_frequency + emg_activity, data = data_cleaned, Hess = TRUE)
##
## Coefficients:
## Value Std. Error t value
## age 0.001476 0.006273 0.23524
## ses2 0.118201 0.256713 0.46044
## ses3 0.063176 0.256893 0.24592
## ses4 0.269664 0.265372 1.01617
## service_typePreventive -0.009192 0.228881 -0.04016
## service_typeRehab -0.229356 0.234052 -0.97994
## visit_frequencyWeekly 0.208030 0.232312 0.89548
## visit_frequencyYearly -0.047011 0.234822 -0.20020
## emg_activityLow -0.022637 0.235060 -0.09630
## emg_activityModerate -0.156813 0.227523 -0.68922
##
## Intercepts:
## Value Std. Error t value
## 1|2 -1.8855 0.4196 -4.4933
## 2|3 -1.2506 0.4097 -3.0525
## 3|4 -0.6000 0.4052 -1.4806
## 4|5 -0.1916 0.4039 -0.4744
## 5|6 0.2779 0.4046 0.6869
## 6|7 0.6910 0.4066 1.6993
## 7|8 1.0847 0.4094 2.6493
## 8|9 1.7019 0.4166 4.0856
## 9|10 2.6523 0.4416 6.0064
##
## Residual Deviance: 1584.211
## AIC: 1622.211
logL_modelo <- logLik(modelo_ordinal)
modelo_nulo <- polr(patient_satisfaction_ord ~ 1, data = data_cleaned, Hess = TRUE)
logL_nulo <- logLik(modelo_nulo)
R2_McFadden <- 1 - (logL_modelo / logL_nulo)
cat("\n--- Pseudo R^2 de McFadden ---\n")
##
## --- Pseudo R^2 de McFadden ---
print(R2_McFadden)
## 'log Lik.' 0.002502743 (df=19)
prueba_lrt <- anova(modelo_ordinal, update(modelo_ordinal, ~ 1), test = "Chisq")
cat("\n--- Prueba de Razón de Verosimilitud (LRT) ---\n")
##
## --- Prueba de Razón de Verosimilitud (LRT) ---
print(prueba_lrt)
## Likelihood ratio tests of ordinal regression models
##
## Response: patient_satisfaction_ord
## Model Resid. df
## 1 1 338
## 2 age + ses + service_type + visit_frequency + emg_activity 328
## Resid. Dev Test Df LR stat. Pr(Chi)
## 1 1588.185
## 2 1584.211 1 vs 2 10 3.974821 0.9484757
###Evaluación de Capacidad Explicativa (Modelo 2): El R2 de McFadden es extremadamente bajo (0.0025) y la prueba LRT no rechaza la hipótesis nula (p-valor 0.948), lo que indica que el modelo no es estadísticamente significativo y que las variables elegidas inicialmente no explican la satisfacción.
Aquí se introduce el modelo no lineal para buscar mayor poder predictivo.
Justificación:
Se busca una relación no lineal, incluyendo variables funcionales y
demográficas, para maximizar la capacidad predictiva de la
satisfacción alta frente a la satisfacción
baja.
library(rpart)
library(rpart.plot)
library(dplyr)
data_modeled <- data_cleaned %>%
mutate(
Satisfaccion_Alta_Bin = factor(
ifelse(patient_satisfaction >= 9, "Alta", "Baja")
)
)
formula_arbol <- Satisfaccion_Alta_Bin ~
age + ses + service_type + visit_frequency + emg_activity +
quality_of_life_score + step_frequency_steps_min +
stride_length_m + joint_angle
modelo_arbol <- rpart(
formula_arbol,
data = data_modeled,
method = "class",
control = rpart.control(minsplit = 20, cp = 0.01))
rpart.plot(
modelo_arbol,
type = 4,
extra = 101,
main = "Árbol de Decisión: Predicción de Alta Satisfacción",
nn = TRUE
)
predicciones_arbol <- predict(modelo_arbol, data_modeled, type = "class")
matriz_confusion <- table(Real = data_modeled$Satisfaccion_Alta_Bin, Predicho = predicciones_arbol)
print(matriz_confusion)
## Predicho
## Real Alta Baja
## Alta 28 30
## Baja 14 275
accuracy_arbol <- sum(diag(matriz_confusion)) / sum(matriz_confusion)
cat("\n--- Precisión (Accuracy) del Modelo ---\n")
##
## --- Precisión (Accuracy) del Modelo ---
print(accuracy_arbol)
## [1] 0.8731988
El Árbol de Decisión es el modelo más fuerte, alcanzando una Precisión (Accuracy) del 87.3% en la clasificación. Los predictores clave identificados para diferenciar la Satisfacción Baja y Alta son la Frecuencia Mínima de Pasos y el Ángulo de la Articulación, con umbrales específicos. La Matriz de Confusión indica que el modelo es muy bueno prediciendo la Satisfacción Baja (Verdaderos Negativos), pero omite casi la mitad de los casos de Satisfacción Alta.
Se utiliza el target binario (Satisfaccion_Alta) para intentar modelar la probabilidad, tomando las variables del árbol.
Se usa el dataframe limpio y modelado para asegurar consistencia
modelo_logistico_bin <- glm(
Satisfaccion_Alta_Bin ~ quality_of_life_score + age + visit_frequency,
data = data_modeled,
family = binomial(link = "logit")
)
summary(modelo_logistico_bin)
##
## Call:
## glm(formula = Satisfaccion_Alta_Bin ~ quality_of_life_score +
## age + visit_frequency, family = binomial(link = "logit"),
## data = data_modeled)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.747057 0.869580 2.009 0.0445 *
## quality_of_life_score -0.007677 0.010531 -0.729 0.4660
## age 0.011030 0.009637 1.144 0.2524
## visit_frequencyWeekly -0.055187 0.356675 -0.155 0.8770
## visit_frequencyYearly -0.056930 0.357484 -0.159 0.8735
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 313.23 on 346 degrees of freedom
## Residual deviance: 311.52 on 342 degrees of freedom
## AIC: 321.52
##
## Number of Fisher Scoring iterations: 4
exp(coef(modelo_logistico_bin))
## (Intercept) quality_of_life_score age
## 5.7376904 0.9923527 1.0110908
## visit_frequencyWeekly visit_frequencyYearly
## 0.9463085 0.9446606
Este modelo confirma que la Satisfacción Alta es difícil de predecir. El puntaje de Calidad de Vida no es significativo. La Edad tiene una relación marginal positiva, y el tipo de Frecuencia de Visita (semanal o anual) reduce las probabilidades de Satisfacción Alta en comparación con la base.
El analisis secuencial de modelos nos permitió descartar relaciones lineales y ordinales débiles (MLR y Logística Ordinal), y converge en que el Árbol de Decisión (Modelo 3) es la herramienta más valiosa para la gestión, con una capacidad predictiva del 87.3%.
Los determinantes críticos de la satisfacción, según el Árbol, no son los factores demográficos o subjetivos (como la Calidad de Vida), sino las métricas funcionales objetivas: 1. Frecuencia Mínima de Pasos (\(step\_frequency\_steps\_min\)): Divisor principal del árbol. 2. Ángulo de la Articulación (\(joint\_angle\)): Segunda variable más importante.
Las conclusiones se estructuran como recomendaciones de gestión en salud basadas en la evidencia cuantitativa:
```