1 Instalacion de paquetes

# Instalación (Solo si es necesario)
# install.packages(c("tidyverse", "janitor", "writexl", "openxlsx", "stringi", "mice", "VIM", "MASS"))
# install.packages("naniar")
# análisis, diagnóstico y visualización de valores faltantes (NA)
# install.packages("stringr")
# install.packages("readr")
# install.packages("glue")
# install.packages("mice", repos = "https://cloud.r-project.org")

2 Cargar librería

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
library(naniar) # Análisis, diagnóstico y visualización de valores faltantes (NA)
library(stringr) # limpiar texto (strings) de forma consistente y segura.
library(readr) # leer archivos de datos (CSV, TXT) de forma rápida y correcta.
library(glue) # construir textos dinámicos combinando texto + variables.
library(mice) # Imputar Valores.

3 Carga y Estructuración de Datos

# 1. Carga del archivo con las nuevas columnas
df_raw <- read_csv(
  "C:/Users/john/Desktop/Data2AI_LATAM/db/2_Factores_de_emisión_del_inventario_Nacional_de_Emisiones_Atmosféricas_20260131.csv",
  locale = locale(encoding = "UTF-8")
)

df <- df_raw %>%
  janitor::clean_names() # estandariza nombres: minúsculas + _

glimpse(df)
Rows: 226,381
Columns: 31
$ ano                      <dbl> 1990, 1990, 1990, 1990, 1990, 1990, 1990, 199…
$ region                   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ desagregacion_especifica <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ cartera                  <chr> "Minenergía", "Minenergía", "Minenergía", "Mi…
$ entidad                  <chr> "UPME", "UPME", "UPME", "UPME", "UPME", "UPME…
$ instrumento              <chr> "FECOC 2016", "FECOC 2016", "FECOC 2016", "FE…
$ fuente_especifica        <chr> "FECOC - UPME nombre del archivo FECOC-UPME_E…
$ s                        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ s_2                      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ c                        <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", …
$ sb1                      <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ sb2                      <chr> "a", "a", "a", "a", "a", "a", "b", "b", "b", …
$ sb3                      <chr> "i", "i", "i", "i", "i", "i", NA, NA, NA, NA,…
$ sb4                      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ sector_ipcc              <chr> "1. Energía", "1. Energía", "1. Energía", "1.…
$ categoria                <chr> "1.A. Actividades de quema de combustibles", …
$ subcategoria1            <chr> "1.A.1. Industrias de la energía", "1.A.1. In…
$ subcategoria2            <chr> "1.A.1.a. Producción de electricidad pública …
$ subcategoria3            <chr> "1.A.1.a.i. Generación de electricidad", "1.A…
$ subcategoria4            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ clasificacion1           <chr> "CARBON MINERAL", "DIESEL OIL", "FUEL OIL", "…
$ clasificacion2           <chr> "CENTRALES TERMICAS", "CENTRALES TERMICAS", "…
$ clasificacion3           <chr> NA, NA, NA, NA, NA, NA, "CONSUMO TOTAL", "CON…
$ nombre_factor            <chr> "CONSUMO DE COMBUSTIBLE EN GENERACION DE ENER…
$ nivel                    <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ codigo_factor            <chr> "1990NACIONAL1A1aiCARBON MINERALCENTRALES TER…
$ valor_f                  <dbl> 88136.00, 74193483.00, 78281203.00, 55539.11,…
$ unidad_f                 <chr> "kg CO2/TJ", "kg CO2/TJ", "kg CO2/TJ", "kg CO…
$ inc_inferior             <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ inc_superior             <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ contaminante             <chr> "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", "CO…

4 LIMPIEZA NUMÉRICA (valor_f, inc_)

Detecta formatos tipo:

“74,193,483” (coma como miles)

“55,539.11” (coma miles, punto decimal)

to_num_mixed <- function(x) {
  x <- as.character(x)
  x <- str_replace_all(x, "\\s+", "")
  x <- na_if(x, "")

  # Caso: coma + punto → coma = miles
  x <- ifelse(
    str_detect(x, ",") & str_detect(x, "\\."),
    str_replace_all(x, ",", ""),
    x
  )

  # Caso: solo coma → coma = miles
  x <- ifelse(
    str_detect(x, ",") & !str_detect(x, "\\."),
    str_replace_all(x, ",", ""),
    x
  )

  suppressWarnings(as.numeric(x))
}

