▲ Apache Arrow · Formato Columnar

Pipeline de Datos con Apache Arrow

DataFlow Analytics SL — Migración de 2M eventos/día de CSV/Pandas a Arrow + Parquet
PyArrow 16.0 · Python 3.12
Parquet + ZSTD compression
Zero-copy interop: Pandas, Polars, DuckDB
Arrow IPC para microservicios
0
Dataset TechGadgetsPro — Semana del 9 al 15 Jun 2026
Resultados computados con pyarrow.compute (vectorizado)
● Revenue total (EUR)
€ 47.312
↑ +18.4% vs semana anterior
● Eventos procesados
2.14 M
↑ 7 días · ~306K/día
● Tasa de conversión
3.7 %
↑ purchases / sessions
● Ticket medio
€ 89.50
↑ +6.2% vs mes anterior
1
Arquitectura del Pipeline
Ingesta → Arrow in-memory → Parquet S3 → IPC API → Dashboard
📥
Raw Events
CSV · Kafka · API
Arrow Table
pa.table() · columnar
⚙️
pc.compute()
Vectorized KPIs
🗄️
Parquet/S3
Partitioned · ZSTD
🔗
Arrow IPC
Buffer → API REST
📊
Dashboard JS
tableFromIPC()
S
Schema Arrow — eventos TechGadgetsPro
event_id
int64
Identificador único del evento
user_id
int64
Usuario anónimo hasheado
session_id
string
UUID de sesión de navegación
event_type
string
page_view · add_to_cart · checkout · purchase
product_id
string
SKU del producto (p.ej. SKU-0041)
product_category
string
electronics · accessories · software
revenue
float64
EUR — 0.0 si no es purchase
timestamp
timestamp[us]
UTC, microsegundos
year / month
int32
Particionado Hive en S3
D
Muestra de datos (5 filas de 2.14M)
event_id user_id event_type product_id revenue
10000001 7823491 page_view SKU-0041 0.00
10000002 7823491 add_to_cart SKU-0041 0.00
10000007 7823491 checkout SKU-0041 0.00
10000008 7823491 purchase SKU-0041 € 129.99
10000023 9041122 purchase SKU-0093 € 59.90
Top 5 productos por revenue
#SKUCategoríaPurchasesRevenue
1 SKU-0041 electronics 3.241 € 18.934
2 SKU-0093 electronics 2.180 € 9.821
3 SKU-0017 accessories 1.930 € 7.412
4 SKU-0058 software 1.450 € 6.090
5 SKU-0029 accessories 1.220 € 5.055
2
Creación de tabla Arrow + KPIs vectorizados con pyarrow.compute
Python pipeline/arrow_events.py
import pyarrow as pa
import pyarrow.compute as pc
import pyarrow.parquet as pq
import pyarrow.dataset as ds
import pyarrow.ipc as ipc

# ── 1. Construir tabla Arrow desde datos TechGadgetsPro ─────────────────────
events = pa.table({
    "event_id":         pa.array([10000001, 10000002, 10000007, 10000008, 10000023], type=pa.int64()),
    "user_id":          pa.array([7823491,  7823491,  7823491,  7823491,  9041122],  type=pa.int64()),
    "event_type":       pa.array(["page_view", "add_to_cart", "checkout", "purchase", "purchase"]),
    "product_id":       pa.array(["SKU-0041", "SKU-0041", "SKU-0041", "SKU-0041", "SKU-0093"]),
    "product_category": pa.array(["electronics", "electronics", "electronics", "electronics", "electronics"]),
    "revenue":          pa.array([0.00, 0.00, 0.00, 129.99, 59.90],           type=pa.float64()),
    "year":             pa.array([2026] * 5, type=pa.int32()),
    "month":            pa.array([6]    * 5, type=pa.int32()),
})
# En producción se leen 2.14M filas desde Parquet/S3 — misma API

# ── 2. KPIs con compute (vectorizado, sin bucles Python) ────────────────────
purchases = pc.filter(events, pc.equal(events["event_type"], "purchase"))

total_revenue    = pc.sum(purchases["revenue"]).as_py()          # 189.89
avg_ticket       = pc.mean(purchases["revenue"]).as_py()         # 94.95
conversion_rate  = purchases.num_rows / events.num_rows          # 0.40 (40%)

# Top productos por revenue — DuckDB sobre Arrow (zero-copy)
import duckdb
top_products = duckdb.arrow(purchases).query("""
    SELECT product_id, product_category,
           COUNT(*) AS purchases,
           SUM(revenue) AS total_revenue
    FROM purchases
    GROUP BY product_id, product_category
    ORDER BY total_revenue DESC
    LIMIT 5
""").arrow()   # resultado también es Arrow table

print(f"Revenue: €{total_revenue:,.2f} | Ticket: €{avg_ticket:.2f} | Conv.: {conversion_rate:.1%}")
3
Escritura/Lectura Parquet particionado en S3 con predicate pushdown
Python pipeline/parquet_s3.py
# ── 3. Escribir dataset particionado (Hive-style) en S3 ────────────────────
ds.write_dataset(
    events,
    "s3://dataflow-techgadgetspro/events/",
    format="parquet",
    partitioning=ds.partitioning(
        pa.schema([("year", pa.int32()), ("month", pa.int32())]),
        flavor="hive",   # → year=2026/month=6/part-0.parquet
    ),
    existing_data_behavior="overwrite_or_ignore",
    file_options=pq.ParquetFileFormat().make_write_options(compression="zstd"),
)
# Resultado: 3 GB CSV  ──►  214 MB Parquet/ZSTD  (14× menor en disco)

# ── 4. Lectura con partition pruning + predicate pushdown ──────────────────
dataset = ds.dataset(
    "s3://dataflow-techgadgetspro/events/",
    format="parquet",
    partitioning="hive",
)

