11 Introducción a PySpark y Fundamentos ETL
11.1 ¿Qué es el ETL y por qué importa?
ETL (Extract, Transform, Load) es el proceso que mueve datos desde los sistemas operacionales (OLTP, APIs, logs, archivos) hacia el Data Warehouse, transformándolos en el camino para que sean consistentes, limpios y aptos para el análisis.
Distinción con el Preprocesamiento (Parte II) ETL es ingeniería de datos: opera sobre flujos de producción, terabytes, esquemas fijos, y su salida es reproducible y automatizada. El preprocesamiento analítico (Parte II) toma una vista ya limpia del DW y la transforma para un experimento concreto. El DW es el punto de transferencia entre ambos.
11.1.1 ETL clásico vs ELT moderno
| ETL clásico | ELT moderno | |
|---|---|---|
| Orden | Extraer → Transformar → Cargar | Extraer → Cargar → Transformar |
| Dónde transforma | Motor ETL externo | Dentro del DW / lakehouse |
| Herramientas | Informatica, SSIS, Talend | dbt + BigQuery / Snowflake / DuckDB |
| Ventaja | Datos siempre limpios al llegar | Aprovechar la potencia del DW columnares |
| Desventaja | Cuello de botella en el motor | El DW necesita ser potente y barato |
11.1.2 ¿Por qué PySpark?
Cuando los datos superan la memoria RAM de una sola máquina, pandas deja de ser suficiente. Apache Spark divide el trabajo en particiones que se procesan en paralelo sobre un clúster de nodos, usando un plan de ejecución optimizado (DAG — Directed Acyclic Graph).
PySpark es la API Python de Spark. Con ella puedes escribir transformaciones que escalan de un laptop (modo local) a cientos de nodos (AWS EMR, Databricks, GCP Dataproc) sin cambiar el código.
11.2 Entorno de prácticas: Databricks Community Edition
Todas las prácticas de este bloque se realizan en Databricks Community Edition, que proporciona un clúster Spark gratuito sin necesidad de instalación local.
11.2.1 Configurar la cuenta
- Ir a community.cloud.databricks.com y crear una cuenta gratuita.
- En el menú lateral: Compute → Create compute → elegir el runtime Databricks Runtime 14.x LTS (Spark 3.5, Python 3.11).
- Crear un Notebook (lenguaje: Python).
En Databricks, spark ya existe como variable global — no crees un SparkSession manual. Simplemente empieza con spark.read...
11.2.2 Cargar datasets a DBFS
# Databricks File System (DBFS) — sistema de archivos del clúster
# Opción A: subir archivo desde la UI (File → Upload data)
# Opción B: descargar directamente desde el notebook con dbutils
dbutils.fs.mkdirs("dbfs:/datasets/taxi/")
# Descargar NYC Taxi (Parquet, ~500 MB por mes)
import urllib.request
url = "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet"
urllib.request.urlretrieve(url, "/tmp/yellow_2023_01.parquet")
dbutils.fs.mv("file:/tmp/yellow_2023_01.parquet", "dbfs:/datasets/taxi/yellow_2023_01.parquet")
# Verificar
display(dbutils.fs.ls("dbfs:/datasets/taxi/"))# Leer desde DBFS en Databricks
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
display(df.limit(5)) # display() es el equivalente de show() en Databricks11.2.3 Comandos específicos de Databricks
| Comando | Descripción |
|---|---|
display(df) |
Tabla interactiva con gráficos integrados |
dbutils.fs.ls("dbfs:/ruta/") |
Listar archivos en DBFS |
dbutils.fs.mkdirs("dbfs:/ruta/") |
Crear directorio en DBFS |
dbutils.fs.rm("dbfs:/ruta/", True) |
Eliminar archivo o directorio |
%sql SELECT ... |
Celda SQL sobre tablas registradas |
spark.catalog.listTables() |
Ver tablas disponibles en el metastore |
Delta Lake está preinstalado en Databricks. No necesitas pip install delta-spark — simplemente usa .format("delta") directamente.
11.2.4 Datasets para las prácticas
| Dataset | Tamaño | Dónde obtenerlo en Databricks |
|---|---|---|
| NYC Taxi (1 mes) | ~500 MB | Descargar con urllib (ver §2.2) |
| NYC Taxi (todo 2023) | ~6 GB | Bucle sobre los 12 meses |
| TPC-H sintético | Configurable | %pip install tpch + python -m tpch generate |
| Databricks sample data | Varios | dbfs:/databricks-datasets/ (preinstalado) |
# Explorar los datasets de muestra de Databricks
display(dbutils.fs.ls("dbfs:/databricks-datasets/"))
# Incluye: nyctaxi, airlines, wine-quality, iot-stream, retail-org, etc.
# Leer el dataset de ejemplo de taxis (ya disponible)
df_sample = spark.read.format("delta")\
.load("dbfs:/databricks-datasets/nyctaxi/tables/nyctaxi_yellow")
display(df_sample.limit(10))11.3 PySpark: Fundamentos
11.3.1 SparkSession
En Databricks el SparkSession ya existe como spark. En entorno local:
from pyspark.sql import SparkSession
# Solo necesario fuera de Databricks
spark = (SparkSession.builder
.appName("ETL-Mineria-Datos")
.master("local[*]")
.config("spark.sql.shuffle.partitions", "8")
.getOrCreate())11.3.2 El DataFrame de Spark: lazy vs eager
# Transformaciones (lazy) — solo construyen el plan, no ejecutan nada
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
df2 = df.filter(df.trip_distance > 1.0)
df3 = df2.select("tpep_pickup_datetime", "trip_distance", "total_amount")
# Acción — dispara la ejecución del DAG completo
df3.show(5) # o display(df3) en Databricks
df3.count()| Concepto | Significado |
|---|---|
| Transformación | filter, select, groupBy, join — construyen el DAG |
| Acción | show, count, collect, write — ejecutan el DAG |
| Partición | Unidad de paralelismo; un archivo Parquet por partición típicamente |
| RDD | Capa de bajo nivel; usa DataFrame salvo casos especiales |
11.3.3 Esquema explícito (mejor práctica)
from pyspark.sql.types import *
schema = StructType([
StructField("tpep_pickup_datetime", TimestampType(), True),
StructField("tpep_dropoff_datetime", TimestampType(), True),
StructField("passenger_count", IntegerType(), True),
StructField("trip_distance", DoubleType(), True),
StructField("fare_amount", DoubleType(), True),
StructField("tip_amount", DoubleType(), True),
StructField("total_amount", DoubleType(), True),
StructField("payment_type", IntegerType(), True),
])
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
# Parquet guarda el schema; con CSV/JSON conviene declararlo explícito:
# df = spark.read.csv("dbfs:/ruta.csv", header=True, schema=schema)inferSchema=True hace una pasada completa del archivo; con esquema explícito la lectura es más rápida y sin sorpresas de tipos.
11.4 Referencias de este capítulo
- Chambers & Zaharia. Spark: The Definitive Guide. O’Reilly, 2018.
- PySpark Documentation
- Databricks Community Edition
- Databricks Free Training — cursos oficiales gratuitos.