df_clean <- df %>%
  mutate(
    valor_f      = to_num_mixed(valor_f),
    inc_inferior = to_num_mixed(inc_inferior),
    inc_superior = to_num_mixed(inc_superior)
  )

4.1 Abreviar valores internos (solo si son largos)

Normalizamos (mayúsculas, sin tildes).

Generamos abreviatura por siglas + recorte.

Creamos un diccionario por columna para reemplazo consistente.

normalize_key <- function(x) {
  x %>%
    as.character() %>%
    stringi::stri_enc_toutf8(is_unknown_8bit = TRUE) %>%
    stringi::stri_trans_general("Latin-ASCII") %>%
    str_squish() %>%
    str_to_upper()
}

make_abbrev <- function(x, max_len = 28) {
  if (is.na(x) || nchar(x) <= max_len) return(x)

  cleaned <- x %>%
    str_replace_all("[^A-Z0-9 ]", " ") %>%
    str_squish() %>%
    str_replace_all(
      "\\b(DE|DEL|LA|LAS|EL|LOS|Y|EN|PARA|POR|CON|SIN|A|AL)\\b",
      ""
    ) %>%
    str_squish()

  words <- unlist(str_split(cleaned, " "))
  sigla <- paste0(substr(words, 1, 1), collapse = "")
  pref  <- substr(words[1], 1, min(3, nchar(words[1])))

  out <- str_squish(paste0(pref, "_", sigla))

  if (nchar(out) < 6) out <- substr(x, 1, max_len)
  substr(out, 1, max_len)
}

cols_to_abbrev <- intersect(
  names(df_clean),
  c(
    "region","desagregacion_especifica","cartera","entidad","instrumento",
    "fuente_especifica","sector_ipcc","categoria",
    "subcategoria1","subcategoria2","subcategoria3","subcategoria4",
    "clasificacion1","clasificacion2","clasificacion3",
    "nombre_factor","nivel","unidad_f","contaminante"
  )
)

# Copias claras para auditoría
df_before <- df_clean
df_abbrev <- df_clean

abbrev_maps <- list()
max_len <- 28

for (col in cols_to_abbrev) {

  v_norm <- normalize_key(df_abbrev[[col]])
  u <- sort(unique(v_norm[!is.na(v_norm)]))

  ab <- purrr::map_chr(u, make_abbrev, max_len = max_len)

  # Resolver colisiones
  if (any(duplicated(ab))) {
    for (d in unique(ab[duplicated(ab)])) {
      idx <- which(ab == d)
      ab[idx] <- paste0(ab[idx], "_", seq_along(idx))
      ab[idx] <- substr(ab[idx], 1, max_len)
    }
  }

  map_tbl <- tibble(
    column = col,
    original_norm = u,
    abbreviated = ab
  )

  abbrev_maps[[col]] <- map_tbl

  # Aplicar mapeo
  m <- setNames(map_tbl$abbreviated, map_tbl$original_norm)
  x_norm <- normalize_key(df_abbrev[[col]])

  idx <- !is.na(x_norm)
  mapped <- unname(m[x_norm[idx]])

  df_abbrev[[col]][idx] <- ifelse(
    is.na(mapped),
    df_abbrev[[col]][idx],
    mapped
  )
}

dict_abbrev <- bind_rows(abbrev_maps)
df_after <- df_abbrev

4.2 Resumen por cada columna (auditoría)

Cuántos cambiaron, NA antes/después, y cuántos quedaron largos.

audit_cols <- intersect(cols_to_abbrev, intersect(names(df_before), names(df_after)))

audit_summary <- purrr::map_dfr(audit_cols, function(col) {
  before <- as.character(df_before[[col]])
  after  <- as.character(df_after[[col]])

  tibble(
    column = col,
    n_total = length(after),
    n_na_before = sum(is.na(before)),
    n_na_after  = sum(is.na(after)),
    n_changed = sum(!is.na(before) & !is.na(after) & before != after),
    n_long_before = sum(!is.na(before) & nchar(before) > max_len),
    n_long_after  = sum(!is.na(after)  & nchar(after)  > max_len)
  )
})

