ESTUDIAR ESTADÍSTICA.

Razones académicas

Lenguaje de la ciencia: casi todas las disciplinas (medicina, biología, economía, psicología, ingeniería, ciencias sociales) utilizan la estadística para validar hipótesis y analizar datos.

Pensamiento crítico: enseña a interpretar la incertidumbre, a diferenciar entre correlación y causalidad, y a evaluar la validez de los resultados.

Base matemática sólida: combina probabilidad, cálculo, álgebra y métodos computacionales, lo que fortalece la formación matemática.

💼 Razones profesionales

Alta demanda laboral: en la era del big data, las empresas necesitan personas que sepan transformar datos en decisiones.

Versatilidad: un estadístico puede trabajar en salud, finanzas, educación, industria, inteligencia artificial, política pública, consultoría, investigación, etc.

Complemento ideal: se puede aplicar en conjunto con otras áreas: bioestadística, econometría, ciencia de datos, machine learning, analítica de negocios.

Impacto social

Mejora de políticas públicas: permite evaluar programas sociales, económicos y de salud.

Toma de decisiones informadas: en un mundo lleno de información, ayuda a separar la evidencia real de la desinformación.

Aporta al progreso científico y tecnológico: cada avance en vacunas, genética, telecomunicaciones o inteligencia artificial depende de modelos estadísticos.

Razones personales

Formación en lógica y razonamiento: entrenar la mente a pensar con rigor.

Capacidad de adaptación: el estadístico aprende a trabajar con distintos tipos de datos y problemas.

Curiosidad: te permite responder preguntas sobre fenómenos complejos de la vida diaria.

OPTIMIZACIÓN

La optimización, explicada en términos sencillos, es como buscar la mejor opción posible dentro de muchas alternativas. Imagina que tienes varias rutas para ir a casa: unas son más rápidas, otras más baratas, otras más bonitas. La optimización es el proceso de poner en números esas opciones (tiempo, costo, comodidad) y luego aplicar reglas matemáticas para decidir cuál es la “mejor” de acuerdo con tu objetivo. Por ejemplo, si quieres gastar menos dinero, la optimización te ayuda a encontrar la ruta más barata; si quieres llegar más rápido, te señala el camino más corto. En esencia, es una herramienta que combina matemáticas y lógica para tomar decisiones inteligentes con los recursos que tienes y las metas que persigues.

# ============================
# Markowitz Diversificación en R (sin ventas en corto)
# ============================

# Paquetes (descomenta instalación si hace falta)
# install.packages(c("quadprog", "ggplot2", "dplyr", "scales"))
# Opcional para datos reales:
# install.packages(c("quantmod", "PerformanceAnalytics"))

library(quadprog)
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## 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
library(scales)
## Warning: package 'scales' was built under R version 4.4.3
# ----------------------------
# 0) DATOS
# ----------------------------
# Opción A: Datos sintéticos (siempre funciona)
set.seed(123)
n_assets <- 4
n_obs <- 750   # ~ 3 años de datos diarios
Sigma_true <- matrix(c(
  0.040, 0.018, 0.012, 0.010,
  0.018, 0.035, 0.014, 0.011,
  0.012, 0.014, 0.030, 0.009,
  0.010, 0.011, 0.009, 0.025
), n_assets, n_assets)
mu_true <- c(0.12, 0.10, 0.09, 0.08) / 252 # retorno esperado diario aprox

# Simula rendimientos diarios ~ N(mu, Sigma)
R <- MASS::mvrnorm(n = n_obs, mu = mu_true, Sigma = Sigma_true) %>% as.data.frame()
colnames(R) <- paste0("Asset_", seq_len(n_assets))

# --- Opción B: Datos reales (requiere internet) ---
# library(quantmod)
# library(PerformanceAnalytics)
# tickers <- c("AAPL","MSFT","AMZN","GOOGL")
# getSymbols(tickers, from = "2020-01-01", src = "yahoo")
# prices <- do.call(merge, lapply(tickers, function(t) Ad(get(t))))
# R_xts <- na.omit(ROC(prices, type = "discrete"))
# R <- data.frame(R_xts)
# colnames(R) <- tickers

