Ejercicio 1

1.1 Realicen una exploración inicial de la base de datos. Para esto, para cada una de las bases de datos describan la misma. Es decir, redacten un breve párrafo comentando qué variables contiene la base de datos, el número de observaciones, el periodo de tiempo que comprende, el tipo de las variables, entre otros aspectos relevantes

Explorando las bases de datos de los precios de forma individual identifican que no todas tienen el mismo número de observaciones. Un hecho muy común cuando se trabaja con datos en formato panel es que algunas fechas para algunos productos no estén en la base de datos, es decir, hay fechas faltantes. A continuación, ustedes quieren “explicitar” las fechas faltantes.

# Definir directorio de trabajo y cargar bases
setwd("C:/Users/s05e07/Downloads/Info")

# Cargar la base de IPC con el separador correcto y cambiar el formato decimal
ipc_df <- read_delim("IPC.csv", delim = ",", col_types = cols()) %>%
  rename(fecha = 1, ipc = 2) %>%                  # Renombrar columnas
  mutate(ipc = as.numeric(gsub(",", ".", ipc)))   # Convertir IPC a numérico

# Guardar el archivo con el nuevo formato
write_csv(ipc_df, "IPC_formateado.csv")

# Cargar archivos
gasolina <- read.csv("clean_precios_gasolina.csv", stringsAsFactors = FALSE) %>% 
  rename(fecha = 1, Gasolina = 2)
petroleo <- read.csv("clean_precios_petroleo.csv", stringsAsFactors = FALSE) %>% 
  rename(fecha = 1, Petroleo = 2)
ipc <- read.csv("IPC_formateado.csv", stringsAsFactors = FALSE) %>% 
  rename(fecha = 1, IPC = 2)
carbon <- read.csv("clean_precios_carbon.csv", stringsAsFactors = FALSE) %>% 
  rename(fecha = 1, Carbon = 2)
gasnatural <- read.csv("clean_precios_gas_natural.csv", stringsAsFactors = FALSE) %>% 
  rename(fecha = 1, GasNatural = 2)

1.2 Creen una función que tenga como parámetros un dataframe, una columna de fecha y una columna de precio, posteriormente, la función tiene que agregar las fechas faltantes en la columna de fecha y añadir un missing value en su valor correspondiente en el precio para esta fecha explicitada. Finalmente, tiene que retornar el dataframe con las fechas completas.

# Función para completar fechas faltantes
fecha_completa <- function(df, date_col, price_col) {
  df[[date_col]] <- as.Date(df[[date_col]])
  full_dates <- data.frame(seq(min(df[[date_col]], na.rm = TRUE), 
                               max(df[[date_col]], na.rm = TRUE), by = "day"))
  colnames(full_dates) <- date_col
  df_complete <- full_dates %>% 
    left_join(df, by = date_col) %>% 
    arrange(.data[[date_col]])
  return(df_complete)
}

1.3 Apliquen la función anterior a cada una de las bases de precios.

# Se aplica la función a cada base
gasolina_completa <- fecha_completa(gasolina, "fecha", "Gasolina")
petroleo_completa <- fecha_completa(petroleo, "fecha", "Petroleo")
ipc_completa <- fecha_completa(ipc, "fecha", "IPC")
carbon_completa <- fecha_completa(carbon, "fecha", "Carbon")
gasnatural_completa <- fecha_completa(gasnatural, "fecha", "GasNatural")

1.4 Realicen una unión de cada una de los datasets cargados que les permita juntar los precios y fechas en una misma base de datos.

# Unir bases con formato de columnas separadas por combustible
base_unida <- reduce(list(gasolina_completa, petroleo_completa, ipc_completa, carbon_completa, gasnatural_completa), 
                     full_join, by = "fecha") %>% 
  arrange(fecha)

1.5 Utilizando las funciones de la librería lubridate, creen respectivamente columnas que correspondan al mes y año de las observaciones (revisen la documentación de: Make Dealing with Dates a Little Easier⚫ lubridate (tidyverse.org) y lubridate (rawgit.com)).

