lección 2

Día 1 — Tu primera tarea real

El standup, el sprint planning, los CSVs de María, la exploración con Pandas y tu primer contacto con el caos de los datos reales.

45 min

### 9:30 AM — El standup

Abres Slack y ves que el canal #data-team ya tiene actividad. Es lunes y hay standup por Meet. Te unes al enlace con un nudo en el estómago — es tu primera reunión de equipo real.

Elena comparte pantalla brevemente para mostrar el tablero de Linear y dice:

1Elena Torres:
2"Equipo, ya conocéis a tú. Esta semana va a empezar con una
3tarea de ingesta que María lleva pidiendo dos semanas. Te voy a asignar
4el ticket de la campaña de verano. Raúl, ¿le puedes explicar cómo
5funciona el repo?"
6
7Raúl Vega:
8"Sí, claro. Luego te mando un DM."
9
10Jorge Ruiz:
11"Bienvenido/a. Cuando tengas los datos de la campaña, avísame que
12necesito cruzarlos con los de retención."

Standup del equipo — tu primer contacto con la dinámica diaria

### 10:00 AM — Sprint Planning: tu ticket

Después del standup, Elena se queda contigo y con Raúl en la call para explicarte tu primera tarea. Abre un ticket en Linear:

1╔══════════════════════════════════════════════════════════════╗
2║ TICKET: DATA-147 ║
3║ Título: Ingestar datos de campaña de verano de Marketing ║
4╠══════════════════════════════════════════════════════════════╣
5║ Descripción: ║
6║ María nos ha pasado los resultados de la campaña de ║
7║ verano 2024 en CSVs. Necesitamos: ║
8║ 1. Subir los archivos a la zona raw del lake (S3) ║
9║ 2. Validar que los datos están completos ║
10║ 3. Dejar una tabla limpia en la zona silver ║
11║ ║
12║ Prioridad: Alta ║
13║ Asignado a: TÚ ║
14║ Fecha límite: Miércoles ║
15╚══════════════════════════════════════════════════════════════╝

Tu primer ticket real — la presión empieza con un deadline de 2 días

Sientes un pinchazo de ansiedad: "miércoles" suena pronto. Pero Elena te tranquiliza: "No es un pipeline de producción todavía. Necesito que entiendas los datos, los subas a raw y empieces la limpieza. Si el miércoles tienes los datos de verano limpios en silver, perfecto." Asientes. Puedes con esto.

### 10:30 AM — El DM de María

Antes de que puedas siquiera abrir VS Code, llega un mensaje directo de María en Slack:

1María García 10:32 AM
2Holaaa! 👋 Bienvenido/a al equipo!
3Te he mandado un email con los CSVs de la campaña de verano
4Son 3 archivos: los clientes que participaron, las ventas durante
5la campaña, y los productos en promoción
6¿Me avisas cuando los tengas cargados? Es que tengo una presentación
7el jueves y necesito los números
8Graciaaas 🙏

María no pierde tiempo — la presión de negocio es real

Tres archivos adjuntos en un email: campana_verano_clientes.csv (15.247 filas), campana_verano_ventas.csv (42.891 filas), y productos_promo_verano.csv (312 filas). Los descargas a tu máquina. Es hora de ensuciarse las manos.

Consejo de senior: NUNCA abras un CSV de producción directamente en Excel para "echarle un ojo". Excel modifica datos silenciosamente — convierte códigos postales a números (pierde los ceros), reformatea fechas según tu locale, y trunca filas si hay más de 1 millón. Usa SIEMPRE Pandas o la terminal para la primera exploración.

### 11:00 AM — Explorar los datos con Pandas

Abres VS Code, creas un script de exploración y empiezas a investigar qué te ha mandado María. El primer paso de CUALQUIER tarea con datos nuevos es siempre el mismo: mirar antes de actuar. Nunca transformes datos que no has explorado primero.

1# explorar_campana.py — Tu primer script en FreshMart
2import pandas as pd
3
4# Cargar los 3 archivos de María
5clientes = pd.read_csv('datos/campana_verano_clientes.csv')
6ventas = pd.read_csv('datos/campana_verano_ventas.csv')
7productos = pd.read_csv('datos/productos_promo_verano.csv')
8
9print("=" * 60)
10print("CLIENTES DE LA CAMPAÑA")
11print("=" * 60)
12print(f"Filas: {len(clientes):,}")
13print(f"Columnas: {list(clientes.columns)}")
14print()
15print(clientes.info())
16print()
17print(clientes.head())
18print()
19print("Nulos por columna:")
20print(clientes.isnull().sum())
21print()
22print(f"IDs únicos: {clientes['cliente_id'].nunique():,}")
23print(f"IDs totales: {len(clientes):,}")
24print(f"→ Posibles duplicados: {len(clientes) - clientes['cliente_id'].nunique():,}")

Siempre empieza explorando: .info(), .head(), .isnull().sum()