june_purchases = dataset.scanner(
    columns=["user_id", "product_id", "revenue", "timestamp"],
    filter=(
        (ds.field("year") == 2026) &
        (ds.field("month") == 6) &
        (ds.field("event_type") == "purchase") &
        (ds.field("revenue") > 0)
    ),
).to_table()
# Sólo lee los archivos de year=2026/month=6 — los demás se saltan completamente
# Lectura selectiva de columnas: I/O reducido 4× respecto a leer todas

# Streaming para lotes de 100K filas (útil con 2M eventos/día)
pf = pq.ParquetFile("local_events.parquet")
for batch in pf.iter_batches(batch_size=100_000):
    filtered = pc.filter(batch, pc.greater(batch["revenue"], 0))
    process_batch(filtered)   # RAM constante ~200MB en lugar de 3 GB
4
Arrow IPC — Intercambio entre microservicios Python → Next.js Dashboard
🛠 Python Backend
FastAPI / worker
pa.table() → IPC buffer
HTTP POST
application/vnd.apache.arrow.stream
▲ Arrow IPC Buffer
~4× menor que JSON
zero serialization cost
fetch() + arrayBuffer()
tableFromIPC()
📊 Next.js Dashboard
apache-arrow (npm)
Arrow JS zero-copy
Python api/kpi_endpoint.py
from fastapi import FastAPI
from fastapi.responses import Response
import pyarrow as pa
import pyarrow.ipc as ipc

app = FastAPI()

@app.get("/api/kpis/arrow")
async def get_kpis_arrow():
    # Tabla con KPIs de la semana
    kpis = pa.table({
        "metric":   ["revenue", "conversion", "avg_ticket"],
        "value":    [47312.0, 3.7, 89.5],
        "delta_pct":[18.4, 0.3, 6.2],
    })
    # Serializar a IPC stream (bytes)
    sink = pa.BufferOutputStream()
    with ipc.new_stream(sink, kpis.schema) as writer:
        writer.write_table(kpis)
    return Response(
        content=sink.getvalue().to_pybytes(),
        media_type="application/vnd.apache.arrow.stream",
    )
TypeScript components/KPIWidget.tsx
import { tableFromIPC } from "apache-arrow";

async function fetchKPIs() {
  const res = await fetch("/api/kpis/arrow");
  const buf = await res.arrayBuffer();

  // Parse Arrow IPC — zero-copy en WASM backends
  const table = tableFromIPC(new Uint8Array(buf));

  const metrics: Record<string, number> = {};
  for (const row of table) {
    metrics[row.metric] = row.value;
  }
  // metrics.revenue     → 47312.0
  // metrics.conversion  → 3.7
  // metrics.avg_ticket  → 89.5
  return metrics;
}

// Ventaja: 2.4 MB JSON  ──►  590 KB Arrow IPC (4× menor)
// Tiempo de parse: 180ms JSON  ──►  12ms Arrow (15× más rápido)
5
Comparativa de rendimiento — 5 millones de eventos TechGadgetsPro
Benchmark en MacBook Pro M3 · 32 GB RAM
⏱ Tiempo de lectura (5M filas)
CSV + Pandas
CSV/Pandas
41.2 s
Parquet + Pandas
Parquet/pd
14.1 s
Parquet + Arrow
Arrow
4.1 s
Parquet + Pushdown
PD
1.6 s
Pushdown = solo columnas necesarias + filtro de partición — lee 1 de cada 6 archivos
💾 Uso de memoria RAM (5M filas)
CSV + Pandas
3.2 GB
3.2 GB
Arrow in-memory
1.1 GB
1.1 GB
Arrow streaming
200MB
200 MB
💾 Tamaño en disco (dataset semana)
CSV (raw)
CSV 3 GB
3.0 GB
Parquet/SNAPPY
820 MB
820 MB
Parquet/ZSTD
214MB
214 MB
6
Zero-copy interop — Arrow como lingua franca del stack de datos
Conversiones sin copiar datos en memoria
Origen → Destino Método Copia de memoria Tiempo (1M filas) Recomendación
Arrow → Pandas .to_pandas() Zero-copy 12 ms ✓ Recomendado
Pandas → Arrow pa.Table.from_pandas() 1 copia 95 ms OK para ingestión
Arrow → Polars pl.from_arrow() Zero-copy 8 ms ✓ Preferir Polars
Arrow → DuckDB duckdb.arrow(table) Zero-copy 2 ms ✓ SQL sobre Arrow
Arrow → JSON REST json.dumps() Serialización full 1.8 s ✗ Evitar en API interna
Arrow → IPC stream ipc.new_stream() Near zero-copy 45 ms ✓ Para microservicios
7
Guía de buenas prácticas — Apache Arrow en producción
✓ Hacer siempre
Parquet para storage, Arrow para compute
Escribe Parquet en S3; mantén Arrow in-memory para operaciones de cálculo.
Predicate pushdown en lecturas Parquet
Siempre especifica columns= y filters=; reduces I/O 4-10×.
iter_batches() para datasets grandes
RAM constante (~200 MB) vs 3 GB cargando el fichero entero.
IPC para microservicios internos
4× menos bytes que JSON, 15× más rápido de parsear en JS.
✗ Evitar
Bucles Python sobre filas Arrow
Usa siempre pyarrow.compute (pc.*); los bucles rompen la vectorización.
Leer todas las columnas del Parquet
Columnar = solo lees lo que necesitas. Sin columns= cargas datos innecesarios.
JSON/CSV para comunicación inter-servicio
Arrow IPC o Parquet bytes son siempre superiores para datos estructurados.
Particionar por columnas de alta cardinalidad
Particionar por user_id crea millones de ficheros. Usa year/month/day.