# Crear columnas de mes y año
base_unida <- base_unida %>% 
  mutate(mes = month(fecha),
         año = year(fecha))

# Teniendo en cuenta  que la base IPC contiene datos desde 1954, debemos filtrar la base
# para tener solo datos desde enero de 2000 hasta enero de 2024
base_unida <- base_unida %>% 
  filter(año >= 2000 & (año < 2024 | (año == 2024 & mes == 1)))

1.6 Revisen y presenten sus resultados en una tabla: ¿Qué columnas y en qué porcentajes de la base de datos cuenta con valores faltantes en el precio de algún bien? Sustituyan estos valores faltantes con el precio promedio del mes -año de ese bien.

# Identificar valores faltantes en el precio de algún combustible (se deja por fuera 
# el año, mes y el IPC ya que este es mensual y mostraría un porcentaje alto)
valores_faltantes <- base_unida %>% 
  summarise(across(where(is.numeric) & !all_of(c("año", "mes", "IPC")), ~mean(is.na(.)) * 100)) %>% 
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "% faltante")

# Tabla Valores Faltantes
valores_faltantes %>% 
  kable("html", caption = "Porcentaje de valores faltantes por columna") %>% 
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
Porcentaje de valores faltantes por columna
Variable % faltante
Gasolina 2.478118
Petroleo 2.478118
Carbon 2.478118
GasNatural 2.478118
# Agregar valores faltantes con el promedio mensual por combustible
base_unida <- base_unida %>% 
  group_by(año, mes) %>% 
  mutate(across(where(is.numeric), ~ifelse(is.na(.), mean(., na.rm = TRUE), .))) %>% 
  ungroup()

1.7 Agreguen la información de los precios diarios a valores promedios por mes y año. Además guarde la base ajustada hasta aquí solo para valores entre enero de 2000 hasta enero de 2024.

# Agregar información a valores promedios por mes y año
base_final_meses <- base_unida %>% 
  group_by(año, mes) %>% 
  summarise(across(where(is.numeric), mean, na.rm = TRUE)) %>% 
  ungroup()
## Warning: There was 1 warning in `summarise()`.
## i In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
## i In group 1: `año = 2000` `mes = 1`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
## `summarise()` has grouped output by 'año'. You can override using the `.groups`
## argument.

1.8 Creen una función que tenga como parámetros: una columna de una serie de un precio de la base de datos, un año, un mes y la columna del IPC de Colombia. Posteriormente, la función debe crear una nueva columna cuvo nombre sea: el “nombre del bien” + “ano base” + “mes base” + el sufijo ” transformada”. Esta columna debe ser la transformación de valores nominales de la serie, a valores reales con base en el año-mes que toma como parámetro la función. Es decir, utilizando la ecuación (1) tendrán que consolidar una función que transforme los valores nominales en precios constantes con base en cualquier mes-año.

# Función para convertir precios nominales a precios reales
convert_to_real <- function(price_col, year_base, month_base, ipc_col, df) {
  ipc_base <- df %>% filter(año == year_base & mes == month_base) %>% pull({{ipc_col}})
  if (length(ipc_base) == 0 || is.na(ipc_base)) {
    stop("No se encontró un valor válido de IPC para el año-mes base especificado.")
  }
  real_price_col <- paste0(deparse(substitute(price_col)), "_", year_base, "_", month_base, "_transformada")
  df <- df %>% mutate({{real_price_col}} := {{price_col}} * ipc_base / {{ipc_col}})
  return(df)
}

1.9 Apliquen la función que desarrolló en el literal anterior para todos los bienes que definió en la base de datos, pueden usar cualquier año-mes base, lo importante es que sean explícitos. ¿Los resultados cambian si utilizan un año mes diferente?

# Aplicar función para convertir a precios reales con base en cualquier mes y año (definido)
año_base <- 2023
mes_base <- 12
basedatos_consolidada <- convert_to_real(Gasolina, año_base, mes_base, IPC, base_final_meses)
basedatos_consolidada <- convert_to_real(Petroleo, año_base, mes_base, IPC, basedatos_consolidada)
basedatos_consolidada <- convert_to_real(Carbon, año_base, mes_base, IPC, basedatos_consolidada)
basedatos_consolidada <- convert_to_real(GasNatural, año_base, mes_base, IPC, basedatos_consolidada)