Ejecutas el script y aparecen las primeras señales: campana_verano_clientes.csv tiene 15.247 filas pero solo 14.946 cliente_id únicos — unos 300 clientes aparecen repetidos porque recibieron la campaña por varios canales. Las columnas abrio_email y clickeo_link tienen muchos NULL, pero eso es normal: son los clientes que no abrieron el email o no hicieron clic. El archivo de campaña, en realidad, viene bastante limpio. El caos de verdad lo encontrarás en el CSV de ventas (fechas en tres formatos distintos) y, más adelante, cuando cruces con la tabla maestra de clientes (clientes.csv): ahí hay 312 filas duplicadas, 5 clientes sin nombre y 847 códigos postales a los que se les ha caído el cero inicial al exportar.

1# Explorar el CSV de ventas — aquí es donde está el CAOS
2print("=" * 60)
3print("VENTAS DE LA CAMPAÑA")
4print("=" * 60)
5print(f"Filas: {len(ventas):,}")
6print(f"Columnas: {list(ventas.columns)}")
7print()
8print(ventas.dtypes)
9print()
10print("Primeras filas:")
11print(ventas.head(10).to_string())
12print()
13print("Nulos por columna:")
14print(ventas.isnull().sum())
15print()
16# Verificar el formato de fecha_compra
17print("Muestras de fecha_compra:")
18print(ventas['fecha_compra'].sample(10, random_state=42).values)
19print()
20# Verificar el formato de precio
21print("Muestras de precio:")
22print(ventas['precio'].sample(10, random_state=42).values)

El CSV de ventas revela el verdadero desorden: fechas mixtas y precios como string

Y aquí es donde la realidad te golpea. La columna fecha_compra tiene TRES formatos diferentes: "2024-07-15" (el 70% de filas), "15/07/2024" (el 25%), y "Jul 15, 2024" (el 5%). La columna precio es un STRING con el símbolo € y coma decimal: "2,45€", "12,99€". Y hay 847 filas donde cliente_id es vacío — compras de usuarios anónimos (guest checkout).

Los 3 formatos de fecha en un mismo archivo no son un error de generación de datos — es algo que pasa CONSTANTEMENTE en la vida real. Diferentes sistemas exportan fechas en su formato local. Alguien copió datos de un Excel español (DD/MM/YYYY), otro los sacó del backend (YYYY-MM-DD), y un tercero lo exportó desde una herramienta americana (Mon DD, YYYY). Pandas puede manejarlos, pero necesitas decirle explícitamente cómo.

### Lo que has descubierto: resumen de problemas

Tu primera auditoría de calidad — y apenas has empezado

### 14:00 PM — El DM de Raúl: cómo funciona el lake

Después de comer, Raúl por fin te escribe. Su mensaje es informal, un poco incompleto, pero te da la información que necesitas:

1Raúl Vega 14:03 PM
2ey! perdona que no te he escrito antes
3he estado con lo del pipeline de inventario que lleva roto 3 días 😅
4mira, para subir cosas al lake:
5- el bucket es s3://freshmart-lake/raw/
6- la convención es raw/{fuente}/{tabla}/{año}/{mes}/{dia}/
7- hay un script en /scripts/upload_to_raw.py que te puede servir
8 (no sé si funciona, creo que lo hice hace 6 meses jaja)
9si te atascas dime!

Raúl: buen tipo, pero la ayuda viene con disclaimers

Anotas la estructura. La zona raw del lake sigue una convención de directorios por fuente/tabla/fecha. Esto significa que tu upload quedaría en: raw/marketing/campana_verano_clientes/2024/09/16/. La regla de oro que Elena repitió en el sprint planning: "A raw se sube el original SIN TOCAR. Ni una coma se modifica. La transformación va en silver."

Consejo de senior: la zona raw es sagrada. Es tu copia de seguridad inmutable. Si la cagaste limpiando datos en silver, siempre puedes volver al original en raw y reprocesar. Si modificas raw, pierdes la fuente de verdad. He visto equipos que perdieron datos irrecuperables por "mejorar" archivos directamente en raw. No seas ese equipo.

### 14:30 PM — Subir los CSVs a la zona raw

En un entorno real usarías boto3 (el SDK de AWS para Python) para subir archivos a S3. Como estamos trabajando en local con los datasets descargados, vamos a simular la estructura de carpetas que tendría el lake. Esto te enseña la convención sin necesidad de tener una cuenta de AWS.

