1. Objetivo del proceso de descarga

El script 01_descarga_datos.R tiene como finalidad realizar la adquisición masiva, controlada y reproducible de los registros históricos de Saber 11 disponibles a través de la API de datos abiertos de Socrata, almacenarlos progresivamente en formato Parquet y verificar su integridad antes de consolidarlos en un único archivo de trabajo.

A diferencia de una descarga simple, esta etapa incorpora controles explícitos sobre la continuidad de las páginas, la presencia y unicidad de los identificadores técnicos, los posibles solapamientos entre bloques, la correspondencia entre el número de registros almacenados y el número reportado por la fuente, y la estabilidad de los extremos de la extracción.

El principio metodológico central es:

La capa de adquisición debe conservar fielmente la información entregada por la fuente y demostrar, mediante controles reproducibles, que la extracción local es completa e íntegra antes de iniciar cualquier auditoría, limpieza o transformación.

Por esta razón, el script no modifica variables sustantivas de Saber 11. Su responsabilidad es construir una capa raw verificable que pueda utilizarse posteriormente por 02_auditoria_datos.R.

2. Arquitectura general de la extracción

El proceso utiliza principalmente los paquetes:

  • jsonlite, para consultar y procesar respuestas JSON;
  • arrow, para escribir, leer y consolidar archivos Parquet;
  • dplyr, incorporado posteriormente para la comprobación de unicidad global.

La fuente de datos se consulta mediante el endpoint SODA3 de Saber 11:

base_url_v3 <- "https://www.datos.gov.co/api/v3/views/kgxf-xxbe/query.json"

La extracción fue configurada con páginas de:

tamano_pagina <- 20000

registros.

Cada página se almacena de forma independiente en:

data/raw/saber11_completo/

utilizando nombres secuenciales del tipo:

saber11_pagina_000001.parquet
saber11_pagina_000002.parquet
...

Esta arquitectura evita mantener la base completa en memoria durante la descarga y permite reanudar el proceso si ocurre una interrupción.

3. Campos técnicos utilizados como controles de integridad

El script exige la presencia de cuatro campos internos de Socrata:

  • :id
  • :version
  • :created_at
  • :updated_at

El campo :id se utiliza como identificador técnico de fila para las verificaciones de integridad de la adquisición.

Para cada página se verifica que:

  1. el campo :id exista;
  2. no contenga valores faltantes;
  3. no contenga cadenas vacías;
  4. no presente duplicados dentro de la página;
  5. no se solape con los identificadores de la página inmediatamente anterior.

Estos controles buscan impedir que una página incompleta, corrupta o repetida sea incorporada silenciosamente a la capa raw.

4. Estrategia de tolerancia a fallos

La función descargar_pagina() permite hasta cinco intentos por página.

Cuando ocurre un error temporal de conexión, el script captura el error mediante tryCatch(), informa el intento fallido, aplica una espera progresiva mediante Sys.sleep(3 * intento) y vuelve a consultar la misma página.

Si los cinco intentos fallan, la ejecución se detiene.

Esta estrategia es adecuada para una extracción de varios millones de registros porque distingue entre fallos transitorios de red y fallos persistentes que requieren intervención.

5. Incidencias observadas durante la descarga

Durante la ejecución ocurrieron fallos temporales en las páginas:

  • 94
  • 108
  • 117
  • 205
  • 337

Los mensajes registrados incluyeron:

  • Timeout of 600 seconds was reached
  • Failure when receiving data from the peer

En todos estos casos el mecanismo de reintentos permitió recuperar posteriormente la página y continuar la extracción.

Interpretación

Estos eventos no constituyeron pérdida de información porque el script no avanzó a la página siguiente hasta obtener correctamente el bloque solicitado.

La presencia de estos fallos demuestra la utilidad práctica del mecanismo de reintentos.

6. Guardado incremental en formato Parquet

Cada página recuperada correctamente se escribe inmediatamente mediante write_parquet().

El diseño:

  • reduce el consumo de memoria;
  • conserva el progreso ya realizado;
  • facilita la reanudación;
  • permite validar páginas individualmente;
  • evita repetir una descarga completa por una interrupción tardía;
  • proporciona una capa raw modular y auditable.