1.10

# Guardar la base de datos consolidada
write.csv(basedatos_consolidada, "basedatos_consolidada_transformados.csv", row.names = FALSE)

Ejercicio 2

2.1 Presenten una tabla de estadísticas descriptivas donde evidencien el número de observaciones, promedio, min, max, desviación estándar de los precios nominales y reales de los combustibles. Expliquen e interpreten en el documento los valores más importantes de la tabla. Es decir, su tabla debe tener 5 columnas y 9 filas.

# Inicialmente se filtran solo precios nominales y reales de combustibles
combustibles <- basedatos_consolidada %>% 
  select(año, mes, starts_with("Gasolina"), starts_with("Petroleo"), starts_with("Carbon"), starts_with("GasNatural"))

# Renombramos las variables con el sufijo "real y nominal"
colnames(combustibles) <- colnames(combustibles) %>%
  str_replace("\\d{4}_\\d{2}_transformada", "") # Eliminar cualquier "AAAA_MM_transformada"

colnames(combustibles) <- ifelse(colnames(combustibles) %in% c("año", "mes"),
                                 colnames(combustibles),  # Mantener "Año" y "Mes" sin cambios
                                 ifelse(str_detect(colnames(combustibles), "_"), 
                                        paste0(colnames(combustibles), "real"), 
                                        paste0(colnames(combustibles), "_nominal")))

# Se crea una base de estadísticas "Observaciones, Promedio, min, max y desviación"
estadisticas_combustibles <- combustibles %>%
  summarise(across(c(starts_with("Gasolina"), starts_with("Petroleo"), starts_with("Carbon"), starts_with("GasNatural")), list(
    "Observaciones" = ~sum(!is.na(.)),
    "Promedio" = ~round(mean(., na.rm = TRUE),2),
    "Mínimo" = ~round(min(., na.rm = TRUE),2),
    "Máximo" = ~round(max(., na.rm = TRUE),2),
    "Desviación estándar" = ~round(sd(., na.rm = TRUE),2)
  ), .names = "{.col},{.fn}"))

# Se ajusta el formato para crear la Tabla
estadisticas_combustibles <- estadisticas_combustibles %>%
  pivot_longer(cols = everything(), names_to = c("Variable", "Estadística"), names_sep = ",") %>%
  pivot_wider(names_from = Estadística, values_from = value)

# Se crea la Tabla
estadisticas_combustibles %>% 
  kable("html", caption = "Estadísticas Descriptivas de los precios de los combustibles") %>% 
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>% 
  column_spec(1, bold = TRUE, border_right = TRUE) %>% 
  row_spec(0, bold = TRUE, background = "#D3D3D3") %>% 
  scroll_box(height = "500px")
Estadísticas Descriptivas de los precios de los combustibles
Variable Observaciones Promedio Mínimo Máximo Desviación estándar
Gasolina_nominal 289 109.17 61.65 166.10 25.77
Gasolina_real 289 193.81 150.71 238.32 14.19
Petroleo_nominal 289 108.08 91.25 129.80 7.98
Petroleo_real 289 202.28 104.86 327.85 49.29
Carbon_nominal 289 96.98 74.21 123.16 12.68
Carbon_real 289 178.23 100.96 270.05 32.82
GasNatural_nominal 289 118.27 98.65 137.71 8.37
GasNatural_real 289 221.86 97.82 366.96 55.75

2.2 - Correlaciones y gráfica detallada (Carbón vs Gasolina - reales) Presenten una gráfica de dispersión de puntos (scatter) donde en el eje X esté el precio del carbón y en el eje Y el precio de la gasolina, ambos reales, para un año determinado. Añadan una línea de ajuste lineal, título, labels y demás elementos que permitan una gráfica autocontenida. Expliquen e interpreten en el documento los elementos más importantes de gráfica.