audit_summary
# A tibble: 19 × 7
   column    n_total n_na_before n_na_after n_changed n_long_before n_long_after
   <chr>       <int>       <int>      <int>     <int>         <int>        <int>
 1 region     226381       82927      82927      9440             0            0
 2 desagreg…  226381      221325     221325      5056          1376            0
 3 cartera    226381        3688       3688    222693          4160            0
 4 entidad    226381       25057      25057    188680        125829            0
 5 instrume…  226381      169467     169467     14762          2129            0
 6 fuente_e…  226381       64838      64838    152739        136862            0
 7 sector_i…  226381           0          0    226381        149454            0
 8 categoria  226381       24067      24067    202314         30484            0
 9 subcateg…  226381        3008       3008    223373         60228            0
10 subcateg…  226381       17515      17515    208866        157818            0
11 subcateg…  226381       44068      44068    182313        166371            0
12 subcateg…  226381      221261     221261      5120          3556            0
13 clasific…  226381       15273      15273     52182          6986            0
14 clasific…  226381       81369      81369     30032           192            0
15 clasific…  226381       90199      90199      8960             0            0
16 nombre_f…  226381         697        697    222956         73816            0
17 nivel      226381       38267      38267    149970             0            0
18 unidad_f   226381        3469       3469    194722         23780            0
19 contamin…  226381       69349      69349     13127             0            0
audit_summary %>%
  select(column, n_long_before, n_long_after)
# A tibble: 19 × 3
   column                   n_long_before n_long_after
   <chr>                            <int>        <int>
 1 region                               0            0
 2 desagregacion_especifica          1376            0
 3 cartera                           4160            0
 4 entidad                         125829            0
 5 instrumento                       2129            0
 6 fuente_especifica               136862            0
 7 sector_ipcc                     149454            0
 8 categoria                        30484            0
 9 subcategoria1                    60228            0
10 subcategoria2                   157818            0
11 subcategoria3                   166371            0
12 subcategoria4                     3556            0
13 clasificacion1                    6986            0
14 clasificacion2                     192            0
15 clasificacion3                       0            0
16 nombre_factor                    73816            0
17 nivel                                0            0
18 unidad_f                         23780            0
19 contaminante                         0            0

4.3 Conclusiones

El proceso de preparación de datos aplicado al Inventario Nacional de Emisiones Atmosféricas permitió consolidar un conjunto de datos limpio, consistente, trazable y apto para análisis estadístico y modelamiento, sin comprometer la integridad de la información original. A continuación, se presentan las conclusiones principales del procedimiento.

Durante todo el pipeline de procesamiento se conservaron las 226.381 observaciones originales, sin pérdida ni duplicación de registros, además la estructura del dataset (filas y columnas) se mantuvo estable en todas las etapas.

No se introdujeron desalineaciones, errores de indexación ni conflictos de tipos de datos, lo que confirma que el proceso no fue destructivo y respetó la estructura original del inventario.

En el análisis comparativo antes y después del procesamiento se observó que el número de valores faltantes (NA) se mantuvo idéntico por columna, no se generaron valores faltantes adicionales durante la limpieza, normalización o abreviación y no se eliminaron ni reemplazaron valores válidos por error, lo que garantiza que el proceso no alteró la calidad semántica ni la completitud de la información disponible.

La limpieza de texto permitió estandarizar de manera homogénea todos los campos categóricos mediante la corrección de problemas de encoding (UTF-8 / mojibake), la eiminación de tildes y caracteres especiales, unificación de mayúsculas/minúsculas, además de la compactación de espacios y tratamiento explícito de valores vacíos. Esta normalización redujo la ambigüedad semántica y evitó duplicidades causadas por diferencias superficiales de escritura, mejorando la consistencia interna del dataset.

Las variables numéricas críticas (valor_f, inc_inferior, inc_superior) fueron transformadas correctamente desde formatos mixtos de origen (comas como separadores de miles y puntos decimales) hacia valores numéricos homogéneos, sin pérdida de información.

El procedimiento fue reproducible, tolerante a formatos inconsistentes y seguro frente a valores faltantes, lo que habilita el uso directo de estas variables en análisis estadísticos, modelos de incertidumbre y procesos de agregación.