1# subir_a_raw.py — Simular la estructura del data lake en local
2import shutil
3from pathlib import Path
4from datetime import date
5
6# Configuración
7LAKE_ROOT = Path("freshmart-lake")
8FUENTE = "marketing"
9FECHA = date.today() # 2024-09-16
10
11# Archivos de María
12archivos = [
13 "campana_verano_clientes.csv",
14 "campana_verano_ventas.csv",
15 "productos_promo_verano.csv",
16]
17
18for archivo in archivos:
19 # Nombre de la "tabla" = nombre del archivo sin extensión
20 tabla = archivo.replace(".csv", "")
21
22 # Estructura: raw/{fuente}/{tabla}/{año}/{mes}/{dia}/
23 destino = (
24 LAKE_ROOT / "raw" / FUENTE / tabla
25 / str(FECHA.year) / f"{FECHA.month:02d}" / f"{FECHA.day:02d}"
26 )
27 destino.mkdir(parents=True, exist_ok=True)
28
29 # Copiar el archivo ORIGINAL sin tocar
30 shutil.copy(f"datos/{archivo}", destino / archivo)
31 print(f"✓ {archivo}{destino}")
32
33print("\n✅ Todos los archivos subidos a raw (sin modificar)")

Simulamos la estructura del lake en local — la convención es lo importante

Ejecutas el script y verificas que la estructura de carpetas se creó correctamente. En un entorno real, esto sería un aws s3 cp con el perfil correcto, pero la lógica es idéntica: el dato original llega a raw intacto, particionado por fecha de ingesta.

### 16:45 PM — El imprevisto: Jorge necesita algo

Cuando crees que el día está terminando tranquilo, llega un mensaje de Jorge en Slack:

1Jorge Ruiz 16:47 PM
2Hola, ¿ya tienes los datos de la campaña en el lake?
3Necesito cruzar los clientes de la campaña con nuestra tabla de
4segmentación. En concreto necesito saber: de los 15K clientes en la
5campaña, ¿cuántos son "premium" (más de 6 pedidos en los últimos
63 meses)?
7¿Puedes prepararme un CSV con: cliente_id, nombre, segmento,
8pedidos_ultimos_3m?
9No es urgente-urgente pero si puedes tenerlo para mañana por la
10mañana me salvas.

Jorge pide un cruce — educado pero con deadline implícito

Aquí hay una decisión que tomar: tienes un ticket asignado (DATA-147, la ingesta) con deadline el miércoles, y ahora Jorge te pide otra cosa. ¿Qué haces? La respuesta correcta no es "sí a todo" ni "no puedo" — es CONSULTAR a tu lead:

1Tú 17:02 PM
2Elena, Jorge me ha pedido un cruce de clientes de la campaña con
3segmentación. ¿Lo priorizo sobre el ticket DATA-147 o primero
4termino la ingesta?
5
6Elena Torres 17:08 PM
7Primero termina la ingesta y la validación. Lo de Jorge lo puedes
8hacer mañana por la mañana, no debería llevarte más de 1h si ya
9tienes los datos en el lake.
10Buen criterio preguntando antes 👍

Siempre consulta antes de cambiar prioridades — Elena te lo reconoce

Consejo de senior: ese "Buen criterio preguntando antes 👍" de Elena es ORO. Muchos juniors cometen uno de dos errores: (1) dicen sí a todo y luego no entregan nada a tiempo, o (2) dicen no a todo y parecen poco colaborativos. La respuesta correcta es: "Puedo hacerlo, ¿pero cuál es la prioridad respecto a mi tarea actual?" Eso demuestra que piensas en el equipo, no solo en tu ticket.

Con la respuesta de Elena clara, le contestas a Jorge: "Lo tengo mañana a primera hora" y cierras el portátil a las 18:00 con la sensación de haber sobrevivido al primer día. Has explorado datos reales, identificado problemas, subido archivos al lake y gestionado una petición lateral. No está mal para un lunes.

## ejercicios

[01]

Explorar el CSV de clientes de la campaña

María te ha mandado el archivo campana_verano_clientes.csv. Tu primera tarea es explorarlo con Pandas para entender qué hay dentro. Calcula: número total de filas, número de cliente_id únicos, cuántos duplicados hay, cuántos valores nulos por columna y muestra las primeras 5 filas.

Cargando editor...
[02]

Detectar los 3 formatos de fecha en el CSV de ventas

El CSV campana_verano_ventas.csv tiene la columna fecha_compra con 3 formatos distintos. Escribe código que: (1) cargue el archivo, (2) muestre 3 ejemplos de cada formato diferente usando expresiones regulares, y (3) cuente cuántas filas hay de cada formato: YYYY-MM-DD, DD/MM/YYYY y "Mon DD, YYYY".

Cargando editor...
[03]

Crear la estructura de carpetas del data lake (zona raw)

Siguiendo la convención que te explicó Raúl (raw/{fuente}/{tabla}/{año}/{mes}/{dia}/), escribe un script que cree la estructura de directorios para los 3 archivos de María y copie cada CSV a su ubicación correcta SIN modificarlo. La fuente es "marketing" y la fecha de ingesta es 2024-09-16.

Cargando editor...
[04]

Auditoría rápida: resumen ejecutivo de calidad

Tu lead Elena quiere un resumen rápido de qué encontraste en los datos. Escribe una función que reciba un DataFrame y devuelva un diccionario con: total_filas, total_columnas, filas_duplicadas, nulos_totales y porcentaje_nulos. Ejecútala sobre los 3 archivos de María e imprime un resumen formateado.

Cargando editor...

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...