# Paquetes (instalan si faltan)
pkgs <- c("dplyr","ggplot2","ggpmisc","ggpubr","corrplot")
for (p in pkgs) if (!requireNamespace(p, quietly = TRUE)) install.packages(p)
lapply(pkgs, require, character.only = TRUE)
## [[1]]
## [1] TRUE
## 
## [[2]]
## [1] TRUE
## 
## [[3]]
## [1] TRUE
## 
## [[4]]
## [1] TRUE
## 
## [[5]]
## [1] TRUE
# 0) Validación básica
if (!exists("basedatos_consolidada")) stop("ERROR: 'basedatos_consolidada' no existe. Ejecuta la sección que la crea.")

# 1) Detectar columnas reales de Carbón y Gasolina (varias nomenclaturas)
nombres <- names(basedatos_consolidada)

# patrones comunes
pattern_carbon <- c("carbon.*real", "carbon.*r$", "\\bcarbon\\b")
pattern_gasolina <- c("gasolin.*real", "gasolin.*r$", "\\bgasolina\\b", "gasoline.*real")

find_first <- function(patterns, names_vec) {
  for (pat in patterns) {
    hit <- grep(pat, names_vec, value = TRUE, ignore.case = TRUE)
    if (length(hit) > 0) return(hit[1])
  }
  return(NA_character_)
}

carbon_col <- find_first(pattern_carbon, nombres)
gasolina_col <- find_first(pattern_gasolina, nombres)

if (is.na(carbon_col) || is.na(gasolina_col)) {
  cat("Columnas detectadas:\n")
  cat("Carbon_col =", carbon_col, "\n")
  cat("Gasolina_col =", gasolina_col, "\n")
  stop("No se detectaron automáticamente las columnas 'reales' de Carbon o Gasolina. Ajusta los patrones o renombra tus columnas.")
}

cat("Usando columnas - Carbón (real):", carbon_col, " | Gasolina (real):", gasolina_col, "\n")
## Usando columnas - Carbón (real): Carbon  | Gasolina (real): Gasolina
# 2) Preparar datos: asegurar numéricos y eliminar NAs
df_plot <- basedatos_consolidada %>%
  select(any_of(c("año", "mes", "fecha", carbon_col, gasolina_col))) %>%
  rename(precio_carbon_real = !!sym(carbon_col),
         precio_gasolina_real = !!sym(gasolina_col)) %>%
  mutate(
    precio_carbon_real = as.numeric(precio_carbon_real),
    precio_gasolina_real = as.numeric(precio_gasolina_real),
    fecha = if("fecha" %in% names(.) ) as.Date(fecha) else NA
  ) %>%
  drop_na(precio_carbon_real, precio_gasolina_real)

if (nrow(df_plot) == 0) stop("El subconjunto para graficar quedó vacío tras eliminar NAs.")

# 3) (Opcional) Filtrar a un año concreto si quieres — aquí dejo opción interactiva:
# Si quieres fijar un año específico, asigna valor a `anio_focus`. Si NULL, se usa todo el período.
anio_focus <- NULL   # p. ej. 2020; si NULL -> no filtra por año
if (!is.null(anio_focus) && "año" %in% names(df_plot)) {
  df_plot <- df_plot %>% filter(año == anio_focus)
  if (nrow(df_plot) == 0) stop(paste("No hay datos para el año", anio_focus))
}

# 4) Estadísticas de relación: correlación y regresión lineal
# Correlación Pearson y p-value
cor_test <- cor.test(df_plot$precio_carbon_real, df_plot$precio_gasolina_real, method = "pearson")
r_val <- cor_test$estimate[[1]]
p_val <- cor_test$p.value

# Regresión lineal (para línea de ajuste y R^2)
lm_fit <- lm(precio_gasolina_real ~ precio_carbon_real, data = df_plot)
r2 <- summary(lm_fit)$r.squared
coef_int <- coef(lm_fit)[1]; coef_slope <- coef(lm_fit)[2]

# Formato de las anotaciones
lab_r <- paste0("r = ", formatC(r_val, format = "f", digits = 3))
lab_p <- paste0("p = ", formatC(p_val, format = "g", digits = 3))
lab_eq <- paste0("y = ", formatC(coef_slope, digits = 3, format = "f"), " x + ", formatC(coef_int, digits = 3, format = "f"))
lab_r2 <- paste0("R² = ", formatC(r2, digits = 3, format = "f"))