# ----------------------------
# 1) ESTADÍSTICOS BÁSICOS
# ----------------------------
mu <- colMeans(R)                 # vector de medias
Sigma <- cov(R)                   # matriz de covarianzas
ones <- rep(1, ncol(R))
rf <- 0.00/252                    # tasa libre de riesgo diaria (ajusta si quieres)

stopifnot(all(is.finite(mu)), all(is.finite(Sigma)))

# ----------------------------
# 2) QP helpers (sin ventas en corto: w >= 0)
# ----------------------------
# Min varianza sujeto a:
#   sum(w) = 1
#   mu' w = target_return   (opcional cuando buscamos un punto en la frontera)
#   w >= 0
solve_minvar_target <- function(Sigma, mu, target_return = NULL) {
  n <- length(mu)
  Dmat <- 2 * as.matrix(Sigma)
  dvec <- rep(0, n)

  # Igualdades como desigualdades de igualdad (Quadprog usa Amat' w >= bvec)
  # Usamos restricciones:
  # 1) sum(w) = 1  -> implementada con dos: sum(w) >= 1 y -sum(w) >= -1
  # 2) mu' w = R*  -> dos:  mu' w >= R*,  -mu' w >= -R*
  # 3) w >= 0

  Aeq1 <- matrix(ones, n, 1)
  Aeq2 <- matrix(mu,   n, 1)

  Amat <- cbind(
    diag(n),           # w_i >= 0
    Aeq1,              #  sum(w) >= 1
    -Aeq1              # -sum(w) >= -1
  )
  bvec <- c(rep(0, n), 1, -1)

  meq <- 0

  if (!is.null(target_return)) {
    Amat <- cbind(Amat,  Aeq2, -Aeq2)
    bvec <- c(bvec, target_return, -target_return)
  }

  sol <- solve.QP(Dmat, dvec, Amat, bvec, meq = meq)
  as.numeric(sol$solution)
}

# GMV: minimizar varianza con sum(w)=1, w>=0
solve_gmv <- function(Sigma) {
  n <- ncol(Sigma)
  Dmat <- 2 * as.matrix(Sigma)
  dvec <- rep(0, n)
  Amat <- cbind(
    diag(n),          # w >= 0
    matrix(1, n, 1),  # sum(w) >= 1
    -matrix(1, n, 1)  # -sum(w) >= -1
  )
  bvec <- c(rep(0, n), 1, -1)
  meq <- 0
  sol <- solve.QP(Dmat, dvec, Amat, bvec, meq = meq)
  as.numeric(sol$solution)
}

# ----------------------------
# 3) Construir la Frontera Eficiente
# ----------------------------
# Barrer retornos objetivo entre min y max alcanzables (sin short)
ret_grid <- seq(
  from = max(min(mu) * 0.5, quantile(mu, 0.15)),
  to   = max(mu) * 1.4,
  length.out = 60
)

frontier <- lapply(ret_grid, function(rt) {
  w <- tryCatch(solve_minvar_target(Sigma, mu, rt), error = function(e) rep(NA_real_, length(mu)))
  if (any(is.na(w))) return(NULL)
  list(
    target = rt,
    ret = sum(w * mu),
    sd  = sqrt(t(w) %*% Sigma %*% w),
    w   = w
  )
}) %>% purrr::compact()

front_df <- do.call(rbind, lapply(frontier, function(x) data.frame(target = x$target, ret = x$ret, sd = as.numeric(x$sd))))
# Marcar puntos eficientes (crecientes en retorno para una misma sd)
front_df <- front_df %>% arrange(sd)

# ----------------------------
# 4) Portafolios especiales
# ----------------------------
# GMV
w_gmv <- solve_gmv(Sigma)
ret_gmv <- sum(w_gmv * mu)
sd_gmv  <- sqrt(as.numeric(t(w_gmv) %*% Sigma %*% w_gmv))