El contador filas_acumuladas se actualiza después de cada escritura exitosa.

7. Resultado de la descarga paginada

La extracción final produjo:

Indicador Resultado
Páginas con datos 356
Tamaño máximo de página 20.000
Registros acumulados 7.109.704
Filas de la última página 9.704
Página siguiente consultada 357
Registros en página 357 0

Las páginas 1 a 355 contienen 20.000 registros y la página 356 contiene 9.704.

La suma es:

\[ 355 \times 20.000 + 9.704 = 7.109.704 \]

La página 357 no devolvió registros, proporcionando una señal adicional de que se alcanzó el final de la fuente paginada.

8. Manejo correcto de la página vacía

Durante el desarrollo se reforzó la condición que identifica el final de la fuente:

if (!is.data.frame(bloque) || length(bloque) == 0 || nrow(bloque) == 0) {
  cat("La página", pagina, "no contiene registros. Finaliza la descarga.\n")
  break
}

La ejecución posterior confirmó:

La página 357 no contiene registros. Finaliza la descarga.

Interpretación

El cambio mejora la robustez del extractor porque permite interpretar correctamente una respuesta vacía como señal de terminación.

9. Capacidad de reanudación

El script reconoce archivos previamente descargados.

Antes de reanudar:

  1. identifica las páginas existentes;
  2. extrae sus números;
  3. las ordena;
  4. verifica que no existan huecos;
  5. identifica la última página almacenada;
  6. consulta nuevamente esa misma página en Socrata;
  7. compara sus :id con la copia local;
  8. solo continúa si ambas versiones coinciden.

En la ejecución observada, después de completar 356 páginas, la lógica de reanudación informó:

Reanudando desde la página: 357 | Filas existentes: 7109704

Esto confirma que el sistema reconoció correctamente todo el trabajo almacenado.

10. Protección frente a cambios de la fuente

El script compara la última página local con la misma página consultada nuevamente en Socrata.

Si los vectores de :id no coinciden, el proceso se detiene.

Justificación

La paginación de una fuente remota puede generar inconsistencias si el dataset cambia durante una extracción prolongada.

Esta comprobación busca evitar que una reanudación mezcle estados diferentes de la fuente.

No elimina por sí sola todos los riesgos posibles de mutación concurrente, pero constituye una salvaguarda importante.

11. Validación de continuidad de archivos

Una vez terminada la descarga se vuelven a enumerar los archivos Parquet y se comprueba que los números de página formen exactamente la secuencia:

1, 2, 3, ..., 356

Si existe cualquier salto, el proceso se detiene.

Resultado

La secuencia completa superó la validación.

No se detectaron páginas faltantes en la capa local.

12. Segunda lectura de los identificadores

La validación final vuelve a recorrer los 356 archivos.

Para reducir el uso de memoria se lee únicamente:

col_select = ":id"

En cada archivo se comprueba nuevamente:

  • que no esté vacío;
  • que no existan :id faltantes;
  • que no existan :id vacíos;
  • que no existan duplicados dentro del archivo;
  • que no exista solapamiento con la página consecutiva anterior.

Interpretación

El control se realiza tanto durante la adquisición como después de la escritura física.

Esto permite detectar problemas que pudieran haberse producido entre la descarga y el almacenamiento.

13. Validación del tamaño de las páginas

El script comprueba que todas las páginas intermedias contengan exactamente 20.000 registros.

También verifica que la última página:

  • tenga al menos un registro;
  • no exceda 20.000;
  • contenga exactamente el número esperado de filas.

Se definió:

filas_esperadas <- 7109704
paginas_esperadas <- ceiling(filas_esperadas / tamano_pagina)

Por tanto:

  • páginas esperadas = 356;
  • filas esperadas en la última página = 9.704.

Ambos valores coincidieron con los archivos almacenados.

14. Triple validación del número total de registros

El proceso contrasta tres cantidades:

\[ N_{local} = N_{fuente} = N_{esperado} \]