Las columnas con textos extensos (por ejemplo: entidad, sector_ipcc, fuente_especifica, subcategoria*) fueron tratadas mediante un sistema de abreviación inteligente que solo abrevia valores que exceden un umbral máximo de longitud (28 caracteres), utiliza siglas informativas combinadas con prefijos semánticos, elimina conectores no informativos para mejorar legibilidad y aplica un diccionario de reemplazo por columna, garantizando consistencia.

El número de valores largos se redujo de decenas de miles a cero en todas las columnas auditadas, no se produjeron colisiones semánticas (abreviaturas ambiguas) y la información se mantuvo interpretable y trazable.

El proceso generó Un diccionario de abreviaciones, que permite auditar los cambios realizados, revertir el proceso si es necesario y documentar el mapeo original abreviado.

Tras el procesamiento, el conjunto de datos queda plenamente preparado para analisis de nulos por variables, análisis exploratorio y estadístico, modelamiento de emisiones y escenarios y visualización.

5 ANÁLISIS DE NULOS

# Función de revisión de nulos
reporte_nulos <- df_after %>%
  summarise(across(everything(),
                   ~ sum(is.na(.)),
                   .names = "nulos_{col}")) %>%
  pivot_longer(everything(),
               names_to = "variable",
               values_to = "nulos") %>%
  mutate(
    variable = str_replace(variable, "nulos_", ""),
    porcentaje = round((nulos / nrow(df_after)) * 100, 2)
  ) %>%
  arrange(desc(nulos))   # ← AQUÍ se ordena por cantidad de nulos

options(dplyr.width = Inf)
options(tibble.print_max = Inf)

# Mostrar resultados
reporte_nulos
# A tibble: 31 × 3
   variable                  nulos porcentaje
   <chr>                     <int>      <dbl>
 1 desagregacion_especifica 221325      97.8 
 2 subcategoria4            221261      97.7 
 3 sb4                      213183      94.2 
 4 sb3                      185098      81.8 
 5 inc_superior             175622      77.6 
 6 inc_inferior             175547      77.5 
 7 instrumento              169467      74.9 
 8 s_2                      100621      44.4 
 9 clasificacion3            90199      39.8 
10 region                    82927      36.6 
11 clasificacion2            81369      35.9 
12 contaminante              69349      30.6 
13 fuente_especifica         64838      28.6 
14 subcategoria3             44068      19.5 
15 nivel                     38267      16.9 
16 entidad                   25057      11.1 
17 categoria                 24067      10.6 
18 sb2                       17516       7.74
19 subcategoria2             17515       7.74
20 clasificacion1            15273       6.75
21 valor_f                   14825       6.55
22 sb1                        6275       2.77
23 ano                        4149       1.83
24 cartera                    3688       1.63
25 unidad_f                   3469       1.53
26 s                          3267       1.44
27 c                          3267       1.44
28 subcategoria1              3008       1.33
29 nombre_factor               697       0.31
30 codigo_factor               610       0.27
31 sector_ipcc                   0       0   

5.1 NA_APL (no aplica / jerárquico)

En tu dataset, esto aplica muy bien a niveles jerárquicos que “no existen” para ciertas filas:

cols_na_apl <- c(
  "desagregacion_especifica",
  "s_2",
  "sb3", "sb4",
  "subcategoria4",
  "clasificacion3",
  "nivel"
)

5.2 NA_REP (no reportado)

Usa esto cuando el campo normalmente debería existir, pero viene vacío (más “dato faltante real”):

cols_na_rep <- c(
  "region"
  # agrega aquí otras si en tu diagnóstico aplica, por ejemplo:
  # "fuente_especifica", "contaminante"
)

5.3 Aplicar reemplazo (solo donde hay NA)

df_after <- df_after %>%
  mutate(
    across(all_of(intersect(cols_na_apl, names(df_after))),
           ~ tidyr::replace_na(as.character(.x), "NA_APL")),
    across(all_of(intersect(cols_na_rep, names(df_after))),
           ~ tidyr::replace_na(as.character(.x), "NA_REP"))
  )

5.4 Análisis de nulos REALES (excluye NA_APL y NA_REP)

