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()
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
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
| # | SKU | Categoría | Purchases | Revenue |
|---|---|---|---|---|
| 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
zero serialization cost
fetch() + arrayBuffer()
tableFromIPC()
📊 Next.js Dashboard
apache-arrow (npm)
Arrow JS zero-copy
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
41.2 s
Parquet + Pandas
14.1 s
Parquet + Arrow
4.1 s
Parquet + Pushdown
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
Arrow in-memory
1.1 GB
Arrow streaming
200 MB
💾 Tamaño en disco (dataset semana)
CSV (raw)
3.0 GB
Parquet/SNAPPY
820 MB
Parquet/ZSTD
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.