Los resultados fueron:

\[ 7.109.704 = 7.109.704 = 7.109.704 \]

Fuente del conteo Registros
Archivos Parquet locales 7.109.704
Conteo actual de Socrata 7.109.704
Total esperado configurado 7.109.704

Interpretación

La coincidencia exacta constituye evidencia fuerte de completitud cuantitativa.

No equivale por sí sola a una prueba de identidad fila por fila; por eso se incorporan controles adicionales mediante :id.

15. Consulta independiente del conteo en Socrata

El total de la fuente se obtiene mediante una consulta:

SELECT count(*) AS total_registros

El código comprueba que la respuesta sea válida y que total_registros pueda convertirse a numérico.

Resultado

Socrata reportó:

7.109.704 registros.

16. Validación de los extremos de la extracción

Además del conteo total, el script vuelve a consultar:

  • la primera página;
  • la última página.

Luego compara los vectores de :id obtenidos desde la fuente con los almacenados localmente.

Ambas comparaciones fueron superadas.

Importancia metodológica

La prueba de extremos complementa la continuidad de páginas, el conteo total, las validaciones de tamaño y los controles de identificadores.

17. Marcador de descarga completa

El archivo:

_DESCARGA_COMPLETA.txt

se crea únicamente después de superar las verificaciones finales.

El marcador registra:

  • número de filas locales;
  • número de filas de la fuente;
  • número de filas esperadas;
  • número de páginas;
  • fecha y hora.

Esto evita confundir una descarga interrumpida con una descarga validada.

18. Resultado de la validación final

La consola reportó:

VALIDACIÓN CORRECTA |
Páginas: 356 |
Filas locales: 7109704 |
Filas fuente: 7109704 |
Filas esperadas: 7109704 |
Última página: 9704 |
Columnas esperadas: 55

Este resultado resume la primera capa de validación de la adquisición.

19. Necesidad de validar la unicidad global de :id

La comprobación inicial garantizaba:

  • unicidad dentro de cada página;
  • ausencia de solapamiento entre páginas consecutivas.

Sin embargo, no descartaba formalmente que un :id de una página antigua pudiera reaparecer muchas páginas después.

Por esta razón se añadió una validación global.

20. Unicidad global del identificador técnico

Los archivos se abrieron como un único dataset mediante open_dataset().

Se calcularon:

filas_totales = n()
ids_unicos = n_distinct(`:id`)

Resultado:

Métrica Resultado
Filas totales 7.109.704
:id únicos 7.109.704
Diferencia 0

La consola confirmó:

UNICIDAD GLOBAL CORRECTA | Filas: 7109704 | IDs únicos: 7109704

Conclusión

No existe evidencia de duplicación técnica de :id en toda la extracción.

21. Alcance correcto de la unicidad técnica

La igualdad:

\[ N_{filas} = N_{:id\ únicos} \]

demuestra que cada fila descargada posee un identificador técnico Socrata diferente.

No demuestra necesariamente que dos filas con contenido sustantivo similar correspondan a entidades académicas diferentes.

Por tanto:

No deben eliminarse registros de la capa raw únicamente porque varias variables sustantivas sean idénticas si sus :id son diferentes.

Una posible duplicación semántica debe investigarse posteriormente mediante claves propias de Saber 11.

22. Evidencia acumulada de integridad de ingestión

Al finalizar la etapa se cuenta con las siguientes comprobaciones:

  1. 356 páginas continuas.
  2. 7.109.704 filas almacenadas.
  3. 7.109.704 filas reportadas por Socrata.
  4. 7.109.704 filas esperadas.
  5. 9.704 registros en la última página.
  6. página 357 sin registros.
  7. sin páginas intermedias incompletas.
  8. sin :id faltantes dentro de páginas.
  9. sin duplicados de :id dentro de páginas.
  10. sin solapamientos entre páginas consecutivas.
  11. primera página local coincidente con la fuente.
  12. última página local coincidente con la fuente.
  13. 7.109.704 :id globalmente únicos.

Conclusión