# 5) Gráfica detallada con ggplot2 + anotaciones
p <- ggplot(df_plot, aes(x = precio_carbon_real, y = precio_gasolina_real)) +
  geom_point(size = 2.5, alpha = 0.75) +
  geom_smooth(method = "lm", se = TRUE, linewidth = 0.9, color = "#D62828", fill = alpha("#D62828", 0.15)) +
  stat_poly_eq(
    aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")),
    formula = y ~ x, parse = TRUE, label.x.npc = "left", label.y.npc = 0.95,
    size = 3.8, color = "black"
  ) +
  annotate("text", x = Inf, y = -Inf, label = paste(lab_r, lab_p, sep = " | "), 
           hjust = 1.05, vjust = -1.1, size = 3.8, color = "black") +
  labs(
    title = ifelse(is.null(anio_focus), 
                   "Dispersión: Precio real del Carbón vs Precio real de la Gasolina (todo el período)",
                   paste0("Dispersión: Carbón vs Gasolina (año ", anio_focus, ")")),
    subtitle = "Puntos mensuales (o frecuencia de la serie). Línea: ajuste lineal (lm) con banda de confianza",
    x = "Precio real del Carbón (unidad de la base de datos)",
    y = "Precio real de la Gasolina (unidad de la base de datos)",
    caption = paste0("Coef. Pearson: ", formatC(r_val, digits=3), 
                     "  |  p-value: ", formatC(p_val, digits=3),
                     "  |  Ecuación: ", lab_eq, "  |  R²: ", formatC(r2, digits=3),
                     "\nFuente: basedatos_consolidada")
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 10, color = "gray30"),
    plot.caption = element_text(size = 9, color = "gray40"),
    axis.title = element_text(size = 11),
    panel.grid.minor = element_blank()
  )

# Mostrar la gráfica
print(p)

# 6) (Opcional) Guardar la imagen en el directorio del proyecto
try({
  ggsave(filename = "scatter_carbon_gasolina_reales.png", plot = p, width = 8, height = 6, dpi = 300)
  cat("Figura guardada como scatter_carbon_gasolina_reales.png\n")
}, silent = TRUE)
## Figura guardada como scatter_carbon_gasolina_reales.png
# 7) Si hay más de 2 series reales, mostrar matriz de correlación entre todas las reales
real_cols <- grep("real", nombres, value = TRUE, ignore.case = TRUE)
real_cols <- intersect(real_cols, nombres)  # garantía
if (length(real_cols) >= 2) {
  df_real_all <- basedatos_consolidada %>% select(any_of(real_cols)) %>% mutate(across(everything(), as.numeric))
  mat_cor_real <- cor(df_real_all, use = "pairwise.complete.obs", method = "pearson")
  cat("\nMatriz de correlación (series reales detectadas):\n")
  print(round(mat_cor_real, 3))
  # Mostrar heatmap pequeño
  try({
    corrplot::corrplot(mat_cor_real, method = "color", addCoef.col = "black", number.cex = 0.7,
                       tl.cex = 0.8, title = "Correlaciones - series reales", mar = c(0,0,1,0))
  }, silent = TRUE)
}

# 8) Interpretación automática breve (texto en consola; pega en tu documento si quieres)
cat("\nInterpretación breve:\n")
## 
## Interpretación breve:
cat("- r (Pearson) =", formatC(r_val, digits=3), " ; p-value =", formatC(p_val, digits=3), "\n")
## - r (Pearson) = 0.969  ; p-value = 3.11e-177
if (abs(r_val) >= 0.8) {
  cat("- Interpretación: correlación muy fuerte.\n")
} else if (abs(r_val) >= 0.6) {
  cat("- Interpretación: correlación fuerte.\n")
} else if (abs(r_val) >= 0.4) {
  cat("- Interpretación: correlación moderada.\n")
} else if (abs(r_val) >= 0.2) {
  cat("- Interpretación: correlación débil.\n")
} else {
  cat("- Interpretación: correlación muy débil o nula.\n")
}
## - Interpretación: correlación muy fuerte.

