47 · Pandas + SQLite: read_sql y to_sql
Una línea para escribir un DataFrame en una tabla SQL; una línea para consultarlo de vuelta. `df.to_sql` y `pd.read_sql` convierten SQLite en un almacén consultable sin esfuerzo para cualquier flujo de trabajo de Pandas — ideal para persistir resultados de evaluación de modelos o extraer datos de entrenamiento de una BD ligera.
Pandas y SQLite se componen perfectamente: `df.to_sql('table', conn)` serializa un DataFrame en filas SQL en una sola llamada, y `pd.read_sql('SELECT ...', conn)` hidrata el resultado de cualquier consulta de vuelta en un DataFrame. El puente bidireccional significa que puedes usar Pandas para el manejo de datos y SQL para el filtrado y la agregación — lo que mejor se adapte a la tarea — sin ninguna conversión manual.
Sin esto:
Sin `to_sql`/`read_sql` escribirías un bucle sobre las filas del DataFrame, llamando a `cur.execute` para cada una, y luego reconstruirías manualmente un DataFrame a partir de los resultados de `fetchall()`. Ambas direcciones son tediosas y propensas a errores — el puente elimina el código repetitivo por completo.
La lección anterior cubrió sqlite3 en crudo — cursores, consultas parametrizadas, ciclos de commit. Esta lección conecta SQLite con Pandas: la forma más fácil de volcar un DataFrame en un almacén consultable y recuperarlo es df.to_sql y pd.read_sql, y aceptan una conexión sqlite3 directamente.
df.to_sql(nombre, conn, ...)
Argumentos clave:
| argumento | significado |
|---|---|
| if_exists="fail" | error si la tabla ya existe (por defecto) |
| if_exists="replace" | eliminar y recrear la tabla, luego insertar |
| if_exists="append" | insertar filas en una tabla existente |
| index=False | no escribir el índice del DataFrame como columna (generalmente lo que quieres) |
| chunksize=N | escribir N filas por INSERT — útil para DataFrames muy grandes |
pd.read_sql(sql, conn, ...)
Ejecuta cualquier cadena SQL y devuelve un DataFrame. Los nombres de columna provienen de los alias de columna de la consulta. Pasa chunksize=N para obtener un iterador de DataFrames en lugar de un único resultado grande — el mismo patrón perezoso que pd.read_csv(..., chunksize=N).
Cadenas de conexión SQLAlchemy
Pandas también acepta objetos Engine de SQLAlchemy y URLs de cadena de conexión como "sqlite:///mydb.db". Ese camino es necesario para bases de datos más allá de SQLite (PostgreSQL, MySQL). Para trabajo exclusivo con SQLite una conexión sqlite3 directa es más simple.
Python (in browser)
`sqlite3` es stdlib — solo `pandas` necesita listarse en paquetes. `to_sql` infiere los tipos de columna a partir de los dtypes del DataFrame automáticamente. `read_sql` devuelve un DataFrame fresco con dtypes inferidos de los tipos de columna SQLite.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
`if_exists` controla qué sucede cuando la tabla ya existe. `"replace"` destruye silenciosamente los datos existentes — útil para snapshots pero peligroso para acumuladores. `"append"` agrega filas sin tocar el esquema.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
SQLAlchemy no está instalado en el sandbox de Pyodide usado por este bootcamp, por lo que los ejemplos de create_engine son solo read-along. La conexión `sqlite3` directa mostrada en las celdas ejecutables funciona perfectamente para SQLite.
¿Qué hace `df.to_sql('t', conn, if_exists='append')` cuando la tabla `t` **no** existe todavía?
- `df.to_sql('nombre', conn, if_exists='replace', index=False)` escribe un DataFrame en una tabla SQL en una sola llamada.
- `pd.read_sql('SELECT ...', conn)` ejecuta cualquier consulta SQL y devuelve un DataFrame — los nombres de columna provienen de la consulta.
- `if_exists='replace'` elimina y recrea; `if_exists='append'` agrega filas. Ambos crean la tabla si no existe.
- `pd.read_sql(..., chunksize=N)` devuelve un iterador perezoso de DataFrames — úsalo para transmitir tablas grandes sin cargar todo en RAM.
- Para pipelines de producción o bases de datos que no sean SQLite, enruta Pandas a través de un `Engine` de SQLAlchemy (`create_engine('postgresql://...')`).
El caso de uso canónico de ML: extraer datos de entrenamiento de un SQLite de producción (o PostgreSQL vía SQLAlchemy) con `pd.read_sql('SELECT features, label FROM events WHERE split=\'train\'', conn)`, entrenar un modelo y luego escribir métricas de evaluación de vuelta con `metrics_df.to_sql('eval_runs', conn, if_exists='append', index=False)`. Los feature stores ligeros para prototipos casi siempre siguen exactamente este patrón — un archivo SQLite por experimento, un `to_sql` por artefacto, un `read_sql` para comparar ejecuciones.
Si lo quitas: Sin `read_sql`/`to_sql` iteras manualmente las filas del cursor en listas y luego llamas a `pd.DataFrame(...)` — añadiendo 10–20 líneas de código repetitivo por consulta. Para las rutas de escritura iteras sobre `df.itertuples()` llamando a `cur.execute` fila por fila, lo cual es lento y fácil de equivocarse con la coerción de tipos.