Jupyter Notebook en Tu Data Lake en 60 Segundos
Quieres explorar tu data lake en un notebook de Jupyter. Así es cómo normalmente se ve:
- Instalar JupyterLab
- Instalar DuckDB y los bindings de Python
- Averiguar en qué bucket S3 están tus datos
- Instalar boto3 y configurar credenciales de AWS
- Escribir el boilerplate de configuración de DuckDB para S3
- Descubrir manualmente qué tablas existen
- Escribir las sentencias de lectura de Parquet para cada tabla
- Esperar que las rutas estén bien
Son 20 minutos de configuración antes de escribir una sola consulta analítica. Cada vez.
La Forma DataSpoc Lens
dataspoc-lens add-bucket company s3://company-data-lakedataspoc-lens notebookEso es todo. JupyterLab se abre en tu navegador con cada tabla de tu data lake ya montada cómo una vista de DuckDB. La magia SQL funcióna de inmediato. La variable conn está lista para pandas.
Qué Sucede Por Debajo
Cuando ejecutas dataspoc-lens notebook, Lens:
- Lee el manifiesto de
s3://company-data-lake/.dataspoc/manifest.json - Crea una conexión DuckDB con credenciales S3 de tu entorno
- Monta cada ruta Parquet cómo una vista con nombre
- Genera un script de inicio (
~/.dataspoc/jupyter_startup.py) - Lanza JupyterLab con el script de inicio precargado
El script de inicio se ve así (auto-generado, nunca lo editas):
# ~/.dataspoc/jupyter_startup.py (auto-generated)import duckdb
conn = duckdb.connect()
# S3 configuration (from environment)conn.execute("INSTALL httpfs; LOAD httpfs;")conn.execute("SET s3_region = 'us-east-1';")
# Mount all tables from manifestconn.execute(""" CREATE VIEW raw__postgres__orders AS SELECT * FROM read_parquet('s3://company-data-lake/raw/postgres/orders/*.parquet')""")conn.execute(""" CREATE VIEW raw__postgres__customers AS SELECT * FROM read_parquet('s3://company-data-lake/raw/postgres/customers/*.parquet')""")conn.execute(""" CREATE VIEW curated__sales__customers AS SELECT * FROM read_parquet('s3://company-data-lake/curated/sales/customers/*.parquet')""")# ... one view per table in your lake
print(f"DataSpoc Lens: {len(conn.execute('SHOW TABLES').fetchall())} tables mounted")print("Use 'conn' for queries or %%sql magic for inline SQL")Ejemplo de Celdas del Notebook
Celda 1: Ver Qué Está Disponible
# All your tables are already hereconn.sql("SHOW TABLES").show()┌─────────────────────────────────────┐│ name │├─────────────────────────────────────┤│ raw__postgres__orders ││ raw__postgres__customers ││ raw__postgres__order_items ││ curated__sales__customers ││ raw__google_sheets__campaigns │└─────────────────────────────────────┘Celda 2: Magia SQL
%%sqlSELECT DATE_TRUNC('month', created_at) as month, COUNT(*) as orders, SUM(amount) as revenueFROM raw__postgres__ordersWHERE created_at >= '2026-01-01'GROUP BY monthORDER BY month┌────────────┬────────┬───────────┐│ month │ orders │ revenue │├────────────┼────────┼───────────┤│ 2026-01-01 │ 3,421 │ 284,500 ││ 2026-02-01 │ 3,892 │ 312,100 ││ 2026-03-01 │ 4,156 │ 341,800 ││ 2026-04-01 │ 2,847 │ 228,400 │└────────────┴────────┴───────────┘Celda 3: Pandas DataFrame
df = conn.sql(""" SELECT customer_id, COUNT(*) as order_count, SUM(amount) as total_spend, MAX(created_at) as last_order FROM raw__postgres__orders GROUP BY customer_id""").df()
df.describe() order_count total_spendcount 3201.000000 3201.000000mean 14.340000 832.450000std 8.230000 456.780000min 1.000000 12.990000max 89.000000 8945.000000Celda 4: Gráfico con Matplotlib
import matplotlib.pyplot as plt
monthly = conn.sql(""" SELECT DATE_TRUNC('month', created_at) as month, SUM(amount) as revenue FROM raw__postgres__orders WHERE created_at >= '2025-01-01' GROUP BY month ORDER BY month""").df()
plt.figure(figsize=(12, 5))plt.bar(monthly["month"].dt.strftime("%b %Y"), monthly["revenue"])plt.title("Monthly Revenue")plt.ylabel("Revenue ($)")plt.xticks(rotation=45)plt.tight_layout()plt.show()Celda 5: Cruzar Múltiples Fuentes
%%sqlSELECT c.channel, COUNT(DISTINCT o.customer_id) as customers, SUM(o.amount) as revenue, ROUND(SUM(o.amount) / COUNT(DISTINCT o.customer_id), 2) as avg_revenue_per_customerFROM raw__postgres__orders oJOIN raw__postgres__customers cu ON o.customer_id = cu.idJOIN raw__google_sheets__campaigns c ON cu.utm_source = c.channelGROUP BY c.channelORDER BY revenue DESCLa Alternativa Marimo
¿Prefieres notebooks reactivos? Lens también soporta Marimo:
dataspoc-lens notebook --runtime marimoEsto lanza un notebook Marimo con las mismas tablas pre-montadas:
import marimo as moimport duckdb
# conn is pre-configured (same startup script)conn = duckdb.connect()# ... tables already mounted
# Marimo's built-in SQL supportresult = mo.sql(""" SELECT channel, SUM(amount) as revenue FROM raw__postgres__orders GROUP BY channel""", connection=conn)
# Reactive: change the query, chart updates automaticallymo.ui.table(result)Ventajas de Marimo:
- Ejecución reactiva (las celdas se re-ejecutan cuando cambian las dependencias)
- Widgets de UI integrados (sliders, dropdowns)
- Reproducible (sin estado oculto)
- Se exporta a scripts Python
Demo Docker: Entorno Completamente Autocontenido
¿Quieres probarlo sin instalar nada? Usa la demo Docker:
docker run -it --rm \ -p 8888:8888 \ -e AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY \ -e AWS_DEFAULT_REGION \ dataspoc/lens-notebook:latest \ --bucket s3://company-data-lakeEsto te da:
- JupyterLab corriendo en
http://localhost:8888 - DuckDB con todas las tablas de tu bucket montadas
- Magia
%%sqlpreconfigurada - pandas, matplotlib, seaborn, plotly preinstalados
- Variable
connlista para usar
Docker Compose para Uso en Equipo
version: "3.8"services: notebook: image: dataspoc/lens-notebook:latest ports: - "8888:8888" environment: - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_DEFAULT_REGION - DATASPOC_BUCKET=s3://company-data-lake volumes: - ./notebooks:/home/jovyan/notebooksdocker compose upComparte el docker-compose.yaml con tu equipo. Todos obtienen el mismo entorno, mismas tablas, mismas vistas SQL. Sin problemas de “funcióna en mi máquina”.
Consejos
Múltiples buckets:
dataspoc-lens add-bucket sales s3://company-salesdataspoc-lens add-bucket marketing s3://company-marketingdataspoc-lens notebook# Both buckets' tables are mountedRefrescar tablas sin reiniciar:
# In a notebook cellfrom dataspoc_lens import LensClientlens = LensClient()lens.refresh() # Re-reads manifest, mounts new tablesUsar con VS Code Jupyter:
dataspoc-lens notebook --no-browser --ip 0.0.0.0# Then connect VS Code to the running Jupyter serverEl Resumen en 60 Segundos
| Paso | Comando | Tiempo |
|---|---|---|
| Conectar bucket | dataspoc-lens add-bucket company s3://... | 2s |
| Lanzar notebook | dataspoc-lens notebook | 5s |
| Empezar a consultar | %%sql SELECT ... | 0s (las tablas ya están ahí) |
Sin boto3. Sin configuración de credenciales. Sin adivinar rutas de Parquet. Sin boilerplate de configuración de DuckDB.
Solo abre el notebook y escribe SQL.