2.3 - Gráfica de dispersión entre precios reales del Carbón y la Gasolina Presenten una gráfica de serie de tiempo con el precio real del Carbón, Gas Natural, Petróleo y Gasolina Corriente para un mismo año, asegúrense de que tenga el título, labels, colores y demás elementos que permitan una gráfica autocontenida. Expliquen e interpreten en el documento los elementos más importantes de la gráfica.

# ===========================================
# PUNTO 2.3 - VERSIÓN FINAL CORREGIDA
# ===========================================

# Cargar librerías necesarias
library(dplyr)
library(ggplot2)
library(ggpmisc)

# --- 1) Seleccionar el año a analizar ---
anio_filtro <- 2020   # 🔹 Cambia el año si deseas graficar otro

# --- 2) Detectar columnas de carbón y gasolina ---
nombres <- names(basedatos_consolidada)

col_carbon <- grep("carbon", nombres, value = TRUE, ignore.case = TRUE)[1]
col_gasolina <- grep("gasolin", nombres, value = TRUE, ignore.case = TRUE)[1]

cat("Columnas detectadas:\n")
## Columnas detectadas:
cat(" - Carbón:", col_carbon, "\n")
##  - Carbón: Carbon
cat(" - Gasolina:", col_gasolina, "\n\n")
##  - Gasolina: Gasolina
if (is.na(col_carbon) || is.na(col_gasolina)) {
  stop("❌ No se detectaron columnas que contengan 'carbon' o 'gasolin' en el nombre.")
}

# --- 3) Verificar columna 'año' ---
if (!"año" %in% nombres) {
  stop("❌ No se encontró la columna 'año' en la base de datos.")
}

# --- 4) Preparar los datos ---
df_disp <- basedatos_consolidada %>%
  select(año,
         precio_carbon = all_of(col_carbon),
         precio_gasolina = all_of(col_gasolina)) %>%
  filter(año == anio_filtro) %>%
  mutate(across(everything(), as.numeric)) %>%
  drop_na()

if (nrow(df_disp) == 0) stop(paste("⚠️ No hay datos disponibles para el año", anio_filtro))

# --- 5) Calcular correlación y modelo lineal ---
modelo <- lm(precio_gasolina ~ precio_carbon, data = df_disp)
r_val <- cor(df_disp$precio_carbon, df_disp$precio_gasolina, use = "pairwise.complete.obs")
r2_val <- summary(modelo)$r.squared

cat("Coeficiente de correlación de Pearson (r):", round(r_val, 3), "\n")
## Coeficiente de correlación de Pearson (r): 0.307
cat("Coeficiente de determinación (R²):", round(r2_val, 3), "\n\n")
## Coeficiente de determinación (R²): 0.094
# --- 6) Crear la gráfica de dispersión ---
ggplot(df_disp, aes(x = precio_carbon, y = precio_gasolina)) +
  geom_point(color = "blue", size = 2.8, alpha = 0.75) +
  geom_smooth(method = "lm", se = TRUE, color = "pink", linewidth = 1) +
  stat_poly_eq(aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")),
               formula = y ~ x, parse = TRUE, label.x.npc = "left", label.y.npc = 0.95,
               size = 4, color = "black") +
  labs(
    title = paste("Relación entre precios reales del Carbón y la Gasolina -", anio_filtro),
    subtitle = "Gráfica de dispersión con línea de ajuste lineal (modelo lm)",
    x = paste0("Precio real del Carbón (", col_carbon, ")"),
    y = paste0("Precio real de la Gasolina (", col_gasolina, ")"),
    caption = paste0("Fuente: elaboración propia con base en datos del taller\n",
                     "r = ", round(r_val, 3), " | R² = ", round(r2_val, 3))
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(size = 14, face = "bold"),
    plot.subtitle = element_text(size = 11, color = "gray30"),
    plot.caption = element_text(size = 9, color = "gray40"),
    panel.grid.minor = element_blank()
  )