lección 5
Great Expectations: el framework profesional de validación
Instalación, Expectations, Suites, Data Sources y reportes HTML. El estándar de la industria para validación de datos.
⏱ 55 min
### De artesanal a industrial: por qué necesitas un framework
En la lección anterior construiste validadores con Python puro. Funcionan. Pero imagina que tu empresa tiene 47 tablas, cada una con 15-20 reglas de validación, y el equipo crece de 2 a 8 ingenieros. De repente necesitas: reportes que los no-técnicos puedan leer, historial de resultados, integración con Airflow, documentación automática de las reglas, alertas cuando algo cambia... Escribir todo eso a mano sería reinventar la rueda.
Aquí entra Great Expectations (GE). Es el framework open-source más usado para validación de datos en Python. Lo usan empresas como GitHub, Shopify, Heineken, y cientos más. La idea central es elegante: en vez de escribir código imperativo ("if len(df) == 0: raise..."), DECLARAS tus expectativas sobre los datos de forma legible: "espero que esta columna no tenga nulos", "espero que los valores estén entre 0 y 1000", "espero que haya al menos 100 filas".
Analogía: es la diferencia entre escribir tests unitarios a mano (funciones con asserts) y usar un framework de testing como pytest (fixtures, parametrize, reportes, plugins). GE es el "pytest de los datos".
### Paso 1: Instalar Great Expectations
La instalación es un simple pip install. Asegúrate de estar en tu entorno virtual:
1# 🟦 Windows (PowerShell) — con tu venv activado:2.venv\Scripts\activate3pip install great-expectations45# 🍎 Mac (Terminal/zsh) — con tu venv activado:6source .venv/bin/activate7pip install great-expectations89# Verificar la instalación:10python -c "import great_expectations as gx; print(f'GX version: {gx.__version__}')"
La instalación incluye muchas dependencias — puede tardar 1-2 minutos
Great Expectations es un paquete GRANDE (instala muchas dependencias). En un proyecto real, ponlo en tu requirements.txt o pyproject.toml. Si ves errores de compatibilidad, asegúrate de tener Python 3.9 o superior. La versión actual (1.x) cambió significativamente respecto a la 0.x, así que cuidado con tutoriales antiguos.
### Los conceptos clave de Great Expectations
GE tiene su propio vocabulario. Antes de escribir código, necesitas entender 4 conceptos fundamentales:
- 01.Expectation: una declaración sobre cómo DEBERÍAN ser los datos. Ej: "espero que la columna email no tenga nulos". Es el átomo de la validación.
- 02.Expectation Suite: un conjunto de Expectations agrupadas para un dataset concreto. Ej: "la suite de validación para la tabla pedidos" con 15 expectations.
- 03.Data Source: la conexión a los datos que quieres validar — un CSV, un DataFrame de Pandas, una tabla de PostgreSQL, un bucket de S3...
- 04.Validation Result: el resultado de ejecutar una Suite contra un Data Source. Incluye qué pasó, qué falló y estadísticas detalladas.
### Tu primera validación con Great Expectations
Vamos a hacer algo práctico: validar un DataFrame de pedidos usando GE. El flujo es: crear un contexto, definir las expectations, ejecutar la validación y ver los resultados.
1import great_expectations as gx2import pandas as pd34# 1. Crear un contexto efímero (in-memory, sin archivos de configuración)5context = gx.get_context()67# 2. Crear nuestros datos de ejemplo8pedidos = pd.DataFrame({9 'pedido_id': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010],10 'cliente': ['Ana', 'Luis', None, 'María', 'Pedro', 'Laura', 'Carlos', 'Eva', None, 'Javi'],11 'importe': [150.0, 25.0, 300.0, -10.0, 50.0, 75.0, 120.0, 0.0, 200.0, 45.0],12 'email': ['ana@mail.com', 'luis@mail.com', 'maria@mail.com', None, 'pedro@mail.com',13 'laura@', 'carlos@mail.com', 'eva@mail.com', 'iris@mail.com', None],14})1516# 3. Conectar datos como Data Source17data_source = context.data_sources.add_pandas("mi_source")18data_asset = data_source.add_dataframe_asset(name="pedidos")19batch_definition = data_asset.add_batch_definition_whole_dataframe("batch_pedidos")2021# 4. Crear una Expectation Suite22suite = gx.ExpectationSuite(name="suite_pedidos")2324# 5. Añadir Expectations (reglas de validación)25suite.add_expectation(26 gx.expectations.ExpectColumnValuesToNotBeNull(column="pedido_id")27)28suite.add_expectation(29 gx.expectations.ExpectColumnValuesToNotBeNull(column="importe")30)31suite.add_expectation(32 gx.expectations.ExpectColumnValuesToBeBetween(33 column="importe", min_value=0.01, max_value=5000034 )35)36suite.add_expectation(37 gx.expectations.ExpectColumnValuesToBeUnique(column="pedido_id")38)3940# Registrar la suite en el contexto41suite = context.suites.add(suite)4243# 6. Crear y ejecutar la validación44validation_definition = context.validation_definitions.add(45 gx.ValidationDefinition(46 name="validar_pedidos",47 data=batch_definition,48 suite=suite,49 )50)5152# Ejecutar con el DataFrame real53results = validation_definition.run(batch_parameters={"dataframe": pedidos})5455# 7. Ver resultados56print(f"¿Pasó todo? {results.success}")57print(f"Resultados por expectation:")58for result in results.results:59 estado = "✅" if result.success else "🔴"60 print(f" {estado} {result.expectation_config.type}: {result.result}")
Flujo completo de GE: contexto → datos → suite → validación → resultados
### Las Expectations más útiles
GE tiene más de 300 Expectations predefinidas. No necesitas conocerlas todas — con unas 15-20 cubres el 90% de los casos. Estas son las que usarás constantemente:
1# === COMPLETITUD ===2# Columna sin nulos3ExpectColumnValuesToNotBeNull(column="pedido_id")4# Columna con máximo X% de nulos5ExpectColumnValuesToNotBeNull(column="email", mostly=0.95) # 95% no-null67# === UNICIDAD ===8# Valores únicos9ExpectColumnValuesToBeUnique(column="pedido_id")10# Combinación de columnas única11ExpectCompoundColumnsToBeUnique(column_list=["fecha", "tienda", "producto"])1213# === VALIDEZ ===14# Valores en un rango15ExpectColumnValuesToBeBetween(column="edad", min_value=0, max_value=120)16# Valores de un conjunto17ExpectColumnValuesToBeInSet(column="estado", value_set=["activo", "inactivo", "baja"])18# Formato regex19ExpectColumnValuesToMatchRegex(column="email", regex=r".+@.+\..+")20# Tipo de dato21ExpectColumnValuesToBeOfType(column="importe", type_="float")2223# === VOLUMEN ===24# Número mínimo de filas25ExpectTableRowCountToBeBetween(min_value=100, max_value=100000)26# Número de columnas27ExpectTableColumnCountToEqual(value=8)2829# === DISTRIBUCIÓN ===30# Media dentro de un rango (detectar anomalías)31ExpectColumnMeanToBeBetween(column="importe", min_value=20, max_value=500)32# Mediana33ExpectColumnMedianToBeBetween(column="edad", min_value=25, max_value=55)
Las expectations más comunes — con estas 15 cubres la mayoría de escenarios
Consejo de senior: el parámetro "mostly" es tu mejor amigo. En datos reales, NUNCA hay 0% de nulos ni 0 duplicados. Siempre hay algo de ruido. mostly=0.98 significa "espero que al menos el 98% de los registros cumplan esta regla". Es la diferencia entre un check rígido que falla constantemente y uno realista que alerta cuando hay un problema REAL.
### Generar reportes HTML
Una de las killer features de GE es la generación automática de reportes HTML interactivos. Son documentos que puedes enviar al equipo de producto, al data analyst o al PM sin que necesiten saber Python. Muestran qué checks pasaron, cuáles fallaron, estadísticas de cada columna y el detalle de cada expectation.
1import great_expectations as gx23# Después de ejecutar la validación, generar reporte HTML4# (usando el context y results del ejemplo anterior)56# Opción 1: Data Docs (la forma integrada de GE)7# GE genera un sitio web estático completo con:8# - Historial de validaciones9# - Detalle de cada suite10# - Estadísticas de datos1112# Configurar Data Docs13context.add_or_update_data_docs_site(14 site_config={15 "class_name": "SiteBuilder",16 "store_backend": {17 "class_name": "TupleFilesystemStoreBackend",18 "base_directory": "data_docs/",19 },20 "site_index_builder": {"class_name": "DefaultSiteIndexBuilder"},21 },22 site_name="mi_sitio_docs",23)2425# Construir y abrir los docs26context.build_data_docs()27# Esto genera archivos HTML en data_docs/ que puedes abrir en el navegador2829# Opción 2: Resultado programático (para integrar en alertas)30print(f"Suite: {results.suite_name}")31print(f"Éxito global: {results.success}")32print(f"Total expectations: {len(results.results)}")33print(f"Pasaron: {sum(1 for r in results.results if r.success)}")34print(f"Fallaron: {sum(1 for r in results.results if not r.success)}")
Data Docs genera un sitio HTML completo con historial de validaciones
### Integrar GE en tu pipeline diario
El patrón más común es ejecutar GE como un paso del pipeline: DESPUÉS de la ingesta pero ANTES de cargar datos en producción. Si la validación falla, el pipeline se detiene y alerta al equipo. Si pasa, los datos se cargan con confianza.
1import great_expectations as gx2import pandas as pd3import sys45def pipeline_con_validacion():6 """Pipeline ETL con validación integrada."""78 # Paso 1: Ingesta9 print("📥 Ingesta de datos...")10 df = pd.read_csv("data/pedidos_hoy.csv")11 print(f" Ingestados {len(df)} registros")1213 # Paso 2: Validación con GE14 print("🔍 Validando datos...")15 context = gx.get_context()1617 # Configurar source y suite (normalmente se hace una vez)18 data_source = context.data_sources.add_pandas("pipeline_source")19 data_asset = data_source.add_dataframe_asset(name="pedidos_raw")20 batch_def = data_asset.add_batch_definition_whole_dataframe("batch")2122 suite = gx.ExpectationSuite(name="pedidos_ingesta")23 suite.add_expectation(gx.expectations.ExpectTableRowCountToBeBetween(min_value=50))24 suite.add_expectation(gx.expectations.ExpectColumnValuesToNotBeNull(column="pedido_id"))25 suite.add_expectation(gx.expectations.ExpectColumnValuesToBeUnique(column="pedido_id"))26 suite.add_expectation(gx.expectations.ExpectColumnValuesToBeBetween(27 column="importe", min_value=0.01, max_value=5000028 ))29 suite = context.suites.add(suite)3031 validation_def = context.validation_definitions.add(32 gx.ValidationDefinition(name="val_pedidos", data=batch_def, suite=suite)33 )3435 results = validation_def.run(batch_parameters={"dataframe": df})3637 if not results.success:38 print("🔴 VALIDACIÓN FALLIDA — pipeline abortado")39 for r in results.results:40 if not r.success:41 print(f" ❌ {r.expectation_config.type}")42 # Aquí enviarías alerta (Slack, email, PagerDuty)43 sys.exit(1)4445 print("✅ Validación OK")4647 # Paso 3: Transformación (solo si validación pasó)48 print("⚙️ Transformando datos...")49 df['importe_con_iva'] = df['importe'] * 1.215051 # Paso 4: Carga52 print("📤 Cargando en warehouse...")53 df.to_parquet("output/pedidos_gold.parquet")54 print(f" ✅ {len(df)} registros cargados correctamente")5556# Ejecutar57pipeline_con_validacion()
Patrón producción: ingesta → validación GE → transformación → carga. Si falla la validación, NADA se carga.
Lo que le diría a mi yo de hace 5 años: no intentes validar TODO desde el día 1. Empieza con 5 expectations básicas (volumen, nulos en campos clave, duplicados, rango de valores, tipo de datos). Después añade más a medida que descubras problemas. Las mejores suites de validación se construyen con el tiempo, alimentadas por cada incidente real.
Great Expectations transforma la validación de datos de algo que "deberías hacer" a algo que es parte integral de tu pipeline. En la próxima lección vamos un paso más allá: contratos de datos — acuerdos FORMALES entre quien produce datos y quien los consume.
## ejercicios
Crear una Expectation Suite para ventas Black Friday
Es el día después del Black Friday. El CEO quiere el informe de ventas en 2 horas. ANTES de generar el informe, necesitas validar que los datos son correctos (el año pasado hubo duplicados y nadie se enteró). Crea una suite GE completa para el dataset de ventas.
Usar el parámetro mostly para validaciones realistas
Tu suite de validación falla TODOS los días porque tienes expectations demasiado estrictas. El dataset siempre tiene un 1-2% de ruido inevitable. Ajusta las expectations con el parámetro mostly para que solo alerten cuando hay un problema REAL (> 5% de anomalías).
Pipeline ETL con validación GE en entrada y salida
Construye un mini-pipeline que: 1) Lee datos raw, 2) Valida la entrada con GE, 3) Transforma, 4) Valida la salida con GE. Si cualquier validación falla, el pipeline debe abortar con un mensaje claro.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...