Este documento presenta un modelo SEIR (Susceptible-Expuesto-Infeccioso-Recuperado) que incorpora:
Las ecuaciones diferenciales del modelo son:
\[\frac{dS}{dt} = \mu N - \frac{\beta S I}{N} - \mu S + \omega R\]
\[\frac{dE}{dt} = \frac{\beta S I}{N} - \sigma E - \mu E\]
\[\frac{dI}{dt} = \sigma E - \gamma I - \mu I\]
\[\frac{dR}{dt} = \gamma I - \mu R - \omega R\]
Donde:
# Función del modelo SEIR con demografía y pérdida de inmunidad
seir_demographics <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + E + I + R
# Ecuaciones diferenciales
dS <- mu * N - beta * S * I / N - mu * S + omega * R
dE <- beta * S * I / N - sigma * E - mu * E
dI <- sigma * E - gamma * I - mu * I
dR <- gamma * I - mu * R - omega * R
return(list(c(dS, dE, dI, dR)))
})
}# Parámetros sarampión
parameters <- c(
beta = 2.3, # Tasa de transmisión (contactos/año)
sigma = 1/7.5, # Tasa de incubación (1/período latente en días)
gamma = 1/6.5, # Tasa de recuperación (1/período infeccioso en días)
mu = 1/(75*365), # Tasa de natalidad/mortalidad (1/duración vida)
omega = 1/180 # Tasa de pérdida de inmunidad (1/duración inmunidad)
)
# Calcular R0
R0 <- parameters["beta"] / (parameters["gamma"] + parameters["mu"])
cat("Número básico de reproducción (R₀):", round(R0, 2), "\n")## Número básico de reproducción (R₀): 14.95
# Población de 1 millón
N <- 1e6
# Condiciones iniciales (cerca del equilibrio endémico)
initial_state <- c(
S = 0.198 * N, # 14% susceptibles
E = 0.001 * N, # 0.1% expuestos
I = 0.001 * N, # 0.1% infecciosos
R = 0.8 * N # 85.8% recuperados
)
# Verificar que suman N
cat("Población total inicial:", sum(initial_state), "\n")## Población total inicial: 1e+06
# Tiempo de simulación:
times <- seq(0, 365, by = 0.5)
# Resolver el sistema de ecuaciones diferenciales
output <- ode(
y = initial_state,
times = times,
func = seir_demographics,
parms = parameters
)
# Convertir a data frame
output_df <- as.data.frame(output)
# Calcular proporciones
output_df <- output_df %>%
mutate(
S_prop = S / N,
E_prop = E / N,
I_prop = I / N,
R_prop = R / N,
N_total = S + E + I + R
)
# Mostrar primeras filas
head(output_df)## time S E I R S_prop E_prop I_prop
## 1 0.0 198000.0 1000.000 1000.0000 800000.0 0.1980000 0.001000000 0.0010000000
## 2 0.5 200005.8 1156.158 995.2348 797842.9 0.2000058 0.001156158 0.0009952348
## 3 1.0 202003.1 1304.566 1000.5752 795691.8 0.2020031 0.001304566 0.0010005752
## 4 1.5 203989.8 1447.860 1014.8628 793547.4 0.2039898 0.001447860 0.0010148628
## 5 2.0 205964.0 1588.330 1037.1843 791410.5 0.2059640 0.001588330 0.0010371843
## 6 2.5 207923.6 1727.986 1066.8296 789281.5 0.2079236 0.001727986 0.0010668296
## R_prop N_total
## 1 0.8000000 1e+06
## 2 0.7978429 1e+06
## 3 0.7956918 1e+06
## 4 0.7935474 1e+06
## 5 0.7914105 1e+06
## 6 0.7892815 1e+06
ggplot(output_df, aes(x = time)) +
geom_line(aes(y = S_prop, color = "Susceptibles"), linewidth = 1.2) +
geom_line(aes(y = E_prop, color = "Expuestos"), linewidth = 1.2) +
geom_line(aes(y = I_prop, color = "Infecciosos"), linewidth = 1.2) +
geom_line(aes(y = R_prop, color = "Recuperados"), linewidth = 1.2) +
scale_color_manual(values = c(
"Susceptibles" = "#3498db",
"Expuestos" = "#f39c12",
"Infecciosos" = "#e74c3c",
"Recuperados" = "#2ecc71"
)) +
labs(
title = "Modelo SEIR con Demografía y Pérdida de Inmunidad",
subtitle = paste0("R₀ = ", round(R0, 2)),
x = "Tiempo (días)",
y = "Proporción de la población",
color = "Compartimento"
) +
theme_minimal(base_size = 14) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(size = 12),
panel.grid.minor = element_blank(),
legend.title = element_text(face = "bold")
)ggplot(output_df, aes(x = time, y = I_prop)) +
geom_line(color = "#e74c3c", linewidth = 1.5) +
labs(
title = "Oscilaciones Endémicas en el Compartimento Infeccioso",
x = "Tiempo (años)",
y = "Proporción de infecciosos (I)"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
panel.grid.minor = element_blank()
)# Encontrar picos locales en el compartimento infeccioso
picos <- findpeaks(
output_df$I_prop,
minpeakheight = max(output_df$I_prop) * 0.2
)
if (!is.null(picos)) {
indices_picos <- picos[, 2]
tiempos_picos <- output_df$time[indices_picos]
# Calcular períodos entre picos
if (length(tiempos_picos) > 1) {
periodos <- diff(tiempos_picos)
cat("=== ANÁLISIS DE PERIODICIDAD ===\n")
cat("Número de picos detectados:", length(tiempos_picos), "\n")
cat("Tiempos de los picos (días):\n")
print(round(tiempos_picos, 2))
cat("\nPeríodos entre picos (días):\n")
print(round(periodos, 2))
cat("\nPeríodo promedio:", round(mean(periodos), 2), "días\n")
cat("Desviación estándar:", round(sd(periodos), 2), "días\n")
}
}## === ANÁLISIS DE PERIODICIDAD ===
## Número de picos detectados: 4
## Tiempos de los picos (días):
## [1] 46 141 236 331
##
## Períodos entre picos (días):
## [1] 95 95 95
##
## Período promedio: 95 días
## Desviación estándar: 0 días
ggplot(output_df, aes(x = time, y = I_prop)) +
geom_line(color = "#e74c3c", linewidth = 1.2) +
geom_point(
data = output_df[indices_picos, ],
aes(x = time, y = I_prop),
color = "#c0392b",
size = 4,
shape = 21,
fill = "yellow",
stroke = 2
) +
labs(
title = "Oscilaciones Endémicas con Picos Marcados",
subtitle = paste0("Período promedio (días): ", round(mean(periodos), 2)),
x = "Tiempo (días)",
y = "Proporción de infecciosos (I)"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(size = 12),
panel.grid.minor = element_blank()
)