dane-data 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ """Tablas de referencia: salario mínimo, departamentos, meses y diccionarios DANE."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import unicodedata
7
+ from functools import lru_cache
8
+ from importlib import resources
9
+
10
+ # Salario mínimo legal mensual vigente (SMLMV), pesos corrientes.
11
+ # Fuente: decretos anuales del Gobierno Nacional. Verificar antes de publicar.
12
+ SMLV = {
13
+ 1992: 65_190, 1993: 81_510, 1994: 98_700, 1995: 118_934, 1996: 142_125,
14
+ 1997: 172_005, 1998: 203_826, 1999: 236_460, 2000: 260_100, 2001: 286_000,
15
+ 2002: 309_000, 2003: 332_000, 2004: 358_000, 2005: 381_500, 2006: 408_000,
16
+ 2007: 433_700, 2008: 461_500, 2009: 496_900, 2010: 515_000, 2011: 535_600,
17
+ 2012: 566_700, 2013: 589_500, 2014: 616_000, 2015: 644_350, 2016: 689_455,
18
+ 2017: 737_717, 2018: 781_242, 2019: 828_116, 2020: 877_803, 2021: 908_526,
19
+ 2022: 1_000_000, 2023: 1_160_000, 2024: 1_300_000, 2025: 1_423_500,
20
+ 2026: 1_750_905,
21
+ }
22
+
23
+ DEPARTAMENTOS = {
24
+ 5: "Antioquia", 8: "Atlántico", 11: "Bogotá D.C.", 13: "Bolívar", 15: "Boyacá",
25
+ 17: "Caldas", 18: "Caquetá", 19: "Cauca", 20: "Cesar", 23: "Córdoba",
26
+ 25: "Cundinamarca", 27: "Chocó", 41: "Huila", 44: "La Guajira", 47: "Magdalena",
27
+ 50: "Meta", 52: "Nariño", 54: "Norte de Santander", 63: "Quindío", 66: "Risaralda",
28
+ 68: "Santander", 70: "Sucre", 73: "Tolima", 76: "Valle del Cauca", 81: "Arauca",
29
+ 85: "Casanare", 86: "Putumayo", 88: "San Andrés", 91: "Amazonas", 94: "Guainía",
30
+ 95: "Guaviare", 97: "Vaupés", 99: "Vichada",
31
+ }
32
+
33
+ MESES = {
34
+ 1: "Enero", 2: "Febrero", 3: "Marzo", 4: "Abril", 5: "Mayo", 6: "Junio",
35
+ 7: "Julio", 8: "Agosto", 9: "Septiembre", 10: "Octubre", 11: "Noviembre", 12: "Diciembre",
36
+ }
37
+
38
+
39
+ def strip_accents(text: str) -> str:
40
+ text = text.replace("\xa0", " ").replace(" ", " ").replace("​", "")
41
+ return "".join(c for c in unicodedata.normalize("NFD", text) if unicodedata.category(c) != "Mn").lower()
42
+
43
+
44
+ @lru_cache(maxsize=None)
45
+ def diccionario(encuesta: str) -> dict:
46
+ """
47
+ Diccionario oficial de variables (DDI de ANDA): GEIH 2025 o EAM 2024.
48
+
49
+ Devuelve ``{VARIABLE: {"etiqueta", "pregunta", "categorias", "modulos"}}``.
50
+ Los nombres cambian entre épocas; úselo como guía, no como verdad absoluta
51
+ para años antiguos.
52
+ """
53
+ name = {"geih": "geih_diccionario.json", "eam": "eam_diccionario.json"}[encuesta.lower()]
54
+ with resources.files("dane_data.data").joinpath(name).open(encoding="utf-8") as f:
55
+ return json.load(f)
56
+
57
+
58
+ def describir(variable: str, encuesta: str = "geih") -> str:
59
+ """Etiqueta legible de una variable, o el mismo nombre si no se conoce."""
60
+ info = diccionario(encuesta).get(variable.upper())
61
+ return info["etiqueta"] if info and info["etiqueta"] else variable
62
+
63
+
64
+ def buscar_variable(texto: str, encuesta: str = "geih"):
65
+ """
66
+ Busca variables cuyo nombre, etiqueta o pregunta contengan ``texto``.
67
+
68
+ >>> dd.buscar_variable("pensión")
69
+ """
70
+ import pandas as pd
71
+
72
+ q = strip_accents(texto)
73
+ rows = []
74
+ for var, info in diccionario(encuesta).items():
75
+ blob = strip_accents(f"{var} {info['etiqueta']} {info['pregunta']}")
76
+ if q in blob:
77
+ rows.append({"variable": var, "etiqueta": info["etiqueta"], "pregunta": info["pregunta"],
78
+ "modulo": ", ".join(info.get("modulos", []))[:80]})
79
+ return pd.DataFrame(rows, columns=["variable", "etiqueta", "pregunta", "modulo"])
dane_data/stats.py ADDED
@@ -0,0 +1,109 @@
1
+ """Estadísticas ponderadas por factor de expansión."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Iterable
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+
11
+ def _as_list(x) -> list:
12
+ if x is None:
13
+ return []
14
+ return [x] if isinstance(x, str) else list(x)
15
+
16
+
17
+ def peso_mensual_promedio(df: pd.DataFrame, peso: str = "fex") -> pd.Series:
18
+ """
19
+ Peso que convierte totales de varios meses en un *promedio mensual*.
20
+
21
+ El factor de expansión de la GEIH es mensual: si se juntan 12 meses, la
22
+ suma de ``fex`` es 12 veces la población. Este peso divide ``fex`` por el
23
+ número de meses distintos de cada año presentes en ``df``. Sumarlo
24
+ equivale al promedio de los totales mensuales (método DANE para
25
+ agregados trimestrales/anuales).
26
+ """
27
+ if "mes" not in df.columns or "anio" not in df.columns:
28
+ return df[peso]
29
+ n_meses = df.groupby("anio", observed=True)["mes"].transform("nunique")
30
+ return df[peso] / n_meses
31
+
32
+
33
+ def weighted_quantile(values, weights, q: float) -> float:
34
+ v = np.asarray(values, dtype=float)
35
+ w = np.asarray(weights, dtype=float)
36
+ mask = ~(np.isnan(v) | np.isnan(w)) & (w > 0)
37
+ v, w = v[mask], w[mask]
38
+ if v.size == 0:
39
+ return np.nan
40
+ order = np.argsort(v, kind="stable")
41
+ v, w = v[order], w[order]
42
+ # CDF inversa: el menor valor cuya masa acumulada alcanza q (como _pctile de Stata).
43
+ cum = np.cumsum(w)
44
+ return float(v[np.searchsorted(cum, q * cum[-1], side="left")])
45
+
46
+
47
+ def weighted_mean(values, weights) -> float:
48
+ v = np.asarray(values, dtype=float)
49
+ w = np.asarray(weights, dtype=float)
50
+ mask = ~(np.isnan(v) | np.isnan(w))
51
+ return float(np.average(v[mask], weights=w[mask])) if mask.any() and w[mask].sum() > 0 else np.nan
52
+
53
+
54
+ def tabla(
55
+ df: pd.DataFrame,
56
+ por: str | Iterable[str] | None = None,
57
+ variable: str | None = None,
58
+ estadistico: str = "total",
59
+ peso: str | None = "fex",
60
+ ) -> pd.DataFrame:
61
+ """
62
+ Tabla ponderada para cualquier variable.
63
+
64
+ estadistico:
65
+ * ``"total"`` población expandida (promedio mensual) por grupo.
66
+ * ``"porcentaje"`` distribución porcentual de la *última* columna de ``por``
67
+ dentro de las demás (p. ej. ``por=["anio", "sexo"]`` → % por sexo en cada año).
68
+ * ``"media"``, ``"mediana"`` de ``variable`` (numérica) por grupo.
69
+
70
+ Siempre agrega ``n_muestra`` (observaciones sin expandir). Grupos con
71
+ menos de ~30 observaciones no son confiables.
72
+
73
+ >>> dd.tabla(df, por=["anio", "nivel_educativo"], estadistico="porcentaje")
74
+ >>> dd.tabla(df[df.ocupado == 1], por="sexo", variable="ingreso_laboral", estadistico="mediana")
75
+ """
76
+ por = _as_list(por)
77
+ d = df.copy()
78
+ if peso is None:
79
+ d["_w"] = 1.0
80
+ else:
81
+ d["_w"] = peso_mensual_promedio(d, peso)
82
+ if variable is not None:
83
+ d = d[d[variable].notna()]
84
+ g = d.groupby(por, observed=True, dropna=False) if por else None
85
+
86
+ if estadistico in ("total", "porcentaje"):
87
+ if g is None:
88
+ res = pd.DataFrame({"total": [d["_w"].sum()], "n_muestra": [len(d)]})
89
+ else:
90
+ res = g.agg(total=("_w", "sum"), n_muestra=("_w", "size")).reset_index()
91
+ if estadistico == "porcentaje":
92
+ outer = por[:-1]
93
+ denom = res.groupby(outer, observed=True)["total"].transform("sum") if outer else res["total"].sum()
94
+ res["porcentaje"] = 100 * res["total"] / denom
95
+ return res
96
+
97
+ if variable is None:
98
+ raise ValueError("Indique `variable` para media/mediana.")
99
+ if estadistico == "media":
100
+ fn = lambda x: weighted_mean(x[variable], x["_w"]) # noqa: E731
101
+ elif estadistico == "mediana":
102
+ fn = lambda x: weighted_quantile(x[variable], x["_w"], 0.5) # noqa: E731
103
+ else:
104
+ raise ValueError("estadistico debe ser 'total', 'porcentaje', 'media' o 'mediana'.")
105
+ if g is None:
106
+ return pd.DataFrame({estadistico: [fn(d)], "n_muestra": [len(d)]})
107
+ stat = g[[variable, "_w"]].apply(fn).rename(estadistico)
108
+ n = g.size().rename("n_muestra")
109
+ return pd.concat([stat, n], axis=1).reset_index()
@@ -0,0 +1,265 @@
1
+ Metadata-Version: 2.4
2
+ Name: dane-data
3
+ Version: 0.2.1
4
+ Summary: Descarga, carga y análisis de microdatos del DANE (GEIH y EAM) para estudiantes de economía.
5
+ Author: Luis Ramírez
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/IntegralIndefinida/DANE-data
8
+ Project-URL: Repository, https://github.com/IntegralIndefinida/DANE-data
9
+ Project-URL: Issues, https://github.com/IntegralIndefinida/DANE-data/issues
10
+ Project-URL: Changelog, https://github.com/IntegralIndefinida/DANE-data/releases
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Natural Language :: Spanish
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: requests>=2.28
21
+ Requires-Dist: beautifulsoup4>=4.11
22
+ Requires-Dist: pandas>=2.0
23
+ Requires-Dist: pyarrow>=12
24
+ Provides-Extra: app
25
+ Requires-Dist: streamlit>=1.40; extra == "app"
26
+ Requires-Dist: plotly>=5.18; extra == "app"
27
+ Requires-Dist: openpyxl>=3.1; extra == "app"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # DANE-data 📊🇨🇴
33
+
34
+ **Microdatos del DANE listos para analizar** — Gran Encuesta Integrada de Hogares (**GEIH**, 2007–2026) y
35
+ Encuesta Anual Manufacturera (**EAM**, 1992–2024).
36
+
37
+ Dos formas de usarlo:
38
+
39
+ | | Para quién | Cómo |
40
+ |---|---|---|
41
+ | 🖱️ **Aplicación** | Estudiantes que empiezan, clases, consultas rápidas | `dane-data app` → se abre en el navegador |
42
+ | 🐍 **Librería Python** | Automatización, investigación, estudiantes avanzados | `import dane_data as dd` |
43
+
44
+ Ambas usan el mismo motor: descargan del ANDA del DANE, unen los módulos, **armonizan las variables
45
+ entre épocas** (nombres, separadores, coma decimal, factores de expansión, revisiones CIIU) y calculan
46
+ indicadores ponderados como lo hace el DANE.
47
+
48
+ ---
49
+
50
+ ## Instalación de la app
51
+
52
+ 1. En la página de **[Releases](https://github.com/IntegralIndefinida/DANE-data/releases)** descargue `dane-data-<versión>.zip` (en *Assets*).
53
+ 2. Descomprímalo donde quiera.
54
+ 3. Doble clic en el lanzador de su sistema:
55
+
56
+ | Sistema | Archivo |
57
+ |---|---|
58
+ | Windows | `Abrir dane-data (Windows).bat` |
59
+ | Mac | `Abrir dane-data (Mac).command` |
60
+ | Linux | `abrir-dane-data.sh` |
61
+
62
+ La primera vez necesita internet y tarda 1–3 minutos: el lanzador instala [uv](https://docs.astral.sh/uv/),
63
+ que descarga Python 3.12 y las librerías exactas de `uv.lock` dentro de la carpeta (`.venv/`), sin tocar el
64
+ resto del sistema. Las siguientes veces abre en segundos. **El navegador se abre solo**; para cerrar la
65
+ app basta cerrar la ventana de la terminal.
66
+
67
+ > Primera vez en **Windows**: si aparece "Windows protegió su PC" → *Más información* → *Ejecutar de todas
68
+ > formas*. En **Mac**: si dice "desarrollador no identificado" → clic derecho → *Abrir* → *Abrir*.
69
+
70
+ ## Instalación con pip
71
+
72
+ ```bash
73
+ pip install "dane-data[app]" # librería + aplicación
74
+ pip install dane-data # solo la librería
75
+ dane-data app
76
+ ```
77
+
78
+ ## Instalación para desarrollo
79
+
80
+ ```bash
81
+ git clone https://github.com/IntegralIndefinida/DANE-data
82
+ cd DANE-data
83
+ pip install -e ".[app]" # librería + aplicación
84
+ # pip install -e . # solo la librería
85
+ # o con uv: uv run --extra app dane-data app
86
+ ```
87
+
88
+ Requiere Python ≥ 3.10.
89
+
90
+ ## 🖱️ La aplicación
91
+
92
+ ```bash
93
+ dane-data app # o doble clic en el lanzador; el navegador se abre solo
94
+ ```
95
+
96
+ | Página | Qué hace |
97
+ |---|---|
98
+ | 🏠 Inicio | Explica las encuestas, muestra qué datos hay y configura las carpetas |
99
+ | ⬇️ Descargar datos | Elige encuesta y años y descarga |
100
+ | 👥 Indicadores laborales | TD, TGP, TO, informalidad e ingresos, por sexo, edad, zona, educación o departamento; anual o mensual |
101
+ | 🔎 Tablas y microdatos | Tablas ponderadas a la medida, distribución de ingresos frente al salario mínimo, exportación |
102
+ | 🏭 Indicadores industriales | Producción, valor agregado, empleo, productividad y participación laboral por sector o departamento |
103
+ | 🧱 Panel y microdatos EAM | Panel de establecimientos (balanceado o no), supervivencia, exportación |
104
+ | 📚 Diccionario | Buscador de variables con las etiquetas y categorías oficiales del DANE |
105
+
106
+ Cada tabla se descarga en **CSV, Excel o Stata**, y cada página muestra el **código Python equivalente**:
107
+ así se pasa de la aplicación al notebook sin empezar de cero.
108
+
109
+ Para usarla en un aula (el profesor la sirve y los estudiantes entran desde la red local):
110
+ `dane-data app --address 0.0.0.0`.
111
+
112
+ ## 🐍 La librería
113
+
114
+ ```python
115
+ import dane_data as dd
116
+
117
+ # 1. ¿Dónde están (o estarán) los datos? Se recuerda para siempre.
118
+ dd.set_paths(geih="~/datos/GEIH/data", eam="~/datos/EAM/data", save=True)
119
+
120
+ # 2. Descargar (incremental: lo ya bajado se salta)
121
+ dd.geih.download([2024, 2025])
122
+ dd.eam.download(range(2014, 2025))
123
+
124
+ # 3. Cargar: una persona por fila, módulos ya unidos y variables armonizadas
125
+ df = dd.geih.load(2025) # todos los meses
126
+ df = dd.geih.load([2024, 2025], months=[1, 2, 3]) # primer trimestre
127
+ df = dd.geih.load(2025, columns=["P6500", "P7070"]) # + variables DANE extra
128
+ df = dd.geih.load(2016, empalme=True) # serie de empalme (marco 2018)
129
+ df = dd.geih.load(range(2015, 2026), variables=["fex", "sexo", "ocupado"]) # solo esas columnas
130
+
131
+ # 4. Indicadores laborales ponderados
132
+ dd.geih.indicadores(df) # TGP, TO, TD, informalidad, ingresos…
133
+ dd.geih.indicadores(df, por=["sexo", "zona"])
134
+ dd.geih.indicadores(df, por="grupo_edad", anual=False) # mensual
135
+ dd.geih.serie_indicadores(range(2007, 2027), por="sexo") # precalculado por año, sin cargar microdatos
136
+ dd.geih.serie_indicadores(range(2015, 2027), filtros={"edad": (15, 28)}) # jóvenes
137
+
138
+ # 5. Tablas ponderadas de cualquier cosa
139
+ dd.tabla(df, por=["anio", "nivel_educativo"], estadistico="porcentaje")
140
+ dd.tabla(df[df.ocupado == 1], por="departamento", variable="ingreso_laboral", estadistico="mediana")
141
+ # lo mismo, año por año y guardado en caché (sirve para muchos años sin cargarlos en memoria)
142
+ dd.geih.serie_tabla(range(2007, 2027), por="departamento", variable="ingreso_laboral", estadistico="mediana",
143
+ poblacion="ocupado", filtros={"sexo": ["Mujer"]})
144
+ dd.geih.exportar(range(2015, 2026), "ocupados.csv", poblacion="ocupado") # CSV armonizado, mes a mes
145
+
146
+ # 6. EAM
147
+ eam = dd.eam.load(range(2012, 2025)) # un establecimiento-año por fila
148
+ dd.eam.resumen(eam, por=["anio", "sector"]) # VA, empleo, productividad, participación laboral
149
+ panel = dd.eam.panel(eam, balanceado=True) # índice (nordest, anio)
150
+ dd.eam.supervivencia(eam) # % de plantas que reaparecen cada año
151
+ dd.eam.deflactar(eam, ["valagri"], ipp_por_anio) # a precios constantes
152
+
153
+ # 7. Diccionario oficial
154
+ dd.buscar_variable("pensión")
155
+ dd.describir("P6920")
156
+ ```
157
+
158
+ También en terminal:
159
+
160
+ ```bash
161
+ dane-data estado # rutas y años/meses disponibles
162
+ dane-data descargar geih --anios 2024 2025
163
+ dane-data descargar geih --anios 2016 --empalme
164
+ dane-data descargar eam --anios 2022 2023 2024
165
+ dane-data config --geih RUTA --empalme RUTA --eam RUTA
166
+ ```
167
+
168
+ Vea `examples/01_primeros_pasos.ipynb` para un recorrido completo.
169
+
170
+ ### Variables armonizadas
171
+
172
+ Las variables en **minúscula** las crea `dane-data` y significan lo mismo en todos los años. Las de
173
+ **MAYÚSCULA** (GEIH) son las originales del DANE.
174
+
175
+ **GEIH**: `anio`, `mes`, `fex`, `sexo`, `edad`, `grupo_edad`, `dpto`, `departamento`, `zona`,
176
+ `condicion` (Ocupado / Desocupado / Inactivo / Fuera de la PET), `pet`, `fuerza_trabajo`, `ocupado`,
177
+ `desocupado`, `inactivo`, `formal`, `cotiza_pension`, `ingreso_laboral`, `smlv`, `ratio_smlv`,
178
+ `horas_semana`, `posicion_ocupacional`, `rama2d`, `rama_rev`, `nivel_educativo`, `jefe_hogar`.
179
+
180
+ **EAM**: todas las columnas en minúscula, más `anio`, `ciiu`, `ciiu_rev`, `division`, `sector`, `departamento`.
181
+
182
+ ### Lo que `dane-data` resuelve por usted
183
+
184
+ | Problema en los archivos del DANE | Solución |
185
+ |---|---|
186
+ | 2007–2021: cada módulo partido en Área / Cabecera / Resto | Nacional = Cabecera + Resto (Área es un subconjunto de Cabecera) |
187
+ | Separadores `;` `,` `\t`, UTF-8 / Latin-1, coma decimal, blancos `" "` | Detección automática; columnas numéricas quedan numéricas |
188
+ | Factor de expansión `FEX_C_2011` / `FEX_C` / `FEX_C18` | Unificado en `fex` |
189
+ | Sexo en `P6020` o `P3271`, educación en `P6210` o `P3042`, rama en `RAMA2D` o `RAMA2D_R4` | Unificados fila a fila (sirve al juntar años de distintas épocas) |
190
+ | 2007 sin marcas `OCI`/`DSI`; 2020 y empalme con marcas en Características | La condición se deduce del módulo en que aparece cada persona |
191
+ | Archivos en subcarpetas (`CSV 13/No ocupados.CSV`) | Búsqueda recursiva |
192
+ | Meses a los que les falta un módulo | Se omiten con aviso (si no, el desempleo saldría 0 %); `incluir_incompletos=True` los carga |
193
+ | Varios meses juntos | Niveles = promedio mensual, como el DANE |
194
+ | EAM: `CIIU2N4`, `ciiu2`, `CIIU`, `ciiu3`, `ciiu4`, `ciiu_4`, códigos a 2–3 dígitos | `ciiu`, `ciiu_rev`, `division`, `sector` |
195
+
196
+ Las cargas de la GEIH se guardan en caché (`~/.cache/dane_data`, parquet): un año completo tarda ~6 s la
197
+ primera vez y ~1 s después. Los resultados de `dd.geih.serie_indicadores`, `serie_tabla` y
198
+ `serie_frecuencias` se calculan un año a la vez y se guardan por año: toda la serie 2007–2026 tarda alrededor
199
+ de un minuto la primera vez y es instantánea después. Se recalculan solos si cambia algún archivo del año,
200
+ la consulta (filtros, meses, variables) o el código de dane-data. Todas las tablas y gráficas de la app salen
201
+ de ahí, así que la app nunca tiene más de un año de microdatos en memoria. `dd.geih.clear_cache()` borra
202
+ estas cachés.
203
+
204
+ ### Validación
205
+
206
+ Con los datos reales, TD, TGP, TO y ocupados de cada mes de 2007–2026 y del empalme 2010–2020 coinciden
207
+ con los anexos del DANE, con diferencias menores a 0,01 puntos. También coinciden las cabeceras y cada sexo
208
+ donde el DANE los publica por mes, y las tasas anuales, que son el promedio de los niveles mensuales. Por
209
+ ejemplo, la desocupación promedio fue 10,5 % en 2019 y 10,2 % en 2023. Las pruebas (`pytest`) usan datos
210
+ sintéticos con las rarezas de cada época. Si encuentran los datos reales, también los comparan mes a mes con
211
+ las cifras publicadas (`tests/datos/dane_publicado.csv`) y buscan huecos o saltos bruscos en la serie.
212
+
213
+ ## ⚠️ Advertencias metodológicas
214
+
215
+ - **Informalidad**: `formal` = afiliado a salud (P6090) **y** cotizando a pensión (P6920). Es una
216
+ definición por seguridad social; la oficial del DANE incluye el tamaño de la empresa.
217
+ - **Serie principal vs. empalme**: para series largas 2010–2020 comparables con 2021+, use `empalme=True`.
218
+ - **PET**: desde 2022 son las personas de 15 años o más; antes, 12+ (urbano) y 10+ (rural).
219
+ - **Ingresos**: `INGLABO` en pesos corrientes; la GEIH 2007 no lo trae.
220
+ - **EAM**: los montos vienen en **miles de pesos corrientes**. Según los propios archivos, la CIIU es
221
+ Rev. 2 hasta 2000, Rev. 3 en 2001–2011 y Rev. 4 desde 2012 (la documentación del ANDA dice otra
222
+ cosa; vea la tabla de diferencias más abajo); los sectores no se comparan entre revisiones sin una
223
+ correlativa.
224
+ - **Muestras pequeñas**: con menos de ~100 observaciones (`n_muestra`) las cifras no son confiables.
225
+ - El salario mínimo histórico (`dd.SMLV`) está tomado de los decretos; verifíquelo antes de publicar.
226
+
227
+ ## 📋 Diferencias con la documentación del DANE (ANDA)
228
+
229
+ Cuando los datos contradicen la documentación oficial del ANDA (diccionarios DDI, etiquetas de
230
+ categorías), `dane-data` sigue los datos y lo anota aquí. El buscador de variables
231
+ (`dd.buscar_variable`, `dd.describir` y la página 📚 Diccionario) muestra el DDI **tal como lo publica
232
+ el DANE**, así que en estos casos no coincide con las variables armonizadas.
233
+
234
+ | Variable | Qué dice el ANDA | Qué muestran los datos y qué hace `dane-data` |
235
+ |---|---|---|
236
+ | `P6430` → `posicion_ocupacional` (GEIH, desde 2022) | El DDI de 2022 (catálogo 771) y el de 2025 (catálogo 853) repiten las 9 categorías del cuestionario anterior: 7 = trabajador sin remuneración en negocios de otros hogares, 8 = jornalero o peón, 9 = otro. | Desde enero de 2022 el código 7 es **jornalero o peón** (100 % agropecuario y casi todos con ingreso), el 8 es **otro** y el 9 ya no aparece. Hasta 2021, y en el empalme, se usan las etiquetas anteriores, que sí coinciden con los datos. El código 6 se sigue llamando "familiar sin remuneración"; desde 2022 podría incluir también el trabajo sin remuneración en negocios de otros hogares (sin confirmar). |
237
+ | `ciiu` → `ciiu_rev`, `sector` (EAM 2000 y 2012) | La descripción del estudio (catálogos 491 a 888) dice que "en el año 2000 se incorpora en la EAM la CIIU Rev.3" y que la Rev. 4 se oficializó "con la difusión de las cifras referentes al año 2013". En otro pasaje del mismo texto los cambios de la Rev. 4 se ubican "a partir del año 2012". | El archivo de 2000 trae la variable `ciiu2` con códigos Rev. 2 (solo divisiones 31–39, igual que 1992–1999); la Rev. 3 aparece en 2001. El archivo de 2012 ya trae `ciiu4` con códigos Rev. 4 (divisiones 10–14, que no existen en la Rev. 3). `dane-data` asigna la revisión según la variable de cada año: Rev. 2 hasta 2000, Rev. 3 en 2001–2011 y Rev. 4 desde 2012. |
238
+ | Cobertura por división (EAM, archivos de 1995 en adelante) | La descripción de cada archivo dice que comprende "desde la división 11 a la 33 de la CIIU REV.4", también en los de 1995–2011. | Los archivos de 1995–2000 están en Rev. 2 (divisiones 31–39) y los de 2001–2011 en Rev. 3 (15–37). Los años en Rev. 4 incluyen la división 10, alimentos, que es el 18 % de los establecimientos en 2012–2024; el de 2012 incluye además las divisiones 58, 59 y 73. `dane-data` conserva todas las divisiones de cada archivo. |
239
+ | Títulos de los catálogos 871 y 888 (EAM) | El título de ambos catálogos en el DDI es "EAM_2022". | Sus archivos son de 2023 (871) y 2024 (888), como dice la versión de cada archivo. `dane-data` descarga 2023 del 871 y 2024 del 888. |
240
+ | `FEX_C_2011` → `fex` (GEIH 2008 y 2011, archivos CSV) | Los CSV de los catálogos 206 (2008) y 182 (2011) deberían traer el mismo factor de expansión que las versiones TXT y SPSS de esos catálogos. | Los CSV perdieron el punto decimal y traen puntos de miles: `849.491.032.387.525` es 84,9491032387525 en el TXT. Como la parte entera tiene de 1 a 5 dígitos, el valor no se puede recuperar del CSV. Algunos archivos traen además filas con un campo de más. Se confirmó persona por persona en marzo de 2011 contra el TXT. Para esos años `dane-data` descarga la versión TXT (tabulada) del mismo catálogo. Si en la carpeta solo están los CSV dañados, esos meses se omiten con un aviso para volver a descargarlos. |
241
+ | Septiembre de 2021 (GEIH, catálogo 701, `Septiembre.csv (2) (1).zip`) | Se publica como CSV. | Los archivos vienen separados por espacios, y el texto libre también tiene espacios, así que las columnas no se pueden separar. `dane-data` descarga ese mes en Stata (`Septiembre.zip`, del mismo catálogo). Si solo está el CSV, el mes se omite con un aviso para volver a descargarlo. |
242
+ | `PETI` → `pet` (empalme, octubre de 2012, catálogo 760) | Las cifras publicadas de la serie con proyecciones del CNPV 2018 usan la PET de 15 años o más en todos los meses. | El archivo de octubre de 2012 marca la PET con la definición anterior (12+ urbano, 10+ rural): la PET sube 9 % frente a septiembre y aparecen ~400 mil ocupados menores de 15 años. Con la PET del archivo, la TGP de ese mes saldría 64,5 % frente al 69,1 % publicado. `dane-data` aplica a todo el empalme la PET de 15 años o más (`harmonize(..., edad_minima_pet=15)`), lo que solo cambia octubre de 2012: así los 132 meses del empalme coinciden con el anexo del DANE. |
243
+ | `P6020` → `sexo` (empalme, abril y mayo de 2017, catálogo 763) | Las cifras mensuales por sexo del anexo (hoja "Total_nacional_IML_Sexo") suman el total nacional. | Son todos los hogares de Putumayo de esos meses (320 y 359 personas): solo traen edad y factor de expansión, y el resto de las características (sexo, parentesco, salud, educación) viene en blanco, también en las versiones Stata y SPSS del mismo ZIP. No se pueden recuperar: no están en ningún otro mes del empalme ni en la serie principal, y el catálogo de nuevos departamentos de 2017 (562) no tiene Mocoa en abril ni mayo (posiblemente por la avalancha del 31 de marzo; sin confirmar). `dane-data` deja `sexo` vacío para ellos: los totales coinciden con lo publicado, pero los ocupados de cada sexo quedan cerca de 0,1 % por debajo (unos 16 mil en total). |
244
+
245
+ ## Estructura
246
+
247
+ ```
248
+ src/dane_data/
249
+ ├── config.py rutas (set_paths, variables de entorno, ~/.config/dane_data/config.json)
250
+ ├── _io.py lectura robusta de CSV/TXT del DANE
251
+ ├── stats.py tablas, medias y cuantiles ponderados
252
+ ├── referencias.py SMLV, departamentos, diccionarios DDI (data/*.json)
253
+ ├── geih/ downloader.py · loader.py · indicadores.py
254
+ ├── eam/ downloader.py · loader.py · indicadores.py
255
+ ├── cli.py comando `dane-data`
256
+ └── app/ aplicación Streamlit (Inicio.py + paginas/)
257
+ ```
258
+
259
+ Los descargadores provienen de los repositorios `GEIH` y `EAM` originales; se adaptaron para reportar
260
+ progreso y permitir cancelar.
261
+
262
+ ## Licencia y cita
263
+
264
+ Código: MIT. Datos: DANE, Archivo Nacional de Datos (ANDA). Cite como
265
+ *DANE. Gran Encuesta Integrada de Hogares (GEIH) [microdatos]* / *Encuesta Anual Manufacturera (EAM) [microdatos]*.
@@ -0,0 +1,36 @@
1
+ dane_data/__init__.py,sha256=5v96ERKIJxRMuUYmclw_adcwM19jDYSPiina1PPS5RQ,882
2
+ dane_data/__main__.py,sha256=HubOiSmLeI9sx-7g6L4nyybNFX7G3b-DwF9MqyBBnr0,128
3
+ dane_data/_hooks.py,sha256=V2k-wbrDyayKdJS-kKy_Adr5kiLH0HAvx2yRIwrSEAU,728
4
+ dane_data/_io.py,sha256=oQDSQsHja6tZVFgxjjvZlFMMsTyAaX_Pb55upIvhksU,5163
5
+ dane_data/cli.py,sha256=fjX-OeqE2CQUmhk8dInS32sSw3Xr56OXMNDuQCG0k84,6194
6
+ dane_data/config.py,sha256=vAt-Yl7CAJ7tfbnysOdOFke-NrMk0TlLV1j79Vkw3Qw,2816
7
+ dane_data/referencias.py,sha256=CgdA2C80Xx-0yMgWORgZwbO6DUwDQORXz3Ofjd0eQ4g,3428
8
+ dane_data/stats.py,sha256=jbjD-uQlRJm73JrOCEZG9ocRvrEcePurRnpxtERvd-0,4110
9
+ dane_data/app/Inicio.py,sha256=JTogR--I8G0IYgxzy9RUdWKZbkZCv1I-Eb1eJ1vb3kw,995
10
+ dane_data/app/_ui.py,sha256=JQL0tIseI7poPCBzcbPsZCacK2JOwntP6eGcNNxfGKg,17269
11
+ dane_data/app/.streamlit/config.toml,sha256=Ye75rCxhMpCNioWa9aPJ5znWo9twIPHNoIwfeRNABoY,103
12
+ dane_data/app/paginas/descargar.py,sha256=w4ilj6c1S7mchq-C6oKf3MbcPYhrPks1t3QtovxBNcc,5334
13
+ dane_data/app/paginas/diccionario.py,sha256=bOJNzVJRd3IlhLZKa9PThCXlEiKcVse5-zpKoYRi8R0,3262
14
+ dane_data/app/paginas/eam_industria.py,sha256=EVy585U_bmIe7xD9qAK2N8lOss5yyIacCU3EefqTX70,6445
15
+ dane_data/app/paginas/eam_microdatos.py,sha256=JPfQMLcofoh9YVErbZPF2hpIxLeBC6mmjmmYtOAgLCU,2895
16
+ dane_data/app/paginas/geih_explorar.py,sha256=nxpZgzi8HB-3-gJhP7RCFNeZzHqHlrRf-TKvXokShkI,12375
17
+ dane_data/app/paginas/geih_mercado.py,sha256=SmqvVtr9GzgP91aPK2KkIRSBJiUyy0Og4vew0FERJpk,7246
18
+ dane_data/app/paginas/inicio.py,sha256=vvIfmwLWEavhLut0Z-L9qGQO6f9FT2J4IZN9qwOAgR4,4159
19
+ dane_data/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ dane_data/data/eam_diccionario.json,sha256=lQ7-bzLKnCez17GywM-_qsFLg9Pj2LikTZ0h2HsxO_g,130536
21
+ dane_data/data/geih_diccionario.json,sha256=eK-rnUEYFCP_H53BdlTlaYz0-U0nJqRuh8-Pz19rHHM,259500
22
+ dane_data/eam/__init__.py,sha256=xLKB5RKAchrKcdDZLbAerrtc_OJXqA4recFVkFq7ht4,1583
23
+ dane_data/eam/downloader.py,sha256=3gb8KAz4IQ6hDEGhF9rzvvtiR8nwolg1_EO-44exl-A,20806
24
+ dane_data/eam/indicadores.py,sha256=EifypK15koVj5NED-diXTF7QFmSle-23meWiOyINjcA,6653
25
+ dane_data/eam/loader.py,sha256=xiUJXVPObocptfNSmvWLuKZJ8Ec04t22j-2FBAq7NGo,9419
26
+ dane_data/geih/__init__.py,sha256=DZNj9n4Mqpcwu4MBAO-Vas2Xpx_IRPUtAJ1u6itcNXk,2600
27
+ dane_data/geih/agregados.py,sha256=N0XxjprgSbObbfAvk49xz22IkiFC8ljqQZxqlKHXB8g,16176
28
+ dane_data/geih/downloader.py,sha256=gaHLC60dXJlAnHU2c0znFw_uOK7eh7GmYXq-k5SZNo4,29704
29
+ dane_data/geih/indicadores.py,sha256=BQmHy7JMsdcO2j5rirw2ixbKn1dB1SVG22qe__9rq8w,3924
30
+ dane_data/geih/loader.py,sha256=6-Fukt_SkpF32Q7MbDh_3HERtXrgypfWGueuGsvRkj0,27190
31
+ dane_data-0.2.1.dist-info/licenses/LICENSE,sha256=ExtFUgcCPeaUEoirJXg4cCWPotCZnB7pl0bK_tJYw_g,1069
32
+ dane_data-0.2.1.dist-info/METADATA,sha256=513yOdd-4R0oobFjz6cNJdJcFx-JEfgCZsPk_rn9s5w,17964
33
+ dane_data-0.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
34
+ dane_data-0.2.1.dist-info/entry_points.txt,sha256=rvai3Z4BMaEdSB0EYetvQdTCyCtQfovmDB9cSW0JFvQ,49
35
+ dane_data-0.2.1.dist-info/top_level.txt,sha256=PXaoipgjtjJxKqp3WiPwUYZbZ4eAkqBRUH4MmoOTJP0,10
36
+ dane_data-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dane-data = dane_data.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luis Ramirez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ dane_data