En conjunto, estas verificaciones proporcionan evidencia fuerte de que la descarga es completa desde el punto de vista cuantitativo y no presenta duplicación técnica inducida por la estrategia de paginación.

23. Consolidación de los bloques Parquet

Después de validar las páginas individuales se construyó:

D:/Proyectos_IA/prueba_saber_11/data/raw/saber11_completo.parquet

Antes de consolidar se verificó:

  • que existieran exactamente 356 archivos;
  • que correspondieran a las páginas 1:356;
  • que el archivo consolidado no existiera previamente.

Este último control evita sobrescribir accidentalmente una versión anterior.

24. Consolidación mediante Arrow

Los bloques se abren como un dataset lógico mediante:

dataset_paginas <- open_dataset(
  archivos_paginas,
  format = "parquet"
)

y posteriormente se escriben en un único archivo mediante:

write_parquet(
  dataset_paginas,
  archivo_final
)

La consola confirmó:

ARCHIVO CONSOLIDADO CREADO

Ventaja

Arrow permite trabajar con millones de registros sin cargar simultáneamente todas las páginas en memoria como un único data.frame.

25. Dimensiones del archivo consolidado

La inspección final mediante glimpse() reportó:

  • 7.109.704 filas
  • 55 columnas

La dimensión coincide con el total validado durante la extracción.

26. Estructura inicial observada

La inspección final confirma variables correspondientes a:

Metadatos técnicos

  • :id
  • :version
  • :created_at
  • :updated_at

Identificación temporal

  • periodo

Estudiante

  • estu_consecutivo
  • variables de documento, género, nacimiento, residencia, presentación y nacionalidad.

Establecimiento educativo

  • cole_codigo_icfes
  • códigos DANE;
  • nombre y sede;
  • calendario;
  • jornada;
  • naturaleza;
  • ubicación.

Contexto familiar

  • educación de madre y padre;
  • estrato;
  • tamaño del hogar;
  • computador;
  • internet;
  • automóvil;
  • lavadora.

Resultados académicos

  • desemp_ingles
  • punt_ingles
  • punt_matematicas
  • punt_sociales_ciudadanas
  • punt_c_naturales
  • punt_lectura_critica
  • punt_global

27. Tipos de datos observados

En la inspección del consolidado las variables aparecen inicialmente como character.

Esto es apropiado en la capa raw porque conserva la representación recibida desde la fuente.

La conversión de puntajes, fechas, categorías o códigos no debe mezclarse con la adquisición.

Esas decisiones corresponden a la auditoría y limpieza posteriores.

28. Separación de responsabilidades dentro del pipeline

La arquitectura del proyecto queda:

01_cargar_json.R
        ↓
02_auditoria_datos.R
        ↓
03_limpieza_imputacion.R
        ↓
04_construccion_panel.R
        ↓
05_feature_engineering.R
        ↓
06_dataset_supervisado.R
        ↓
07_modelado.R

La responsabilidad de 01_cargar_json.R es:

adquirir, almacenar, validar y consolidar la información original sin alterar su contenido sustantivo.

El archivo 01 no debe decidir qué registros eliminar, cómo imputar, qué categorías homologar, qué puntajes transformar, qué colegios integrar al panel o qué periodos utilizar en el modelo.

29. Fortalezas metodológicas del script

Reanudabilidad

Una caída de conexión no obliga a repetir millones de registros ya almacenados.

Persistencia incremental

Cada bloque válido queda guardado inmediatamente.

Controles antes y después de guardar

La integridad no se evalúa únicamente durante la descarga.

Validación externa

El total local se contrasta contra la fuente Socrata.

Identidad técnica

Se verifica finalmente la unicidad global de :id.

Protección contra sobrescrituras

Se evita reemplazar silenciosamente el consolidado existente.

Trazabilidad

El marcador final registra el estado de la descarga.

30. Limitaciones que deben reconocerse

A pesar de los controles realizados, el script no prueba por sí solo:

  • ausencia de duplicados semánticos;
  • calidad sustantiva de las variables;
  • validez de los códigos ICFES;
  • ausencia de valores atípicos;
  • comparabilidad temporal de puntajes;
  • consistencia de categorías a través de los años;
  • ausencia de registros académicamente repetidos con distintos :id.

