###############################################################
# Script en R (no RMarkdown): Convolución, Modelos Individual/
# Colectivo, Estimación de severidad y frecuencia, VaR y Primas
# — Totalmente comentado para que puedas copiar/pegar y correr —
###############################################################
#--------------------------------------------------------------
# 0) Preparación: semillas y paquetes
#--------------------------------------------------------------
set.seed(123)
need <- c("ggplot2","dplyr","tibble","fitdistrplus","MASS")
to_install <- setdiff(need, rownames(installed.packages()))
if (length(to_install)) install.packages(to_install, quiet = TRUE)
invisible(lapply(need, library, character.only = TRUE))
##
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
## Warning: package 'fitdistrplus' was built under R version 4.4.3
## Cargando paquete requerido: MASS
## Warning: package 'MASS' was built under R version 4.4.3
##
## Adjuntando el paquete: 'MASS'
## The following object is masked from 'package:dplyr':
##
## select
## Cargando paquete requerido: survival
#--------------------------------------------------------------
# 1) Teorema de Convolución (demostración por simulación)
# Idea: Si X,Y ~ Exp(λ) iid, entonces S=X+Y ~ Gamma(k=2, rate=λ)
# f_S = f_X * f_Y (convolución); lo verificamos con hist + curva teórica
#--------------------------------------------------------------
lambda <- 0.8
n_sim_conv <- 1e5
x1 <- rexp(n_sim_conv, rate=lambda)
x2 <- rexp(n_sim_conv, rate=lambda)
s <- x1 + x2
# Comparación empírica vs teórica Gamma(k=2, rate=lambda)
hist(s, prob=TRUE, breaks=100, col="gray90", border="white",
main="Convolución: Exp(λ)+Exp(λ) ≈ Gamma(2, λ)", xlab="Suma")
curve(dgamma(x, shape=2, rate=lambda), add=TRUE, lwd=2)

