El 26 de agosto de 2026, en el marco de la huelga médica, el Sindicato Nacional de Médicos (Sinamed) sostuvo que otorgar el ajuste salarial reclamado “sería una inversión en salud”: pagar mejor a los médicos —argumentan— evitaría gastos mucho mayores por complicaciones y traslados de pacientes que hoy no encuentran especialistas, sobre todo en el interior. (Fuente periodística: https://www.abc.com.py/politica/2026/08/26/huelga-de-medicos-ajuste-salarial-seria-una-inversion-en-salud-dice-sinamed/)
Las afirmaciones concretas que se pueden contrastar con datos son:
| # | Afirmación del Sinamed | ¿Contrastable con la nómina SFP? |
|---|---|---|
| H1 | El salario médico no se ajusta desde 2012; hoy ronda ₲ 5.000.000. | Parcial: la SFP publica 2016→2026. Sí se puede medir la trayectoria nominal y real. |
| H2 | En 2012 un médico ganaba 2,8 salarios mínimos; hoy 1,5. El mínimo subió 79,8 % en 14 años. | Sí: se compara la remuneración médica con el Salario Mínimo (SMM) vigente cada año. |
| H3 | Piden pasar de ₲ 5.000.000 a ₲ 8.000.000; el costo sería ≈ USD 120 millones/año. | Sí, de forma aproximada: nº de médicos × brecha × 12–13 pagos. |
| H4 | El ajuste “sería una inversión”: se paga solo evitando complicaciones y traslados. | No de forma directa con la nómina. Se plantea como análisis de punto de equilibrio (cuánto habría que ahorrar para que el gasto se autofinancie). |
Qué puede y qué no puede probar este trabajo. La nómina de la SFP permite medir con rigor la erosión del salario médico (H1, H2) y dimensionar el costo fiscal del pedido (H3). No contiene información clínica ni de traslados, por lo que la tesis central —que el ajuste “se paga solo” (H4)— sólo se aborda como escenario, no como resultado demostrado.
Evaluar, con la nómina de pagos de la Secretaría de la Función Pública (SFP) del Ministerio de Salud Pública y Bienestar Social (MSPBS), en qué medida los datos respaldan el argumento del Sinamed de que el ajuste salarial reclamado constituye “una inversión en salud”.
fst
(fpsalud_filtrada.fst).series_externas).ipc_2026).gs_por_usd.| anho | smm | ipc | deflactor |
|---|---|---|---|
| 2016 | 1964507 | 129.93 | 1.52 |
| 2017 | 2041123 | 134.61 | 1.47 |
| 2018 | 2112562 | 139.96 | 1.41 |
| 2019 | 2192839 | 143.82 | 1.38 |
| 2020 | 2192839 | 146.36 | 1.35 |
| 2021 | 2289324 | 153.37 | 1.29 |
| 2022 | 2550307 | 168.35 | 1.18 |
| 2023 | 2680373 | 176.15 | 1.12 |
| 2024 | 2798309 | 182.90 | 1.08 |
| 2025 | 2899048 | 190.29 | 1.04 |
| 2026 | 3044000 | 197.90 | 1.00 |
## Filas con pago (julio, todas las entidades = MSPBS): 1.145.391
## Años disponibles: 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026
profesion sólo está cargada desde 2017; en cambio
cargo y funcion contienen la palabra
médico también en años tempranos y en residentes cuya
profesion figura como “BACHILLER”. Por eso se marca como
médico/a la fila que menciona medicina en cualquiera de
los tres campos, y luego se define un panel estable:
quien fue clasificado médico/a alguna vez se considera
médico/a en todos los años en que aparece. Así se corrige la cobertura
despareja de los campos entre años.
| anho | medicos | % con >1 línea | principal_mediana | total_mediana |
|---|---|---|---|---|
| 2.016 | 6.842 | 77 | 4.200.000 | 8.363.636 |
| 2.017 | 7.177 | 75 | 4.200.000 | 8.363.636 |
| 2.018 | 7.393 | 71 | 4.516.364 | 8.736.000 |
| 2.019 | 7.920 | 71 | 4.536.000 | 9.032.728 |
| 2.020 | 8.887 | 75 | 4.536.000 | 9.034.182 |
| 2.021 | 9.112 | 73 | 4.536.000 | 9.072.000 |
| 2.022 | 9.881 | 73 | 4.536.000 | 8.949.091 |
| 2.023 | 10.436 | 77 | 4.579.985 | 9.669.802 |
| 2.024 | 10.939 | 83 | 4.863.636 | 9.727.273 |
| 2.025 | 11.702 | 72 | 5.454.545 | 9.090.910 |
| 2.026 | 8.621 | 55 | 4.000.000 | 8.000.000 |
Nota sobre 2026. La descarga de julio-2026 (obtenida en agosto-2026) trae ~57.700 filas frente a ~130.000 en 2025: es una planilla preliminar (faltan guardias, honorarios y personal aún no liquidado). Los valores de 2026 se muestran punteados y no se usan para las conclusiones principales.
El campo carga_horaria es texto libre con el horario del
cargo (“LUNES A VIERNES DE 07:00 A 13:00 HS”). Se convierte a
horas semanales parseando el rango diario y el número
de días, y sumando guardias explícitas. La cobertura del campo es baja
antes de 2021 (25–45 %) y alta después (72–98 %), por lo que no
se usa como serie temporal, sólo para caracterizar la jornada
típica del cargo médico base.
| anho | principal_nom | total_nom | principal_real | total_real | total_smm | principal_smm |
|---|---|---|---|---|---|---|
| 2.016 | 4.200.000 | 8.363.636 | 6.397.137 | 12.738.887 | 4.26 | 2.14 |
| 2.017 | 4.200.000 | 8.363.636 | 6.174.727 | 12.295.993 | 4.10 | 2.06 |
| 2.018 | 4.516.364 | 8.736.000 | 6.386.028 | 12.352.489 | 4.14 | 2.14 |
| 2.019 | 4.536.000 | 9.032.728 | 6.241.652 | 12.429.265 | 4.12 | 2.07 |
| 2.020 | 4.536.000 | 9.034.182 | 6.133.332 | 12.215.528 | 4.12 | 2.07 |
| 2.021 | 4.536.000 | 9.072.000 | 5.852.999 | 11.705.997 | 3.96 | 1.98 |
| 2.022 | 4.536.000 | 8.949.091 | 5.332.191 | 10.519.900 | 3.51 | 1.78 |
| 2.023 | 4.579.985 | 9.669.802 | 5.145.496 | 10.863.774 | 3.61 | 1.71 |
| 2.024 | 4.863.636 | 9.727.273 | 5.262.513 | 10.525.026 | 3.48 | 1.74 |
| 2.025 | 5.454.545 | 9.090.910 | 5.672.681 | 9.454.470 | 3.14 | 1.88 |
| 2.026 | 4.000.000 | 8.000.000 | 4.000.000 | 8.000.000 | 2.63 | 1.31 |
El salario médico no siguió el ritmo de los precios. Aunque hubo pequeños aumentos nominales, el poder adquisitivo de la mediana cayó alrededor de un cuarto entre 2016 y 2025. Esto es consistente con H1, con el matiz de que la SFP no permite retroceder hasta 2012.
Para descartar que la caída se deba a un cambio de composición (entran médicos jóvenes peor pagos), se sigue a los mismos médicos presentes en 2017 y en 2025.
## Médicos presentes en 2017 y 2025: 5828
## Cambio real mediano de su remuneración 2017→2025: -9.0%
## Proporción que perdió poder adquisitivo: 61.3%
Incluso quienes permanecieron en el MSPBS —y acumularon antigüedad, que juega a su favor— perdieron en términos reales (mediana ≈ -9.0%), y 6 de cada 10 están peor que en 2017. La erosión no es un efecto de composición.
La afirmación “de 2,8 a 1,5 SMM” describe el sueldo de un cargo y toma como base 2012. Con datos SFP desde 2016, el sueldo de un puesto médico pasó de ~2,1 a ~1,8–1,9 SMM (y 1,7 en 2024). La dirección y el orden de magnitud del reclamo se verifican; el valor exacto “1,5” depende de la base 2012 (fuera del alcance de la SFP) y de tomar el piso nominal fijo de ₲ 5.000.000.
La comparación anterior enfrenta el sueldo de un cargo médico (jornada parcial, típicamente 30 h/semana — el turno “lunes a viernes de 07:00 a 13:00”) con un Salario Mínimo que remunera una jornada plena de 48 h/semana. Son horas distintas. El indicador de suficiencia por hora las pone en la misma base:
\[\text{ratio hora} = \frac{\text{₲/hora del cargo médico}}{\text{₲/hora del SMM}} = \frac{\text{sueldo del cargo} \,/\, (30\cdot 52/12)} {\text{SMM} \,/\, (48\cdot 52/12)}\]
| Año | ≤20 h | 21–30 h | 31–40 h | >40 h | cobertura del campo (%) |
|---|---|---|---|---|---|
| 2021 | 52 | 16 | 17 | 15 | 84 |
| 2022 | 54 | 18 | 17 | 11 | 74 |
| 2023 | 57 | 19 | 18 | 5 | 72 |
| 2024 | 48 | 17 | 18 | 17 | 79 |
| 2025 | 54 | 19 | 16 | 11 | 86 |
| Año | líneas de pago (mediana) | % con ≥2 líneas |
|---|---|---|
| 2016 | 2 | 77 |
| 2017 | 2 | 75 |
| 2018 | 2 | 71 |
| 2019 | 2 | 71 |
| 2020 | 2 | 75 |
| 2021 | 2 | 73 |
| 2022 | 2 | 73 |
| 2023 | 2 | 77 |
| 2024 | 2 | 83 |
| 2025 | 2 | 72 |
| 2026 | 2 | 55 |
| Año | Ratio mensual (cargo/SMM) | Ratio hora (30 h) | Ratio hora (sens. 24 h) | Ratio hora (sens. 40 h) |
|---|---|---|---|---|
| 2016 | 2.14 | 3.42 | 4.28 | 2.57 |
| 2017 | 2.06 | 3.29 | 4.12 | 2.47 |
| 2018 | 2.14 | 3.42 | 4.28 | 2.57 |
| 2019 | 2.07 | 3.31 | 4.14 | 2.48 |
| 2020 | 2.07 | 3.31 | 4.14 | 2.48 |
| 2021 | 1.98 | 3.17 | 3.96 | 2.38 |
| 2022 | 1.78 | 2.85 | 3.56 | 2.13 |
| 2023 | 1.71 | 2.73 | 3.42 | 2.05 |
| 2024 | 1.74 | 2.78 | 3.48 | 2.09 |
| 2025 | 1.88 | 3.01 | 3.76 | 2.26 |
| grupo | y_ini | y_fin | cambio real |
|---|---|---|---|
| Médicos/as | 12.738.887 | 9.454.470 | -25.8% |
| Resto del MSPBS | 5.062.877 | 4.085.919 | -19.3% |
Ambos grupos pierden poder adquisitivo: el congelamiento salarial del sector público no es exclusivo de los médicos, aunque ellos parten de un nivel más alto y la caída relativa es similar.
| Escenario | Médicos alcanzados | Costo anual (₲) | Costo anual (USD) |
|---|---|---|---|
| A · brecha fija ₲3,0 M × todos los médicos (criterio Sinamed) | 11.702 | 456.378.000.000 | 62.517.534 |
| B · subir sólo a los que están bajo ₲8,0 M, por su brecha real | 7.769 | 328.204.523.634 | 44.959.524 |
La nómina no registra traslados ni complicaciones, de modo que no se puede demostrar que el ajuste se autofinancie. Sí se puede dar vuelta la pregunta: ¿cuánto tendría que ahorrar el Estado, por médico y por año, para cubrir el costo del ajuste?
## Costo incremental por médico/año: ₲ 39.000.000 ≈ USD 5342
Esto no prueba la tesis del Sinamed, pero muestra que el umbral de ahorro necesario es bajo frente a los costos unitarios de la no-atención. La plausibilidad del argumento depende de evidencia clínica y de gestión que excede a la nómina.
Hasta acá se describió lo ocurrido 2016–2025. Esta sección lo proyecta hacia 2035 para responder la pregunta que está detrás del conflicto: si el salario médico sigue sin indexarse, ¿dónde termina su poder adquisitivo?
Mecánica y supuestos (todos en el chunk
parametros):
| Política 2026–2035 | Salario real 2035 (₲ 2025) | Pérdida vs. 2025 | Pérdida acum. vs. 2016 | Cargo ppal. en SMM (2035) | ₲ no percibidos por médico en 10 años |
|---|---|---|---|---|---|
| Congelamiento (0 % nominal) | 6.141.493 | -32.4% | -49.9% | 1.27 | 223.257.794 |
| Ajuste parcial (50 % del IPC) | 7.486.446 | -17.6% | -38.9% | 1.55 | 118.058.514 |
| Ajuste pleno = IPC (mantiene) | 9.090.910 | 0.0% | -25.8% | 1.88 | 0 |
| Inflación anual | Salario real 2035 (₲ 2025) | Pérdida vs. 2025 | Pérdida acum. vs. 2016 | ₲ no percibidos / médico (10 años) |
|---|---|---|---|---|
| 6 % | 5.076.317 | -44.2% | -58.6% | 311.989.743 |
| 3 % | 6.764.491 | -25.6% | -44.8% | 173.703.318 |
| 4 % | 6.141.493 | -32.4% | -49.9% | 223.257.794 |
Qué es y qué no es esta proyección. Es un ejercicio determinístico de “si todo lo demás sigue igual”: inflación constante, sin cambios de escala, sin promociones ni antigüedad, sin nuevas guardias. No predice el futuro; cuantifica el costo de no hacer nada y sirve para comparar políticas de ajuste.
profesion incompleta antes de 2017; se
mitiga con el panel “médico/a alguna vez”, pero el conteo de médicos de
2016 puede quedar algo subestimado.devengado sumado.
Incluye guardias, bonificaciones y aguinaldo si cayeron en julio; puede
sobrestimar el “sueldo” mensual típico. Por eso se reporta también el
cargo principal (línea de mayor monto).| Afirmación | Veredicto | Evidencia principal |
|---|---|---|
| H1 · Salario médico congelado / se atrasó | RESPALDADA (2016–2025) | Real mediano -25.8% (2016→2025); cohorte fija -9.0% |
| H2 · Cayó de ~2,8 a ~1,5 salarios mínimos | RESPALDADA en dirección y magnitud; el ‘1,5’ exacto depende de la base 2012 | Cargo: 2.1→1.9 SMM/mes; por hora 3.4→3.0 SMM (-12%). Total: 4.3→3.1 SMM |
| H3 · Pasar de ₲5 M a ₲8 M cuesta ~USD 120 M/año | PLAUSIBLE a escala nacional; SOBREESTIMADA si se atribuye sólo al MSPBS (~USD 55–65 M) | 11.702 médicos MSPBS × ₲3 M × 13 pagos = USD 63 M |
| H4 · El ajuste ‘es una inversión’ (se paga solo) | NO DEMOSTRABLE con la nómina; el punto de equilibrio requerido es bajo | USD 5342/médico/año ≈ 1 traslado aéreo o ~15–20 días de UTI |
Lo que los datos sostienen. La remuneración real de los médicos del MSPBS se deterioró de forma clara entre 2016 y 2025 —del orden de un 26% en la mediana, y 9% incluso para quienes no se movieron— y su salario medido en mínimos bajó de manera sostenida, tanto por mes como por hora (-12% para el cargo base, con jornada constante). En eso, el reclamo del Sinamed (H1, H2) está bien fundado.
Lo que conviene matizar. El costo del pedido para el MSPBS ronda los USD 55–65 millones/año, no 120; la cifra mayor sólo cierra si se suma a todo el sector público. Y la tesis central —que el ajuste “se paga solo” (H4)— no puede demostrarse con la nómina: es una hipótesis razonable (el ahorro necesario por médico es pequeño frente al costo de un traslado o una internación evitada), pero su prueba exige datos clínicos, de derivaciones y de traslados que el Estado debería poner sobre la mesa.
Mirando hacia adelante. La proyección (§6) muestra que no hacer nada no es un escenario neutro: como el Salario Mínimo se indexa por ley y el sueldo público no, mantener el congelamiento llevaría la pérdida acumulada de poder adquisitivo a ≈ -50% hacia 2035 (inflación central), y cada médico dejaría de percibir ≈ ₲ 223.257.794 en la década frente a un salario indexado. Un ajuste parcial (50 % del IPC) sólo frena la caída a la mitad.
En síntesis: los datos confirman que hay un problema salarial real y medible —pasado y, de mantenerse el congelamiento, agravado a futuro—; convertir el ajuste en una “inversión” que se autofinancia es plausible pero queda como afirmación por verificar, no como hecho establecido.
Reproducible con R ≥ 4.4, paquetes data.table,
fst, ggplot2, scales. Series
externas y parámetros en los chunks parametros y
series_externas. Fuente de la nómina: Secretaría de la
Función Pública del Paraguay.
El cuerpo del informe se compila sin mostrar código. El bloque siguiente reúne, en el orden en que se ejecutan, todos los fragmentos de R del documento (incluidos los parámetros y las series externas). No se evalúa aquí: sólo se transcribe para revisión y reproducción.
# Al compilar NO se muestra ningún código; el script completo va en el Anexo final.
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE,
fig.width = 9, fig.height = 5, dpi = 110)
options(scipen = 999)
## --- Parámetros y supuestos (todos editables) ---------------------------------
ruta <- "G:/Mi unidad/FACEN_BIGDATA/FACEN_BIG_DATA_2DO_SEM_2026/FACEN_BIG_DATA_2DO_SEM_2026/datos_funcion_publica_py/"
anio_base <- 2026 # año al que se llevan los guaraníes constantes
piso_actual <- 5000000 # ₲ salario médico "actual" citado por el Sinamed
piso_pedido <- 8000000 # ₲ salario médico solicitado
pagos_anio <- 13 # 12 sueldos + aguinaldo
gs_por_usd <- 7300 # tipo de cambio de referencia (jul-2026 aprox.)
ipc_2026 <- 197.90 # PROYECCIÓN del IPC prom. 2026 (≈ +4 % sobre 2025)
anios_analisis <- 2016:2025 # 2026 se muestra aparte: descarga preliminar (ver §Calidad)
## --- Carga horaria para el indicador "costo hora" (§ H2 bis) ------------------
hrs_smm_sem <- 48 # jornada plena de referencia del SMM (Cód. Laboral art. 194)
hrs_med_base_sem <- 30 # jornada del cargo médico base ("L a V 07:00–13:00"); sens. 24–40
## --- Parámetros de la proyección a futuro (§6) --------------------------------
h_proy <- 10 # años proyectados hacia adelante (t0 = 2025 -> 2035)
anio_t0 <- 2025 # año base de la proyección (último con datos completos)
ipc_esc <- c(baja = 0.03, central = 0.04, alta = 0.06) # inflación anual supuesta
# Política salarial 2026–2035: fracción del IPC que se reconoce como ajuste nominal.
pol_esc <- c("Congelamiento (0 % nominal)" = 0.00,
"Ajuste parcial (50 % del IPC)" = 0.50,
"Ajuste pleno = IPC (mantiene)" = 1.00)
library(data.table)
library(fst)
library(ggplot2)
library(scales)
knitr::opts_chunk$set()
theme_set(theme_minimal(base_size = 12))
gs <- function(x) paste0("₲ ", format(round(x), big.mark = ".", decimal.mark = ","))
externas <- data.table(
anho = 2016:2026,
# SMM vigente en julio de cada año (₲). 2020 sin reajuste por pandemia.
smm = c(1964507, 2041123, 2112562, 2192839, 2192839,
2289324, 2550307, 2680373, 2798309, 2899048, 3044000),
# IPC promedio anual, base 2010 = 100 (Banco Mundial FP.CPI.TOTL; 2026 proyectado).
ipc = c(129.93, 134.61, 139.96, 143.82, 146.36,
153.37, 168.35, 176.15, 182.90, 190.29, ipc_2026)
)
ipc_base <- externas[anho == anio_base, ipc]
externas[, deflactor := ipc_base / ipc] # multiplica ₲ corrientes -> ₲ de anio_base
knitr::kable(externas, digits = 2,
caption = "Salario Mínimo e IPC usados como referencia")
fps <- as.data.table(read_fst(paste0(ruta, "fpsalud_filtrada.fst")))
# La SFP publica en codificación latin1: normalizamos a UTF-8 para poder usar regex.
cols_chr <- names(fps)[vapply(fps, is.character, logical(1))]
for (cc in cols_chr) set(fps, j = cc, value = iconv(fps[[cc]], "latin1", "UTF-8"))
# Sólo julio, filas con pago efectivo (devengado > 0 descarta vacancias y no pagados).
fps <- fps[mes == 7 & devengado > 0]
cat("Filas con pago (julio, todas las entidades = MSPBS):", format(nrow(fps), big.mark = "."), "\n")
cat("Años disponibles:", paste(sort(unique(fps$anho)), collapse = ", "), "\n")
rx_med <- "MEDIC|CIRUJAN|DOCTOR EN MEDICINA|DOCTOR A EN MEDICINA|DOCTORA EN MEDICINA"
blob <- toupper(paste(fps$profesion, fps$cargo, fps$funcion))
fps[, fila_medico := grepl(rx_med, blob) & !grepl("VETERINARI|ESTUDIANTE", blob)]
docs_medicos <- fps[fila_medico == TRUE, unique(documento)]
fps[, medico := documento %in% docs_medicos]
# Remuneración individual por año: total (suma de todas las líneas) y
# "cargo principal" (línea de mayor monto ≈ el sueldo del puesto).
pers <- fps[, .(
sal_total = sum(devengado),
sal_principal = max(devengado),
n_lineas = .N,
medico = any(medico)
), by = .(documento, anho)]
pers <- merge(pers, externas[, .(anho, smm, ipc, deflactor)], by = "anho")
pers[, sal_total_real := sal_total * deflactor]
pers[, sal_principal_real := sal_principal * deflactor]
pers[, sal_total_smm := sal_total / smm]
pers[, sal_principal_smm := sal_principal / smm]
med <- pers[medico == TRUE]
knitr::kable(
med[, .(medicos = .N,
`% con >1 línea` = round(100 * mean(n_lineas > 1)),
principal_mediana = round(median(sal_principal)),
total_mediana = round(median(sal_total))),
by = anho][order(anho)],
format.args = list(big.mark = "."),
caption = "Panel de médicos/as del MSPBS (julio de cada año)"
)
parse_week_hours <- function(x) {
s <- toupper(x)
s <- chartr("ÁÉÍÓÚÜÑ", "AEIOUUN", s)
s <- gsub("HRS?\\b|\\bHS?\\b|HORAS?", " ", s)
s <- gsub("\\(A\\)", " A ", s); s <- gsub("[.,;]", " ", s); s <- gsub(":", " ", s)
s <- gsub("\\s+", " ", trimws(s))
if (is.na(s) || s == "" || s == "0") return(NA_real_)
mult <- NA_real_
if (grepl("LUNES A VIERNES|LUN A VIE", s)) mult <- 5
if (grepl("LUNES A SABADO", s)) mult <- 6
if (grepl("LUNES A DOMINGO|DIARIO|TODOS LOS DIAS", s)) mult <- 7
if (grepl("SABADO.? Y DOMINGO|SABADO.? DOMINGO|FIN(ES)? DE SEMANA", s)) mult <- 2
if (is.na(mult)) {
dias <- c("LUNES","MARTES","MIERCOLES","JUEVES","VIERNES","SABADO","DOMINGO")
nd <- sum(vapply(dias, function(z) grepl(z, s), logical(1)))
mult <- if (nd > 0) nd else 5
}
m <- regmatches(s, regexec("(\\d{1,2})(?: (\\d{2}))? A (\\d{1,2})(?: (\\d{2}))?", s))[[1]]
daily <- NA_real_
if (length(m) == 5) {
h1 <- as.numeric(m[2]); mi1 <- ifelse(m[3] == "", 0, as.numeric(m[3])) / 60
h2 <- as.numeric(m[4]); mi2 <- ifelse(m[5] == "", 0, as.numeric(m[5])) / 60
dur <- (h2 + mi2) - (h1 + mi1)
if (!is.na(dur) && dur <= 0) dur <- dur + 24
if (!is.na(dur) && dur > 0 && dur <= 16) daily <- dur
}
base <- if (!is.na(daily)) daily * mult else NA_real_
extra <- 0
gg <- regmatches(s, gregexpr("GUARDIA[A-Z ]*? DE (\\d{1,2})", s))[[1]]
if (length(gg)) {
hrsg <- as.numeric(sub(".*DE ", "", gg)); nrep <- 1
if (grepl("DOS GUARDIA", s)) nrep <- 2
if (grepl("TRES GUARDIA", s)) nrep <- 3
extra <- sum(hrsg, na.rm = TRUE) * ifelse(length(gg) == 1, nrep, 1)
}
tot <- sum(base, extra, na.rm = TRUE)
if (is.na(tot) || tot <= 0) return(NA_real_)
min(tot, 90)
}
uh <- unique(fps$carga_horaria)
fps[, wk_sem := setNames(vapply(uh, parse_week_hours, numeric(1)), uh)[carga_horaria]]
# Horas de la línea de mayor monto (≈ el cargo principal), por persona-año
pers_h <- fps[, .(wk_ppal = wk_sem[which.max(devengado)]), by = .(documento, anho)]
pers <- merge(pers, pers_h, by = c("documento", "anho"), all.x = TRUE)
med <- pers[medico == TRUE]
cobertura_ch <- fps[, .(cobertura = round(100 * mean(!is.na(wk_sem)))), by = anho][order(anho)]
resumen <- med[, .(
principal_nom = median(sal_principal),
total_nom = median(sal_total),
principal_real = median(sal_principal_real),
total_real = median(sal_total_real),
total_smm = median(sal_total_smm),
principal_smm = median(sal_principal_smm)
), by = anho][order(anho)]
knitr::kable(resumen, digits = 2, format.args = list(big.mark = "."),
caption = paste0("Remuneración mensual mediana de médicos/as del MSPBS. ",
"Valores reales en ₲ de ", anio_base, "."))
d1 <- melt(resumen[, .(anho, `Nominal` = total_nom,
`Real (₲ 2026)` = total_real)],
id.vars = "anho")
ggplot(d1, aes(anho, value, color = variable)) +
geom_line(aes(linetype = anho > max(anios_analisis)), linewidth = 1) +
geom_point(data = d1[anho %in% anios_analisis], size = 2) +
scale_y_continuous(labels = label_number(big.mark = ".", prefix = "₲ ")) +
scale_x_continuous(breaks = 2016:2026) +
scale_linetype_manual(values = c("solid", "dotted"), guide = "none") +
labs(title = "Remuneración mensual TOTAL mediana de un médico/a del MSPBS",
subtitle = "Suma de todas las líneas de pago. 2026 preliminar (punteado).",
x = NULL, y = NULL, color = NULL)
r16 <- resumen[anho == 2016]; r25 <- resumen[anho == 2025]
cambio_nom <- r25$total_nom / r16$total_nom - 1
cambio_real <- r25$total_real / r16$total_real - 1
ipc_16_25 <- externas[anho == 2025, ipc] / externas[anho == 2016, ipc] - 1
c17 <- pers[medico == TRUE & anho == 2017, .(documento, s17 = sal_total_real)]
c25 <- pers[medico == TRUE & anho == 2025, .(documento, s25 = sal_total_real)]
coh <- merge(c17, c25, by = "documento")
cambio_coh <- median(coh$s25 / coh$s17) - 1
cat("Médicos presentes en 2017 y 2025:", nrow(coh), "\n")
cat("Cambio real mediano de su remuneración 2017→2025:", percent(cambio_coh, 0.1), "\n")
cat("Proporción que perdió poder adquisitivo:", percent(mean(coh$s25 < coh$s17), 0.1), "\n")
d2 <- melt(resumen[, .(anho,
`Total / SMM` = total_smm,
`Cargo principal / SMM` = principal_smm)],
id.vars = "anho")
ggplot(d2, aes(anho, value, color = variable)) +
geom_line(aes(linetype = anho > max(anios_analisis)), linewidth = 1) +
geom_point(data = d2[anho %in% anios_analisis], size = 2) +
geom_hline(yintercept = c(1.5, 2.8), linetype = "dashed", color = "grey50") +
annotate("text", x = 2016.2, y = 2.9, label = "2,8 SMM (Sinamed, 2012)", hjust = 0, size = 3) +
annotate("text", x = 2016.2, y = 1.6, label = "1,5 SMM (Sinamed, 2026)", hjust = 0, size = 3) +
scale_x_continuous(breaks = 2016:2026) +
scale_linetype_manual(values = c("solid", "dotted"), guide = "none") +
labs(title = "Remuneración médica expresada en Salarios Mínimos Mensuales (SMM)",
x = NULL, y = "Cantidad de SMM", color = NULL)
smm_16_26 <- externas[anho == 2026, smm] / externas[anho == 2016, smm] - 1
# ¿Cambió la jornada del cargo médico? (años con cobertura decente del campo)
jbin <- med[!is.na(wk_ppal) & anho %in% 2021:2025]
jbin[, tramo := cut(wk_ppal, c(-Inf, 20, 30, 40, Inf),
labels = c("≤20 h", "21–30 h", "31–40 h", ">40 h"))]
tj <- dcast(jbin, anho ~ tramo, fun.aggregate = length, value.var = "wk_ppal")
num <- setdiff(names(tj), "anho")
tot <- rowSums(tj[, ..num])
tj[, (num) := lapply(.SD, function(x) round(100 * x / tot)), .SDcols = num]
tj <- merge(tj, cobertura_ch, by = "anho")
setnames(tj, c("anho", "cobertura"), c("Año", "cobertura del campo (%)"))
knitr::kable(tj, caption = paste0("Jornada semanal de la línea principal de los médicos, ",
"distribución en % (años con buena cobertura del campo)."))
lineas_med <- med[, .(`líneas de pago (mediana)` = as.numeric(median(n_lineas)),
`% con ≥2 líneas` = round(100 * mean(n_lineas >= 2))),
by = anho][order(anho)]
setnames(lineas_med, "anho", "Año")
knitr::kable(lineas_med, caption = "Número de cargos / líneas de pago por médico y año")
mes_h_med <- hrs_med_base_sem * 52 / 12
mes_h_smm <- hrs_smm_sem * 52 / 12
ratio_h <- med[, .(
ratio_mensual = median(sal_principal / smm),
ratio_hora = median((sal_principal / mes_h_med) / (smm / mes_h_smm)),
# sensibilidad: jornada médica de 24 y 40 h
ratio_hora_24 = median((sal_principal / (24 * 52/12)) / (smm / mes_h_smm)),
ratio_hora_40 = median((sal_principal / (40 * 52/12)) / (smm / mes_h_smm))
), by = anho][order(anho)]
knitr::kable(ratio_h[anho %in% anios_analisis], digits = 2,
col.names = c("Año", "Ratio mensual (cargo/SMM)",
paste0("Ratio hora (", hrs_med_base_sem, " h)"),
"Ratio hora (sens. 24 h)", "Ratio hora (sens. 40 h)"),
caption = paste0("Suficiencia del cargo médico frente al Salario Mínimo: por mes vs. ",
"por hora. Jornada médica base = ", hrs_med_base_sem,
" h/sem; SMM = ", hrs_smm_sem, " h/sem."))
d2b <- melt(ratio_h[anho %in% anios_analisis,
.(anho, `Por mes (cargo / SMM)` = ratio_mensual,
`Por hora (misma jornada)` = ratio_hora)],
id.vars = "anho")
ggplot(d2b, aes(anho, value, colour = variable)) +
geom_line(linewidth = 1) + geom_point(size = 2) +
geom_hline(yintercept = 1, linetype = "dotted", colour = "grey55") +
scale_x_continuous(breaks = anios_analisis) +
expand_limits(y = 0) +
labs(title = "¿Cuántos 'mínimos' vale el cargo médico? Por mes y por hora",
subtitle = paste0("Cargo médico base ", hrs_med_base_sem,
" h/sem vs. SMM ", hrs_smm_sem, " h/sem"),
x = NULL, y = "Salarios mínimos equivalentes", colour = NULL)
rh16 <- ratio_h[anho == 2016]; rh25 <- ratio_h[anho == 2025]; rh24 <- ratio_h[anho == 2024]
chg_mes <- rh25$ratio_mensual / rh16$ratio_mensual - 1
chg_hora <- rh25$ratio_hora / rh16$ratio_hora - 1
chg_hora_min <- rh24$ratio_hora / rh16$ratio_hora - 1
comp <- pers[, .(mediana_real = median(sal_total_real)),
by = .(anho, grupo = fifelse(medico, "Médicos/as", "Resto del MSPBS"))]
ggplot(comp[anho %in% anios_analisis], aes(anho, mediana_real, color = grupo)) +
geom_line(linewidth = 1) + geom_point(size = 2) +
scale_y_continuous(labels = label_number(big.mark = ".", prefix = "₲ ")) +
scale_x_continuous(breaks = anios_analisis) +
labs(title = "Remuneración total mediana real: médicos vs. resto del MSPBS",
subtitle = paste0("₲ constantes de ", anio_base),
x = NULL, y = NULL, color = NULL)
tab_comp <- dcast(comp[anho %in% range(anios_analisis)], grupo ~ anho,
value.var = "mediana_real")
setnames(tab_comp, as.character(range(anios_analisis)), c("y_ini", "y_fin"))
tab_comp[, `cambio real` := percent(y_fin / y_ini - 1, 0.1)]
knitr::kable(tab_comp, format.args = list(big.mark = "."),
caption = "Cambio real 2016→2025 por grupo")
n_med_2025 <- med[anho == 2025, .N]
# Médicos cuyo cargo principal hoy está por debajo del piso pedido:
bajo_piso <- med[anho == 2025 & sal_principal < piso_pedido]
n_bajo_piso <- nrow(bajo_piso)
brecha_media <- mean(piso_pedido - bajo_piso$sal_principal)
# Escenario A (Sinamed): brecha fija de ₲3.000.000 para todos los médicos.
costo_A_gs <- n_med_2025 * (piso_pedido - piso_actual) * pagos_anio
# Escenario B (nómina): subir sólo a quienes están por debajo del piso, su brecha real.
costo_B_gs <- n_bajo_piso * brecha_media * pagos_anio
esc <- data.table(
Escenario = c("A · brecha fija ₲3,0 M × todos los médicos (criterio Sinamed)",
"B · subir sólo a los que están bajo ₲8,0 M, por su brecha real"),
`Médicos alcanzados` = c(n_med_2025, n_bajo_piso),
`Costo anual (₲)` = c(costo_A_gs, costo_B_gs),
`Costo anual (USD)` = c(costo_A_gs, costo_B_gs) / gs_por_usd
)
knitr::kable(esc, digits = 0, format.args = list(big.mark = "."),
caption = paste0("Costo del ajuste del piso médico (", pagos_anio,
" pagos, TC ", gs_por_usd, " ₲/USD). Nómina MSPBS."))
masa_2025_gs <- fps[anho == 2025, sum(as.numeric(devengado))] * pagos_anio
costo_por_medico_anio_gs <- (piso_pedido - piso_actual) * pagos_anio # ₲3,0M × 13
costo_por_medico_anio_usd <- costo_por_medico_anio_gs / gs_por_usd
cat("Costo incremental por médico/año:",
gs(costo_por_medico_anio_gs), "≈ USD", round(costo_por_medico_anio_usd), "\n")
base_p <- resumen[anho == anio_t0]
p0_total <- base_p$total_nom
p0_principal <- base_p$principal_nom
smm0 <- externas[anho == anio_t0, smm]
# Grilla inflación × política × horizonte
proy <- CJ(infl = names(ipc_esc), pol = names(pol_esc), t = 0:h_proy)
proy[, `:=`(ipc = ipc_esc[infl], frac = pol_esc[pol], anho = anio_t0 + t)]
proy[, sal_total_nom := p0_total * (1 + frac * ipc)^t]
proy[, sal_principal_nom := p0_principal * (1 + frac * ipc)^t]
proy[, smm := smm0 * (1 + ipc)^t] # SMM indexado por ley
proy[, sal_total_real := sal_total_nom / (1 + ipc)^t] # ₲ constantes de 2025
proy[, sal_principal_real := sal_principal_nom / (1 + ipc)^t]
proy[, total_smm := sal_total_nom / smm]
proy[, principal_smm := sal_principal_nom / smm]
# Histórico re-expresado en ₲ de 2025 para empalmar con la proyección
hist25 <- merge(resumen[anho <= anio_t0, .(anho, total_nom, principal_nom)],
externas[, .(anho, ipc)], by = "anho")
ipc25 <- externas[anho == anio_t0, ipc]
hist25[, `:=`(sal_total_real = total_nom * ipc25 / ipc,
sal_principal_real = principal_nom * ipc25 / ipc)]
# Ingreso NO percibido en 10 años vs. mantener el poder adquisitivo (₲ de 2025)
ref <- proy[pol == "Ajuste pleno = IPC (mantiene)", .(infl, t, ref_real = sal_total_real)]
proy <- merge(proy, ref, by = c("infl", "t"))
no_perc <- proy[t >= 1, .(no_percibido = sum(ref_real - sal_total_real) * pagos_anio),
by = .(infl, pol)]
gc <- proy[infl == "central"]
ggplot() +
geom_line(data = hist25, aes(anho, sal_total_real), linewidth = 1) +
geom_point(data = hist25, aes(anho, sal_total_real), size = 1.5) +
geom_line(data = gc, aes(anho, sal_total_real, color = pol),
linewidth = 1, linetype = "22") +
geom_hline(yintercept = p0_total, linetype = "dotted", colour = "grey55") +
annotate("text", x = 2016, y = p0_total * 1.02, hjust = 0, size = 3,
label = "nivel de 2025") +
scale_y_continuous(labels = label_number(big.mark = ".", prefix = "₲ "),
limits = c(0, NA)) +
scale_x_continuous(breaks = seq(2016, 2035, 3)) +
labs(title = "Poder adquisitivo del salario médico: histórico (2016–2025) y proyección (2026–2035)",
subtitle = paste0("Remuneración total mediana, ₲ constantes de ", anio_t0,
". Inflación central 4 %/año."),
x = NULL, y = NULL, colour = "Política 2026–2035")
banda <- dcast(proy[pol == "Congelamiento (0 % nominal)"],
anho ~ infl, value.var = "sal_total_real")
ggplot(banda, aes(anho)) +
geom_line(data = hist25, aes(anho, sal_total_real), linewidth = 1) +
geom_ribbon(aes(ymin = alta, ymax = baja), alpha = .18, fill = "firebrick") +
geom_line(aes(y = central), colour = "firebrick", linewidth = 1, linetype = "22") +
geom_hline(yintercept = p0_total, linetype = "dotted", colour = "grey55") +
scale_y_continuous(labels = label_number(big.mark = ".", prefix = "₲ "),
limits = c(0, NA)) +
scale_x_continuous(breaks = seq(2016, 2035, 3)) +
labs(title = "Escenario de congelamiento: rango según la inflación (3 % – 6 %)",
subtitle = paste0("Banda = inflación entre 3 % y 6 %; línea = 4 %. ₲ de ", anio_t0, "."),
x = NULL, y = NULL)
fin <- proy[t == h_proy]
fin[, perdida_2025 := 1 - sal_total_real / p0_total]
fin[, infl := factor(infl, levels = c("baja", "central", "alta"),
labels = c("3 %", "4 %", "6 %"))]
ggplot(fin, aes(reorder(pol, -perdida_2025), perdida_2025, fill = infl)) +
geom_col(position = position_dodge(.85), width = .8) +
geom_text(aes(label = percent(perdida_2025, 1)),
position = position_dodge(.85), vjust = -0.35, size = 3) +
scale_y_continuous(labels = percent, expand = expansion(c(0, .15))) +
scale_fill_brewer(palette = "OrRd") +
labs(title = "Pérdida de poder adquisitivo acumulada a 2035 (vs. 2025)",
x = NULL, y = NULL, fill = "Inflación anual") +
theme(axis.text.x = element_text(size = 9))
fin2 <- proy[t == h_proy]
fin2[, perdida_2025 := 1 - sal_total_real / p0_total]
fin2[, perdida_2016 := 1 - sal_total_real / hist25[anho == 2016, sal_total_real]]
tab <- merge(fin2, no_perc, by = c("infl", "pol"))
tab_c <- tab[infl == "central"][order(-perdida_2025)]
knitr::kable(
tab_c[, .(`Política 2026–2035` = pol,
`Salario real 2035 (₲ 2025)` = round(sal_total_real),
`Pérdida vs. 2025` = percent(-perdida_2025, 0.1),
`Pérdida acum. vs. 2016` = percent(-perdida_2016, 0.1),
`Cargo ppal. en SMM (2035)` = round(principal_smm, 2),
`₲ no percibidos por médico en 10 años` = round(no_percibido))],
format.args = list(big.mark = "."),
caption = paste0("Proyección a 2035 con inflación central (4 %/año). ",
"'No percibido' = diferencia acumulada frente a indexar por IPC, ",
pagos_anio, " pagos/año, ₲ de ", anio_t0, "."))
# Sensibilidad del congelamiento a la inflación
sens <- tab[pol == "Congelamiento (0 % nominal)"][order(infl)]
knitr::kable(
sens[, .(`Inflación anual` = c("3 %", "4 %", "6 %")[match(infl, c("baja","central","alta"))],
`Salario real 2035 (₲ 2025)` = round(sal_total_real),
`Pérdida vs. 2025` = percent(-perdida_2025, 0.1),
`Pérdida acum. vs. 2016` = percent(-perdida_2016, 0.1),
`₲ no percibidos / médico (10 años)` = round(no_percibido))],
format.args = list(big.mark = "."),
caption = "Escenario de congelamiento — sensibilidad a la inflación")
cc <- tab[pol == "Congelamiento (0 % nominal)" & infl == "central"]
clo <- tab[pol == "Congelamiento (0 % nominal)" & infl == "baja"]
chi <- tab[pol == "Congelamiento (0 % nominal)" & infl == "alta"]
parc <- tab[pol == "Ajuste parcial (50 % del IPC)" & infl == "central"]
# año en que el sueldo de un cargo médico equivaldría a 1 SMM (congelamiento)
t_1smm <- log(p0_principal / smm0) / log(1 + ipc_esc)
anio_1smm <- round(anio_t0 + t_1smm)
agg_congel <- cc$no_percibido * n_med_2025
knitr::kable(data.table(
Afirmación = c("H1 · Salario médico congelado / se atrasó",
"H2 · Cayó de ~2,8 a ~1,5 salarios mínimos",
"H3 · Pasar de ₲5 M a ₲8 M cuesta ~USD 120 M/año",
"H4 · El ajuste 'es una inversión' (se paga solo)"),
Veredicto = c("RESPALDADA (2016–2025)",
"RESPALDADA en dirección y magnitud; el '1,5' exacto depende de la base 2012",
"PLAUSIBLE a escala nacional; SOBREESTIMADA si se atribuye sólo al MSPBS (~USD 55–65 M)",
"NO DEMOSTRABLE con la nómina; el punto de equilibrio requerido es bajo"),
`Evidencia principal` = c(
sprintf("Real mediano %s (2016→2025); cohorte fija %s", percent(cambio_real,0.1), percent(cambio_coh,0.1)),
sprintf("Cargo: %.1f→%.1f SMM/mes; por hora %.1f→%.1f SMM (%s). Total: %.1f→%.1f SMM",
resumen[anho==2016]$principal_smm, resumen[anho==2025]$principal_smm,
rh16$ratio_hora, rh25$ratio_hora, percent(chg_hora, 1),
resumen[anho==2016]$total_smm, resumen[anho==2025]$total_smm),
sprintf("%s médicos MSPBS × ₲3 M × %d pagos = USD %d M",
format(n_med_2025, big.mark="."), pagos_anio, round(costo_A_gs/gs_por_usd/1e6)),
sprintf("USD %d/médico/año ≈ 1 traslado aéreo o ~15–20 días de UTI",
round(costo_por_medico_anio_usd)))
), caption = "Síntesis del contraste")