reporte_nulos <- df_after %>%
  summarise(
    across(
      everything(),
      ~ {
        # NA reales
        if (is.character(.x)) {
          sum(is.na(.x))
        } else {
          sum(is.na(.x))
        }
      },
      .names = "nulos_{.col}"
    )
  ) %>%
  pivot_longer(
    everything(),
    names_to = "variable",
    values_to = "nulos"
  ) %>%
  mutate(
    variable = str_replace(variable, "nulos_", ""),
    porcentaje = round((nulos / nrow(df_after)) * 100, 2)
  ) %>%
  arrange(desc(nulos))

options(dplyr.width = Inf)
options(tibble.print_max = Inf)

reporte_nulos
# A tibble: 31 × 3
   variable                  nulos porcentaje
   <chr>                     <int>      <dbl>
 1 inc_superior             175622      77.6 
 2 inc_inferior             175547      77.5 
 3 instrumento              169467      74.9 
 4 clasificacion2            81369      35.9 
 5 contaminante              69349      30.6 
 6 fuente_especifica         64838      28.6 
 7 subcategoria3             44068      19.5 
 8 entidad                   25057      11.1 
 9 categoria                 24067      10.6 
10 sb2                       17516       7.74
11 subcategoria2             17515       7.74
12 clasificacion1            15273       6.75
13 valor_f                   14825       6.55
14 sb1                        6275       2.77
15 ano                        4149       1.83
16 cartera                    3688       1.63
17 unidad_f                   3469       1.53
18 s                          3267       1.44
19 c                          3267       1.44
20 subcategoria1              3008       1.33
21 nombre_factor               697       0.31
22 codigo_factor               610       0.27
23 region                        0       0   
24 desagregacion_especifica      0       0   
25 s_2                           0       0   
26 sb3                           0       0   
27 sb4                           0       0   
28 sector_ipcc                   0       0   
29 subcategoria4                 0       0   
30 clasificacion3                0       0   
31 nivel                         0       0   

5.5 Definir en qué columnas NO aceptas NA_APL / NA_REP

Para modelar emisiones, se necesita: categoria, subcategoria1, valor_f, contaminante, ano

cols_clave <- c(
  "categoria",
  "subcategoria1",
  "valor_f",
  "contaminante",
  "ano"
)

5.6 Crear el nuevo df SIN NA_APL / NA_REP

df_modelo <- df_after %>%
  filter(
    across(
      all_of(cols_clave),
      ~ !(.x %in% c("NA_APL", "NA_REP") | is.na(.x))
    )
  )

5.7 Diagnóstico de pérdida de observaciones por reglas de exclusión

tibble(
  filas_originales = nrow(df_after),
  filas_modelo     = nrow(df_modelo),
  filas_eliminadas = nrow(df_after) - nrow(df_modelo),
  porcentaje_perdida = round(
    (nrow(df_after) - nrow(df_modelo)) / nrow(df_after) * 100, 2
  )
)
# A tibble: 1 × 4
  filas_originales filas_modelo filas_eliminadas porcentaje_perdida
             <int>        <int>            <int>              <dbl>
1           226381       115334           111047               49.0

5.8 Observar los datos perdidos

df_after %>%
  mutate(
    motivo = case_when(
      contaminante == "NA_REP" ~ "contaminante",
      region == "NA_REP" ~ "region",
      categoria == "NA_APL" ~ "categoria",
      subcategoria1 == "NA_APL" ~ "subcategoria1",
      TRUE ~ "OK"
    )
  ) %>%
  count(motivo, sort = TRUE)
# A tibble: 2 × 2
  motivo      n
  <chr>   <int>
1 OK     143454
2 region  82927

5.9 Definir columnas a ignorar (regla explícita)

cols_ignorar <- c(
  "s", "sb1", "sb4", # codificación jerárquica
  "s_2", "sb2", "sb3", # etiquetas internas
  "c",                        # clasificación técnica
  "inc_inferior", "inc_superior", # incertidumbre incompleta
  "ano" # índice temporal (no predictor)
)

5.10 Crear dataset para modelado (sin esas columnas)

df_modelo <- df_after %>%
  dplyr::select(-any_of(cols_ignorar))

5.11 Verificación

setdiff(cols_ignorar, names(df_modelo))
 [1] "s"            "sb1"          "sb4"          "s_2"          "sb2"         
 [6] "sb3"          "c"            "inc_inferior" "inc_superior" "ano"         