# (Opcional) chequeo por integral numérica de la convolución:
f_conv_num <- function(s, lambda){
if (s <= 0) return(0)
integrand <- function(x) lambda*exp(-lambda*x) * lambda*exp(-lambda*(s-x))
integrate(integrand, lower=0, upper=s)$value
}
grid <- seq(0.05, 6, by=0.05)
num <- sapply(grid, f_conv_num, lambda=lambda)
theo <- dgamma(grid, shape=2, rate=lambda)
max_abs_err <- max(abs(num - theo))
cat("\n[Convolución] Máx. error abs (integral vs Gamma teórica):", round(max_abs_err, 6), "\n")
##
## [Convolución] Máx. error abs (integral vs Gamma teórica): 0
#--------------------------------------------------------------
# 2) Datos "juguete" para riesgo:
# - Severidad: cars$dist (positivos)
# - Frecuencia: InsectSprays$count (conteos)
# Nota: Son datasets de R usados solo con fines pedagógicos.
#--------------------------------------------------------------
sev_raw <- datasets::cars$dist
freq_raw <- datasets::InsectSprays$count
# Limpieza simple
sev <- sev_raw[is.finite(sev_raw) & sev_raw > 0]
freq <- freq_raw[is.finite(freq_raw) & freq_raw >= 0]
cat("\nResumen severidad (cars$dist):\n"); print(summary(sev))
##
## Resumen severidad (cars$dist):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.00 26.00 36.00 42.98 56.00 120.00
cat("\nResumen frecuencia (InsectSprays$count):\n"); print(summary(freq))
##
## Resumen frecuencia (InsectSprays$count):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 3.00 7.00 9.50 14.25 26.00
#--------------------------------------------------------------
# 3) Estimación de severidad (Gamma, Lognormal, Weibull) por MLE
# Selección por AIC/BIC. Guardamos la mejor para usar luego.
#--------------------------------------------------------------
fits_sev <- list(
gamma = fitdistrplus::fitdist(sev, "gamma"),
lnorm = fitdistrplus::fitdist(sev, "lnorm"),
weibull = fitdistrplus::fitdist(sev, "weibull")
)
aic_tbl <- tibble::tibble(
modelo = names(fits_sev),
AIC = sapply(fits_sev, AIC),
BIC = sapply(fits_sev, BIC)
)
aic_tbl <- dplyr::arrange(aic_tbl, AIC)
cat("\nComparación de severidad por AIC/BIC:\n"); print(aic_tbl)
##
## Comparación de severidad por AIC/BIC:
## # A tibble: 3 × 3
## modelo AIC BIC
## <chr> <dbl> <dbl>
## 1 weibull 461. 465.
## 2 gamma 463. 467.
## 3 lnorm 473. 477.
best_sev_name <- aic_tbl$modelo[1]
best_sev <- fits_sev[[best_sev_name]]
cat("\nMejor modelo de severidad por AIC:", best_sev_name, "\n")
##
## Mejor modelo de severidad por AIC: weibull
print(best_sev)
## Fitting of the distribution ' weibull ' by maximum likelihood
## Parameters:
## estimate Std. Error
## shape 1.723925 0.1908716
## scale 48.148193 4.1547159
# Momentos de la severidad estimada: E[X], Var[X]
EX <- switch(best_sev_name,
"gamma" = with(as.list(best_sev$estimate), shape/rate),
"lnorm" = with(as.list(best_sev$estimate), exp(meanlog + 0.5*sdlog^2)),
"weibull" = with(as.list(best_sev$estimate), scale*gamma(1 + 1/shape))
)
VarX <- switch(best_sev_name,
"gamma" = with(as.list(best_sev$estimate), shape/(rate^2)),
"lnorm" = with(as.list(best_sev$estimate), (exp(sdlog^2)-1)*exp(2*meanlog + sdlog^2)),
"weibull" = with(as.list(best_sev$estimate), scale^2*(gamma(1+2/shape) - (gamma(1+1/shape))^2))
)
cat("\nMomentos de severidad estimada: E[X] y Var[X]\n")
##
## Momentos de severidad estimada: E[X] y Var[X]
print(c(EX=EX, VarX=VarX, SDX=sqrt(VarX)))
## EX VarX SDX
## 42.92058 658.40777 25.65946
#--------------------------------------------------------------
# 4) Estimación de frecuencia (Poisson vs NegBin) por MLE
# Selección por AIC. Calculamos E[N] y Var[N] del modelo elegido.
#--------------------------------------------------------------
# Poisson
fit_pois <- MASS::fitdistr(freq, "Poisson")
loglik_pois <- fit_pois$loglik
k_pois <- length(fit_pois$estimate)
AIC_pois <- 2*k_pois - 2*loglik_pois
# NegBin (param: size y mu)
fit_nb <- MASS::fitdistr(freq, "Negative Binomial")
## Warning in densfun(x, parm[1], parm[2], ...): Se han producido NaNs
loglik_nb <- fit_nb$loglik
k_nb <- length(fit_nb$estimate)
AIC_nb <- 2*k_nb - 2*loglik_nb
freq_comp <- tibble::tibble(
modelo=c("poisson","negbin"),
AIC=c(AIC_pois, AIC_nb),
params = c(paste(names(fit_pois$estimate), round(fit_pois$estimate,3), collapse=", "),
paste(names(fit_nb$estimate), round(fit_nb$estimate,3), collapse=", "))
) |> dplyr::arrange(AIC)
cat("\nComparación de frecuencia por AIC:\n"); print(freq_comp)
##
## Comparación de frecuencia por AIC:
## # A tibble: 2 × 3
## modelo AIC params
## <chr> <dbl> <chr>
## 1 negbin 472. size 1.736, mu 9.5
## 2 poisson 677. lambda 9.5
if (AIC_pois <= AIC_nb){
freq_model <- "poisson"
lambda_hat <- as.numeric(fit_pois$estimate) # E[N], Var[N]=λ
EN <- lambda_hat
VarN <- lambda_hat
} else {
freq_model <- "negbin"
size_hat <- fit_nb$estimate[["size"]]
mu_hat <- fit_nb$estimate[["mu"]]
# Para NegBin parametrizada con (size, mu): E[N]=mu; Var[N]=mu + mu^2/size
EN <- mu_hat
VarN <- mu_hat + mu_hat^2/size_hat
}
cat("\nModelo de frecuencia elegido:", freq_model, "\n")
##
## Modelo de frecuencia elegido: negbin
print(c(EN=EN, VarN=VarN))
## EN VarN
## 9.50000 61.48668
#--------------------------------------------------------------
# 5) Modelo Individual: S = X1 + ... + Xm (iid)
# E[S]=m E[X], Var[S]=m Var[X]
# Estimamos también VaR por simulación.
#--------------------------------------------------------------
m <- 5L
ES_ind <- m * EX
VarS_ind <- m * VarX
SDS_ind <- sqrt(VarS_ind)
# Función generadora de severidad según el modelo elegido
rsev <- switch(best_sev_name,
"gamma" = function(n) rgamma(n, shape = best_sev$estimate["shape"], rate = best_sev$estimate["rate"]),
"lnorm" = function(n) rlnorm(n, meanlog = best_sev$estimate["meanlog"], sdlog = best_sev$estimate["sdlog"]),
"weibull" = function(n) rweibull(n, shape = best_sev$estimate["shape"], scale = best_sev$estimate["scale"])
)
# VaR por simulación
nsim_ind <- 2e5
S_ind_sim <- replicate(nsim_ind, sum(rsev(m)))
VaR_ind_95 <- unname(quantile(S_ind_sim, 0.95))
VaR_ind_99 <- unname(quantile(S_ind_sim, 0.99))
cat("\nModelo INDIVIDUAL (m=5): métricas\n")
##
## Modelo INDIVIDUAL (m=5): métricas
print(c(E_S=ES_ind, Var_S=VarS_ind, SD_S=SDS_ind, VaR_95=VaR_ind_95, VaR_99=VaR_ind_99))
## E_S Var_S SD_S VaR_95 VaR_99
## 214.60288 3292.03883 57.37629 314.95238 363.19191
#--------------------------------------------------------------
# 6) Modelo Colectivo: S = sum_{i=1}^N X_i
# Fórmulas: E[S]=E[N]E[X]
# Var[S]=E[N]Var[X] + Var[N]*(E[X])^2
# Validamos con simulación y estimamos VaR.
#--------------------------------------------------------------
ES_col <- EN * EX
VarS_col <- EN * VarX + VarN * EX^2
SDS_col <- sqrt(VarS_col)
# Simulación de N según frecuencia elegida
rfreq <- if (freq_model=="poisson") {
function(n) rpois(n, lambda=lambda_hat)
} else {
function(n) rnbinom(n, size=size_hat, mu=mu_hat)
}
simulate_aggregate <- function(nsim, rfreq, rsev){
# Devuelve nsim simulaciones de S = sum_{i=1}^N X_i
S <- numeric(nsim)
N <- rfreq(nsim)
for (i in seq_len(nsim)){
ni <- N[i]
S[i] <- if (ni==0) 0 else sum(rsev(ni))
}
S
}
nsim_col <- 2e5
S_col_sim <- simulate_aggregate(nsim_col, rfreq, rsev)
VaR_col_95 <- unname(quantile(S_col_sim, 0.95))
VaR_col_99 <- unname(quantile(S_col_sim, 0.99))
cat("\nModelo COLECTIVO: métricas teóricas y VaR simulado\n")
##
## Modelo COLECTIVO: métricas teóricas y VaR simulado
print(c(E_S=ES_col, SD_S=SDS_col, VaR_95=VaR_col_95, VaR_99=VaR_col_99))
## E_S SD_S VaR_95 VaR_99
## 407.7455 345.7226 1082.2848 1561.9209
#--------------------------------------------------------------
# 7) Primas técnicas bajo distintos principios
# - Valor esperado: Π_EV = (1+θ) E[S]
# - Varianza: Π_VAR = E[S] + β Var[S]
# - Desviación estándar: Π_SD = E[S] + k SD[S]
# - Exponencial (Esscher aprox por MC): (1/α) log E[e^{αS}]
# - VaR (cuantil): Π_VaR_q = VaR_q(S) (como referencia de cola)
#--------------------------------------------------------------
theta <- 0.30 # recargo del 30% sobre el valor esperado
beta <- 0.001 # peso para la varianza
k <- 0.8 # peso para la desviación típica
alpha <- 0.002 # aversión al riesgo (prima exponencial)
Pi_EV <- (1+theta) * ES_col
Pi_VAR <- ES_col + beta * VarS_col
Pi_SD <- ES_col + k * SDS_col
# Prima "exponencial" estable numéricamente (centrando en media)
Pi_EXP <- (1/alpha) * log(mean(exp(alpha*(S_col_sim - mean(S_col_sim))))) + mean(S_col_sim)
# Prima basada en VaR al 99% (solo en referencia)
Pi_VaR99 <- VaR_col_99
cat("\nPRIMAS (modelo COLECTIVO):\n")
##
## PRIMAS (modelo COLECTIVO):
print(
tibble::tibble(
Principio = c("Valor esperado","Varianza","Desv. estándar","Exponencial (MC)","VaR 99%"),
Parametros = c(paste0("theta=",theta),
paste0("beta=",beta),
paste0("k=",k),
paste0("alpha=",alpha),
"q=0.99"),
Prima = c(Pi_EV, Pi_VAR, Pi_SD, Pi_EXP, Pi_VaR99)
) |> dplyr::arrange(Prima)
)
## # A tibble: 5 × 3
## Principio Parametros Prima
## <chr> <chr> <dbl>
## 1 Varianza beta=0.001 527.
## 2 Valor esperado theta=0.3 530.
## 3 Exponencial (MC) alpha=0.002 595.
## 4 Desv. estándar k=0.8 684.
## 5 VaR 99% q=0.99 1562.
#--------------------------------------------------------------
# 8) Comparación rápida Individual vs Colectivo (métricas clave)
#--------------------------------------------------------------
cat("\nComparación INDIVIDUAL vs COLECTIVO:\n")
##
## Comparación INDIVIDUAL vs COLECTIVO:
print(
tibble::tibble(
Modelo = c("Individual (m=5)","Colectivo"),
E_S = c(ES_ind, ES_col),
SD_S = c(SDS_ind, SDS_col),
VaR_95 = c(VaR_ind_95, VaR_col_95),
VaR_99 = c(VaR_ind_99, VaR_col_99)
)
)
## # A tibble: 2 × 5
## Modelo E_S SD_S VaR_95 VaR_99
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 Individual (m=5) 215. 57.4 315. 363.
## 2 Colectivo 408. 346. 1082. 1562.
#--------------------------------------------------------------
# 9) Paso a paso (comentarios):
# - Ajustamos severidad (Gamma/LN/Weibull) y elegimos por AIC.
# - Ajustamos frecuencia (Poisson/NegBin) y elegimos por AIC.
# - Calculamos E[X], Var[X], E[N], Var[N].
# - Modelo COLECTIVO: E[S], Var[S]; validamos VaR por simulación.
# - Calculamos primas con distintos principios (parámetros editables).
# - Ajusta theta, beta, k, alpha según política y riesgo deseado.
#--------------------------------------------------------------