Estas cuestiones corresponden a 02_auditoria_datos.R.

31. Hallazgo metodológico central

El resultado más importante de esta fase no es únicamente haber descargado más de siete millones de filas.

Lo relevante es que se construyó una capa raw con evidencia explícita de:

  • completitud cuantitativa;
  • continuidad de paginación;
  • tolerancia a interrupciones;
  • ausencia de duplicación técnica global;
  • correspondencia con el conteo de la fuente;
  • estabilidad de las páginas de control.

Esto permite que los análisis posteriores partan de una base cuya adquisición puede ser defendida y reproducida.

32. Errores que deben evitarse después de esta etapa

No se recomienda:

  1. eliminar inmediatamente los 356 archivos originales después de crear el consolidado;
  2. sobrescribir el archivo raw durante la limpieza;
  3. interpretar :id como identificador del estudiante;
  4. considerar la unicidad de :id como prueba de ausencia de duplicados sustantivos;
  5. convertir o corregir valores directamente dentro de la única copia raw;
  6. mezclar páginas descargadas en diferentes momentos sin validación.

La capa raw debe conservarse como evidencia del origen de los datos.

33. Preguntas técnicas que puede formular un jurado

¿Cómo se comprobó que la descarga estaba completa?

Se comparó el número de filas realmente almacenadas con el total esperado y con un conteo independiente obtenido directamente desde Socrata. Los tres valores fueron exactamente 7.109.704.

¿Cómo se evitó perder toda la descarga ante fallos de red?

Cada página válida se escribió inmediatamente en formato Parquet y el proceso puede reanudarse desde la última página almacenada después de verificar que continúa coincidiendo con la fuente.

¿Qué ocurrió con los timeouts?

El extractor permite hasta cinco intentos por página y utiliza una espera progresiva. Los fallos observados fueron transitorios y las páginas terminaron recuperándose correctamente.

¿Cómo se comprobó que una página no estaba repetida?

Durante la descarga se verificó el solapamiento entre páginas consecutivas. Posteriormente se ejecutó una prueba global que confirmó 7.109.704 :id diferentes para 7.109.704 filas.

¿La unicidad de :id demuestra que no hay estudiantes repetidos?

No. :id es un identificador técnico de fila de Socrata. La duplicación semántica debe investigarse posteriormente usando variables propias del dominio.

34. Resultado final de 01_cargar_json.R

El proceso puede resumirse mediante:

\[ \boxed{ N_{local} = N_{fuente} = N_{esperado} = 7.109.704 } \]

con:

\[ \boxed{ N_{:id\ únicos} = 7.109.704 } \]

y:

\[ \boxed{ 356\ páginas } \]

La última página contiene:

\[ \boxed{ 9.704\ registros } \]

mientras la página 357 no contiene observaciones.

35. Conclusión general

La ejecución de 01_cargar_json.R produjo una extracción masiva completa y técnicamente validada de la fuente histórica de Saber 11 utilizada en el proyecto.

La arquitectura de descarga demostró capacidad para tolerar fallos transitorios, reanudar la adquisición, impedir huecos en la secuencia de páginas, verificar la identidad técnica de los registros y contrastar el resultado local con el estado reportado por Socrata.

La comprobación global posterior estableció que las 7.109.704 filas poseen 7.109.704 identificadores :id diferentes, por lo que no se encontró evidencia de duplicación técnica global causada por la paginación.

Finalmente, las 356 páginas fueron consolidadas en un único archivo Parquet de 7.109.704 filas y 55 columnas, preservando la información original como capa raw.

En consecuencia:

01_cargar_json.R puede considerarse cerrado como etapa de adquisición, validación y consolidación. El archivo resultante constituye una base de origen suficientemente controlada para iniciar 02_auditoria_datos.R, donde deben estudiarse la calidad sustantiva, los faltantes, la estructura temporal, los identificadores institucionales y la viabilidad longitudinal de Saber 11.