glimpse(df_modelo)
Rows: 226,381
Columns: 21
$ region                   <chr> "NA_REP", "NA_REP", "NA_REP", "NA_REP", "NA_R…
$ desagregacion_especifica <chr> "NA_APL", "NA_APL", "NA_APL", "NA_APL", "NA_A…
$ cartera                  <chr> "MINENERGIA", "MINENERGIA", "MINENERGIA", "MI…
$ entidad                  <chr> "UPME", "UPME", "UPME", "UPME", "UPME", "UPME…
$ instrumento              <chr> "FECOC 2016", "FECOC 2016", "FECOC 2016", "FE…
$ fuente_especifica        <chr> "FEC_FUNAFUE102", "FEC_FUNAFUE102", "FEC_FUNA…
$ sector_ipcc              <chr> "1. ENERGIA", "1. ENERGIA", "1. ENERGIA", "1.…
$ categoria                <chr> "1_1AQC", "1_1AQC", "1_1AQC", "1_1AQC", "1_1A…
$ subcategoria1            <chr> "1_11IE", "1_11IE", "1_11IE", "1_11IE", "1_11…
$ subcategoria2            <chr> "1_11PEPCCAP", "1_11PEPCCAP", "1_11PEPCCAP", …
$ subcategoria3            <chr> "1_11IGE", "1_11IGE", "1_11IGE", "1_11IGE", "…
$ subcategoria4            <chr> "NA_APL", "NA_APL", "NA_APL", "NA_APL", "NA_A…
$ clasificacion1           <chr> "CARBON MINERAL", "DIESEL OIL", "FUEL OIL", "…
$ clasificacion2           <chr> "CENTRALES TERMICAS", "CENTRALES TERMICAS", "…
$ clasificacion3           <chr> "NA_APL", "NA_APL", "NA_APL", "NA_APL", "NA_A…
$ nombre_factor            <chr> "CON_CCGE", "CON_CCGE", "CON_CCGE", "CON_CCGE…
$ nivel                    <chr> "NA_APL", "NA_APL", "NA_APL", "NA_APL", "NA_A…
$ codigo_factor            <chr> "1990NACIONAL1A1aiCARBON MINERALCENTRALES TER…
$ valor_f                  <dbl> 88136.00, 74193483.00, 78281203.00, 55539.11,…
$ unidad_f                 <chr> "KG CO2/TJ", "KG CO2/TJ", "KG CO2/TJ", "KG CO…
$ contaminante             <chr> "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", "CO…

5.12 Generar el registro automáticamente en R

registro_abreviaturas <- tibble::tribble(
  ~abreviatura, ~columna, ~significado, ~tipo,
  "NA_APL", "Varias", "No Aplica", "Control de nulos",
  "NA_REP", "region", "No Reportado", "Control de nulos",
  "MINENERGIA", "cartera", "Ministerio de Minas y Energía", "Institucional",
  "UPME", "entidad", "Unidad de Planeación Minero Energética", "Institucional",
  "FECOC 2016", "instrumento", "Factor de Emisión de Combustión (2016)", "Metodológico",
  "FEC_FUNAFUE102", "fuente_especifica", "Fuente técnica FUNAFUE código 102", "Fuente técnica",
  "1. ENERGIA", "sector_ipcc", "Sector IPCC Energía", "Clasificación IPCC",
  "1_1AQC", "categoria", "Quema de combustibles", "Clasificación IPCC",
  "1_11IE", "subcategoria1", "Industrias de la energía", "Clasificación IPCC",
  "1_11PEPCCAP", "subcategoria2", "Producción de electricidad", "Clasificación IPCC",
  "1_11IGE", "subcategoria3", "Generación eléctrica", "Clasificación IPCC",
  "CON_CCGE", "nombre_factor", "Consumo de combustible en generación eléctrica", "Variable técnica",
  "KG CO2/TJ", "unidad_f", "Kilogramos de CO2 por terajulio", "Unidad",
  "CO2", "contaminante", "Dióxido de carbono", "Contaminante"
)

5.13 Exportar

ruta_salida <- "C:/Users/john/Desktop/Data2AI_LATAM/db"

# Guardar como CSV
readr::write_csv(
  registro_abreviaturas,
  file.path(ruta_salida, "registro_abreviaturas.csv")
)

