# Instalación (Solo si es necesario)
# install.packages(c("tidyverse", "janitor", "writexl", "openxlsx", "stringi", "mice", "VIM", "MASS"))
library(tidyverse) # ggplot2, dplyr, tidyr, readr, purrr
library(janitor) # Limpieza de nombres
library(stringi) # Manipulación avanzada de texto
library(VIM) # Visualización de datos faltantes
library(MASS, exclude = "select") # Evita conflicto con dplyr::select
library(writexl) # Exportación
library(openxlsx) # Lectura/Escritura avanzada Excel
# Leer archivo
df_raw <- read_csv("C:/Users/john/Desktop/Data2AI_LATAM/db/3_Inventario_Nacional_Gases_Efecto_Invernadero_20260131.csv")
# Diagnóstico inicial visual
matrixplot(df_raw[, 1:10])
# Transformación principal
df_analisis <- df_raw %>%
clean_names() %>%
# Separar código de descripción
separate(clasificacion, into = c("codigo", "descripcion"), sep = " ", extra = "merge") %>%
# Selección de columnas clave
select(ano, codigo, descripcion, ch4, co2, n2o, total_emisiones) %>%
# Filtrado de filas vacías
filter(!is.na(total_emisiones)) %>%
# Limpieza de texto y corrección de jerarquía
mutate(
# Quitar punto final del código para conteo correcto (ej. "1.A." -> "1.A")
codigo_limpio = str_remove(codigo, "\\.$"),
# Normalización de descripción
descripcion = str_to_sentence(stringi::stri_trim_both(descripcion)),
descripcion = stringi::stri_replace_all_regex(descripcion, "\\s+", " "),
# Clasificación jerárquica corregida
nivel_jerarquico = case_when(
!str_detect(codigo_limpio, "\\.") ~ "Sector",
str_count(codigo_limpio, "\\.") == 1 ~ "Categoría",
TRUE ~ "Sub-detalle"
)
) %>%
distinct() # Eliminar duplicados lógicos
glimpse(df_analisis)
Rows: 12,433
Columns: 9
$ ano <dbl> 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,…
$ codigo <chr> "1", "1.A.", "1.A.1.", "1.A.1.a.", "1.A.1.ai.", "1.A.…
$ descripcion <chr> "Energía", "Actividades de quema de combustibles (enf…
$ ch4 <dbl> 187.89066785194, 99.56838734724, 0.19343458206, 0.100…
$ co2 <dbl> 44309.8962, 42880.1969, 11246.1375, 6050.4796, 6050.4…
$ n2o <dbl> 2.5138513991, 2.4941920247, 0.0594445132, 0.048810869…
$ total_emisiones <dbl> 50237.0055, 46329.0727, 11267.3065, 6066.2180, 6066.2…
$ codigo_limpio <chr> "1", "1.A", "1.A.1", "1.A.1.a", "1.A.1.ai", "1.A.1.b"…
$ nivel_jerarquico <chr> "Sector", "Categoría", "Sub-detalle", "Sub-detalle", …
# 2.1 Verificación de NAs
total_nas <- sum(is.na(df_analisis$total_emisiones))
cat("Total de NAs en emisiones:", total_nas)
Total de NAs en emisiones: 0
# 2.2 Prueba de "No Duplicidad" por Niveles (Prueba de Fuego)
# El total de 'Sector' debería ser la referencia nacional.
df_analisis %>%
group_by(nivel_jerarquico) %>%
summarise(
total_nivel = sum(total_emisiones, na.rm = TRUE),
num_registros = n(),
ejemplos = paste(head(unique(codigo_limpio), 3), collapse = ", ")
)
# A tibble: 3 × 4
nivel_jerarquico total_nivel num_registros ejemplos
<chr> <dbl> <int> <chr>
1 Categoría 8022457. 1397 1.A, 1.B, 2.A
2 Sector 16067924. 320 1, 2, 3
3 Sub-detalle 18691373. 10716 1.A.1, 1.A.1.a, 1.A.1.ai
# 2.3 Verificación de consistencia de nombres
# Si el conteo es consistente (ej. múltiplo de años), la limpieza fue exitosa
df_analisis %>%
tabyl(descripcion) %>%
arrange(desc(n)) %>%
head(10)
descripcion n percent valid_percent
Incendios forestales 352 0.02831175 0.02838481
Otro 352 0.02831175 0.02838481
Quema controlada 352 0.02831175 0.02838481
Asentamientos que permanecen como tales 160 0.01286898 0.01290219
Ovinos 160 0.01286898 0.01290219
Asentamientos 128 0.01029518 0.01032175
Humedales 128 0.01029518 0.01032175
Humedales que permanecen como tales 128 0.01029518 0.01032175
Otras tierras 128 0.01029518 0.01032175
Pastizales 128 0.01029518 0.01032175
# Distribución de Emisiones y detección de Outliers
ggplot(df_analisis, aes(x = nivel_jerarquico, y = total_emisiones, fill = nivel_jerarquico)) +
geom_boxplot() +
scale_y_log10() +
labs(
title = "Distribución de Emisiones por Nivel Jerárquico",
subtitle = "Verificación de consistencia y valores atípicos",
y = "Total Emisiones (Log10)",
x = "Nivel"
) +
theme_minimal()
# Análisis de datos faltantes final
aggr(df_analisis %>% select(ano, codigo_limpio, total_emisiones),
plot = TRUE, numbers = TRUE)
# Intento de exportación controlada
tryCatch({
writexl::write_xlsx(lista_hojas, path = "C:/Users/john/Desktop/Data2AI_LATAM/db/Inventario_Jerarquizado.xlsx")
cat("Archivo Excel generado correctamente.")
}, error = function(e) {
message("No se pudo generar el Excel. Verifique que el archivo no esté abierto o revise los permisos.")
})
# 5.1. Preparación de datos consolidando nombres truncados
top_10_actividades <- df_analisis %>%
filter(nivel_jerarquico == "Sub-detalle",
ano == max(ano)) %>%
# PASO CRÍTICO: Truncamos antes de agrupar para unificar nombres similares
mutate(descripcion_grafico = stringr::str_trunc(descripcion, width = 35, side = "right")) %>%
group_by(descripcion_grafico) %>%
summarise(total = sum(total_emisiones, na.rm = TRUE)) %>%
ungroup() %>%
mutate(porcentaje = total / sum(total) * 100) %>%
slice_max(total, n = 10)
# 5.2. Creación del gráfico profesional sin superposiciones
ggplot(top_10_actividades, aes(x = reorder(descripcion_grafico, total), y = total)) +
# Barras
geom_col(fill = "firebrick", alpha = 0.8) +
# Etiqueta de porcentaje única por barra
geom_text(aes(label = paste0(round(porcentaje, 1), "%")),
hjust = -0.2,
size = 3.5,
fontface = "bold") +
coord_flip() +
# Ajuste de escala y cuadrícula visible
scale_y_continuous(
labels = scales::label_number(scale_cut = scales::cut_short_scale()),
expand = expansion(mult = c(0, 0.2)) # Más espacio para evitar que el % se salga del cuadro
) +
labs(
title = paste("Top 10 Actividades con Mayores Emisiones -", max(df_analisis$ano)),
subtitle = "Nivel: Sub-detalle | Categorías consolidadas y porcentajes únicos",
x = "Actividad Específica (Abreviada)",
y = "Emisiones Totales (kt CO2 eq)"
) +
# Estética profesional con cuadrícula clara
theme_light() +
theme(
panel.grid.major.y = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major.x = element_line(color = "gray90"), # Cuadrícula vertical clara
plot.title = element_text(face = "bold", size = 14),
axis.text.y = element_text(size = 9, color = "black"),
axis.text.x = element_text(size = 9)
)
El resultado Total de NAs en emisiones es 0.
Categorías como “Incendios forestales” tienen un conteo de 352. Esto tiene sentido lógico (32 años × 11 categorías de incendios, por ejemplo), lo que indica que no hay nombres repetidos por errores de espacios o mayúsculas.
Se ha generado el archivo .xlsx con pestañas separadas. Esto es oro para cualquier reporte oficial, ya que evita que el usuario final confunda los totales con los detalles.
Los números finales nos dan la clave para usar los datos sin errores:
| Nivel | Emisiones Totales | Diagnóstico |
|---|---|---|
| Sector | 16,067,924 | Este es el valor real nacional. Es la suma de los niveles “Padre” (1, 2, 3…). |
| Categoría | 8,022,457 | Representa solo una parte del desglose; algunos sectores no tienen nivel intermedio de categoría. |
| Sub-detalle | 18,691,373 | Confirmado: existe doble conteo (se están sumando hijos junto con nietos). |
Nota: Usa siempre la pestaña de Sectores para hablar del total del país. Usa la pestaña de Detalles solo cuando quieras mostrar qué actividad específica (como “Ovinos” o “Acero”) es la responsable, pero no los sumes todos entre sí.