# Portafolio tangente (máx. Sharpe) aproximado sobre la frontera construida
sharpe_vec <- (front_df$ret - rf) / front_df$sd
idx_tan <- which.max(sharpe_vec)
ret_tan <- front_df$ret[idx_tan]
sd_tan  <- front_df$sd[idx_tan]
# Recalcular pesos exactos en ese target (para reportar)
w_tan <- solve_minvar_target(Sigma, mu, target_return = ret_tan)

# ----------------------------
# 5) Gráfico
# ----------------------------
p <- ggplot(front_df, aes(x = sd, y = ret)) +
  geom_path(size = 1) +
  geom_point(aes(x = sd_gmv,  y = ret_gmv), size = 3) +
  geom_point(aes(x = sd_tan,  y = ret_tan), size = 3) +
  geom_text(aes(x = sd_gmv, y = ret_gmv, label = "GMV"), nudge_y = 0.0005) +
  geom_text(aes(x = sd_tan, y = ret_tan, label = "Tangente"), nudge_y = 0.0005) +
  labs(
    title = "Frontera Eficiente de Markowitz (sin ventas en corto)",
    x = "Riesgo (desv. estándar)",
    y = "Retorno esperado"
  ) +
  scale_x_continuous(labels = percent_format(accuracy = 0.1)) +
  scale_y_continuous(labels = percent_format(accuracy = 0.1)) +
  theme_minimal(base_size = 12)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
print(p)
## Warning in geom_point(aes(x = sd_gmv, y = ret_gmv), size = 3): All aesthetics have length 1, but the data has 52 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_point(aes(x = sd_tan, y = ret_tan), size = 3): All aesthetics have length 1, but the data has 52 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_text(aes(x = sd_gmv, y = ret_gmv, label = "GMV"), nudge_y = 5e-04): All aesthetics have length 1, but the data has 52 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_text(aes(x = sd_tan, y = ret_tan, label = "Tangente"), nudge_y = 5e-04): All aesthetics have length 1, but the data has 52 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.

# ----------------------------
# 6) Reporte de pesos
# ----------------------------
asset_names <- colnames(R)
resumen <- data.frame(
  Activo = asset_names,
  Pesos_GMV = round(w_gmv, 4),
  Pesos_Tangente = round(w_tan, 4)
)

cat("\n--- Pesos GMV ---\n")
## 
## --- Pesos GMV ---
print(resumen[, c("Activo", "Pesos_GMV")], row.names = FALSE)
##   Activo Pesos_GMV
##  Asset_1    0.1495
##  Asset_2    0.1630
##  Asset_3    0.2858
##  Asset_4    0.4017
cat("\n--- Pesos Portafolio Tangente ---\n")
## 
## --- Pesos Portafolio Tangente ---
print(resumen[, c("Activo", "Pesos_Tangente")], row.names = FALSE)
##   Activo Pesos_Tangente
##  Asset_1         0.9983
##  Asset_2         0.0000
##  Asset_3         0.0000
##  Asset_4         0.0017
# Retornos y riesgos
cat("\n--- Métricas ---\n")
## 
## --- Métricas ---
cat(sprintf("GMV: Ret=%.4f%%  SD=%.4f%%\n", 100 * ret_gmv, 100 * sd_gmv))
## GMV: Ret=-0.1890%  SD=12.6989%
cat(sprintf("Tangente: Ret=%.4f%%  SD=%.4f%%  Sharpe=%.4f\n",
            100 * ret_tan, 100 * sd_tan, (ret_tan - rf)/sd_tan))
## Tangente: Ret=0.1323%  SD=19.6829%  Sharpe=0.0067
library(plotly)
## Warning: package 'plotly' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
# Datos de población por país
df <- data.frame(
  country = c("Brazil", "Colombia", "Argentina", "Chile", "Peru"),
  value = c(210, 50, 45, 19, 32) # población (millones)
)

# Mapa interactivo
fig <- plot_geo(df) %>%
  add_trace(
    z = ~value,
    color = ~value,
    colors = "Blues",
    text = ~country,
    locations = ~country,
    locationmode = "country names"
  ) %>%
  colorbar(title = "Población (millones)")

fig