# Guardar como Excel (.xlsx)
openxlsx::write.xlsx(
  registro_abreviaturas,
  file = file.path(ruta_salida, "registro_abreviaturas.xlsx"),
  overwrite = TRUE
)

# Verificación rápida (recomendada)
list.files(
  path = ruta_salida,
  pattern = "registro_abreviaturas",
  full.names = TRUE
)
[1] "C:/Users/john/Desktop/Data2AI_LATAM/db/registro_abreviaturas.csv" 
[2] "C:/Users/john/Desktop/Data2AI_LATAM/db/registro_abreviaturas.xlsx"
# attr(registro_abreviaturas, "ruta_guardado") <- ruta_salida
# attr(registro_abreviaturas, "fecha_creacion") <- Sys.Date()

6 VARIABLE df_modelo (NO TIENE LAS VARIABLES IGNORADAS

6.1 Analisis de nulos

# Función de revisión de nulos
reporte_nulos <- df_modelo %>%
  summarise(across(everything(),
                   ~ sum(is.na(.)),
                   .names = "nulos_{col}")) %>%
  pivot_longer(everything(),
               names_to = "variable",
               values_to = "nulos") %>%
  mutate(
    variable = str_replace(variable, "nulos_", ""),
    porcentaje = round((nulos / nrow(df_modelo)) * 100, 2)
  ) %>%
  arrange(desc(nulos))   # ← AQUÍ se ordena por cantidad de nulos

options(dplyr.width = Inf)
options(tibble.print_max = Inf)

# Mostrar resultados
reporte_nulos
# A tibble: 21 × 3
   variable                  nulos porcentaje
   <chr>                     <int>      <dbl>
 1 instrumento              169467      74.9 
 2 clasificacion2            81369      35.9 
 3 contaminante              69349      30.6 
 4 fuente_especifica         64838      28.6 
 5 subcategoria3             44068      19.5 
 6 entidad                   25057      11.1 
 7 categoria                 24067      10.6 
 8 subcategoria2             17515       7.74
 9 clasificacion1            15273       6.75
10 valor_f                   14825       6.55
11 cartera                    3688       1.63
12 unidad_f                   3469       1.53
13 subcategoria1              3008       1.33
14 nombre_factor               697       0.31
15 codigo_factor               610       0.27
16 region                        0       0   
17 desagregacion_especifica      0       0   
18 sector_ipcc                   0       0   
19 subcategoria4                 0       0   
20 clasificacion3                0       0   
21 nivel                         0       0   

6.2 Conclusiones

6.2.1 Diagnóstico según Rubin (MCAR / MAR / MNAR)

6.2.2 Decisión variable por variable:

imputar / no imputar / tratar como categoría. Proceso de imputación paso a paso en R (con código claro). Según Rubin (1976), los datos faltantes pueden ser:

6.2.3 MCAR (Missing Completely At Random)

El NA no depende de ninguna variable observada ni no observada. Se puede eliminar o imputar sin sesgo.

6.2.4 MAR (Missing At Random)

El NA depende de otras variables observadas. Imputación válida (regresión, MICE, etc.).

6.2.5 MNAR (Missing Not At Random)

El NA depende de la variable faltante en sí (información estructural). NO se debe imputar con valores “inventados”.

En inventarios ambientales y administrativos, la mayoría de NA son MAR o MNAR, no MCAR.

6.2.6 Variables que NO se deben imputar numéricamente

Son estructurales o administrativas:

Variable % NA Decisión Justificación
1_instrumento 74.9 NO imputar Ausencia estructural (MNAR)
4_fuente_especifica 28.6 NO imputar No aplica a todos los registros
5_subcategoria3 19.5 NO imputar Nivel jerárquico no obligatorio
7_categoria, 13_subcategoria1, 8_subcategoria2 7–10 NO imputar Estructura IPCC
9_clasificacion1, 2_clasificacion2 6–36 NO imputar Clasificación técnica
6_entidad, 11_cartera <12 NO imputar Administrativas

Nota: Se usó No aplica (NA_APL), No reporta (NA_REP) como categorías explícitas.

6.2.7 Variables categóricas que se tratan como categoría explícita

No imputación estadística:

Variable Acción
1_instrumento "NA_APL"
4_fuente_especifica "NA_APL"
2_clasificacion2 "NA_APL"
3_contaminante "NA_REP"

6.2.8 Variable que SÍ se puede imputar estadísticamente

Variable % NA Tipo Decisión
10_valor_f 6.55 Numérica continua Imputar (MAR)

valor_f depende de sector_ipcc, categoria, subcategorías, contaminante, unidad_f

7 IMPUTACION EN R

7.1 Separar variables para imputación numérica

df_imputacion <- df_modelo %>%
  select(
    valor_f,
    sector_ipcc,
    categoria,
    subcategoria1,
    subcategoria2,
    subcategoria3,
    contaminante,
    unidad_f
  )

7.2 usar paquete MICE

#library(mice)

# Inicializar
ini <- mice(df_imputacion, maxit = 0)

# Método: predictive mean matching
meth <- ini$method
meth["valor_f"] <- "pmm"

# No imputar categóricas
meth[names(meth) != "valor_f"] <- ""

# Ejecutar imputación
imp <- mice(
  df_imputacion,
  method = meth,
  m = 5,
  maxit = 15,
  seed = 5477976
)

 iter imp variable
  1   1  valor_f
  1   2  valor_f
  1   3  valor_f
  1   4  valor_f
  1   5  valor_f
  2   1  valor_f
  2   2  valor_f
  2   3  valor_f
  2   4  valor_f
  2   5  valor_f
  3   1  valor_f
  3   2  valor_f
  3   3  valor_f
  3   4  valor_f
  3   5  valor_f
  4   1  valor_f
  4   2  valor_f
  4   3  valor_f
  4   4  valor_f
  4   5  valor_f
  5   1  valor_f
  5   2  valor_f
  5   3  valor_f
  5   4  valor_f
  5   5  valor_f
  6   1  valor_f
  6   2  valor_f
  6   3  valor_f
  6   4  valor_f
  6   5  valor_f
  7   1  valor_f
  7   2  valor_f
  7   3  valor_f
  7   4  valor_f
  7   5  valor_f
  8   1  valor_f
  8   2  valor_f
  8   3  valor_f
  8   4  valor_f
  8   5  valor_f
  9   1  valor_f
  9   2  valor_f
  9   3  valor_f
  9   4  valor_f
  9   5  valor_f
  10   1  valor_f
  10   2  valor_f
  10   3  valor_f
  10   4  valor_f
  10   5  valor_f
  11   1  valor_f
  11   2  valor_f
  11   3  valor_f
  11   4  valor_f
  11   5  valor_f
  12   1  valor_f
  12   2  valor_f
  12   3  valor_f
  12   4  valor_f
  12   5  valor_f
  13   1  valor_f
  13   2  valor_f
  13   3  valor_f
  13   4  valor_f
  13   5  valor_f
  14   1  valor_f
  14   2  valor_f
  14   3  valor_f
  14   4  valor_f
  14   5  valor_f
  15   1  valor_f
  15   2  valor_f
  15   3  valor_f
  15   4  valor_f
  15   5  valor_f

7.3 Reemplazar solo valor_f

df_modelo$valor_f <- complete(imp)$valor_f

7.4 Verificación: ¿Bajaron los NA?

# Antes de imputar
sum(is.na(df_before$valor_f))
[1] 14825
# Después de imputar
sum(is.na(df_modelo$valor_f))
[1] 0

8 EXPORTAR df CON EL NOMBRE DE 2_FACTORES_DE_EMISION_CLEAN

# Exportar a CSV

readr::write_csv(
  df_modelo,
  file.path(ruta_salida, "2_Factores_de_emision_clean.csv")
)

# Exportar a Excel (.xlsx)
openxlsx::write.xlsx(
  df_modelo,
  file = file.path(ruta_salida, "2_Factores_de_emision_clean.xlsx"),
  overwrite = TRUE
)

# Verificación

list.files(
  path = ruta_salida,
  pattern = "2_Factores_de_emision_clean",
  full.names = TRUE
)
[1] "C:/Users/john/Desktop/Data2AI_LATAM/db/2_Factores_de_emision_clean.csv" 
[2] "C:/Users/john/Desktop/Data2AI_LATAM/db/2_Factores_de_emision_clean.xlsx"