Lección 2 de 18· Orientación: qué vas a construir
En la lección anterior conociste el objetivo: terminar el proyecto con un pipeline ETL reproducible y un dataset analítico que puedas mostrar en tu portafolio. Antes de escribir una sola línea de Polars, necesitamos un plano: qué etapas tiene el pipeline, qué produce cada una y por qué decidimos separarlas así.
Esta lección es el mapa que vas a seguir durante los cinco hitos. No hay código todavía —es una decisión de diseño consciente—. Un buen ingeniero de datos piensa la arquitectura antes de implementarla, igual que un arquitecto dibuja planos antes de levantar paredes. Si entiendes el flujo completo aquí, cada notebook posterior será "rellenar una etapa que ya tiene sentido" en vez de código suelto sin contexto.
Al terminar sabrás:
Un pipeline ETL toma datos crudos y dispersos y los convierte en datos limpios, confiables y listos para analizar. El nombre clásico es ETL —Extract, Transform, Load— pero los pipelines modernos añaden una etapa que en la práctica decide si confías o no en el resultado: la validación de calidad. Por eso en este proyecto trabajamos con cuatro etapas:
| Etapa | Pregunta que responde | Entrada | Salida (artefacto) |
|---|---|---|---|
| Extract | ¿De dónde vienen los datos? | Fuentes heterogéneas (CSV, JSON, dicts en memoria) | DataFrames crudos, sin tipar |
| Transform | ¿Cómo los dejo limpios y enriquecidos? | DataFrames crudos | DataFrame único, normalizado y enriquecido (lazy) |
| Validate | ¿Puedo confiar en ellos? | DataFrame transformado | El mismo DataFrame + un reporte de calidad (pasa / falla) |
| Load | ¿Cómo los dejo listos para consultar? | DataFrame validado | Parquet particionado, consultable con DuckDB |
La regla mental clave: cada etapa recibe un artefacto y produce otro artefacto bien definido. Esa frontera limpia entre etapas es lo que hace que el pipeline sea testeable, depurable y reproducible.
Así se ven las cuatro etapas, las fuentes de entrada y los artefactos intermedios que pasan de una etapa a la siguiente. Sigue las flechas de izquierda a derecha: los datos entran crudos por la izquierda y salen como una capa analítica consultable por la derecha.
Fíjate en dos detalles del diagrama que guían todo el proyecto:
La tentación del principiante es escribir un único script gigante: lee, limpia, junta y guarda, todo de corrido. Funciona... hasta que deja de funcionar. Separar en etapas con artefactos claros te da cuatro ventajas concretas:
Esta separación es exactamente lo que vas a construir hito por hito: cada hito del curso es una etapa del diagrama, y la lección de orquestación final (Hito 5) las encadena en una sola función reproducible.
Este proyecto usa dos motores, y no por capricho: cada uno brilla en una parte distinta del flujo. Si vienes de los cursos de Polars y DuckDB, esto une las dos piezas.
Polars hace la transformación (etapa Transform). Polars es un DataFrame en Rust con una API de expresiones y un modo lazy. En modo lazy no ejecutas las operaciones de inmediato: describes qué quieres (filtrar, normalizar, hacer joins, agregar) y Polars construye un plan de consulta que un optimizador reordena —empuja los filtros hacia abajo, elimina columnas que no usas, fusiona operaciones— antes de ejecutar todo de una pasada con .collect(). Es el motor ideal para la lógica de limpieza y enriquecimiento fila a fila y columna a columna.
DuckDB hace el análisis (etapa Load + consulta). DuckDB es una base de datos analítica columnar que ejecuta SQL directamente sobre archivos Parquet, sin necesidad de cargar un servidor ni importar los datos a ninguna parte. Una vez que el pipeline escribe la capa analítica en Parquet, DuckDB la consulta con SQL puro —agregaciones, ventanas, joins entre archivos— a gran velocidad. Es el motor ideal para responder las preguntas de negocio sobre el resultado final.
La regla de decisión que usaremos en el proyecto:
| Necesitas… | Herramienta | Por qué |
|---|---|---|
| Limpiar, normalizar, enriquecer registro a registro con un plan optimizado | Polars (lazy) | API de expresiones + optimizador del plan lazy |
| Persistir el resultado en un formato analítico abierto | Parquet | Columnar, comprimido, portátil, lo lee todo el ecosistema |
| Consultar la capa final con SQL analítico | DuckDB | SQL columnar directo sobre Parquet, sin servidor |
En una frase: Polars construye los datos; DuckDB los interroga. Polars y DuckDB además interoperan muy bien (DuckDB puede leer un DataFrame de Polars y viceversa), así que no estás pegando dos mundos a la fuerza: es una combinación que se usa en producción real.
Un pipeline solo es interesante si los datos se parecen a los de un trabajo real. Por eso el dominio del proyecto es un e-commerce: una tienda online con pedidos, clientes y un catálogo de productos. Es un escenario que cualquier revisor entiende al instante y que tiene la complejidad justa: varias fuentes, en distintos formatos, que hay que unir y limpiar.
Las tres fuentes llegan en formatos heterogéneos a propósito —así practicas la ingesta real, donde nada viene perfecto—. Todas se generan en memoria dentro de los notebooks (sin red, sin archivos externos), pero imitan de dónde vendrían en la vida real:
| Fuente | Formato simulado | Grano (1 fila = …) | Columnas clave |
|---|---|---|---|
Pedidos (orders) | JSON (como una API) | Una línea de pedido (un producto dentro de un pedido) | order_id, customer_id, product_id, quantity, unit_price, order_ts |
Clientes (customers) | CSV (export de un CRM) | Un cliente | customer_id, name, email, country, signup_date |
Catálogo (products) | Lista de dicts (en memoria) | Un producto | product_id, product_name, category, cost |
Entender el grano de cada fuente es lo más importante de esta tabla, y la causa número uno de bugs en ETL. Fíjate: la tabla de pedidos está a grano de línea de pedido, no de pedido. Un mismo order_id puede aparecer en varias filas si el cliente compró tres productos distintos. En cambio, customers y products están a grano de una entidad por fila. Cuando hagamos los joins en el Hito 2, mezclar estos granos a la ligera es lo que duplica filas y arruina las sumas —lo veremos con cuidado.
Las relaciones entre las fuentes son las típicas de un modelo dimensional: la tabla de hechos (orders) referencia a las dimensiones (customers, products) por sus claves.
Un pipeline no se construye "porque sí": existe para responder preguntas de negocio. Definirlas ahora nos da el criterio de éxito del proyecto. Si al final, con DuckDB, podemos responder estas preguntas de forma limpia y rápida, el pipeline cumplió su objetivo:
unit_price (lo que paga el cliente) frente a cost (lo que cuesta el producto), ¿cuáles son los productos más rentables?Cada una de estas preguntas impone requisitos sobre las etapas anteriores: para calcular el margen necesitamos haber unido orders con products (Transform); para confiar en los ingresos necesitamos garantizar que no hay unit_price negativos ni nulos (Validate); para consultar rápido por mes conviene particionar el Parquet por fecha (Load). Las preguntas de negocio del final dictan las decisiones de diseño del principio —ese es el modo de pensar de un ingeniero de datos.
Antes de avanzar al primer hito, verifica que las decisiones de arquitectura están claras. Responde este breve cuestionario sobre el diseño del pipeline.
Gratis