Workflow de exploración de datos
Cómo manejamos queries de exploración vs. scripts reusables en el proyecto
trivasa-bi-dev, para mantener el directorio de trabajo limpio y el proceso reproducible — más la convención de formato de salida en terminal que comparten los scripts de validación.
Empaquetado como skill (2026-09-02): trivasa-sql-exploracion,
en el repo claude-skills — cubre las secciones 1-6 de este documento (puntual vs.
reusable, conexión por default, heredoc, checklist) más la convención de nombres
NN_slug.py para lo que sí se guarda, que esta página nunca llegó a documentar. Esta
página queda como referencia histórica de origen — para explorar o decidir cómo nombrar
un script nuevo, usar el skill, no este documento. (La sección 7, formato de salida, ya
apuntaba a su propio skill, rich-output-checks, desde 2026-08-10 — sin cambios.)
1. Regla general: exploración puntual vs. reusable
| Tipo | Ejemplo | Dónde vive |
|---|---|---|
| Puntual (curiosidad, validación, responder una pregunta de negocio) | “¿Qué productos tienen IVA variable?” | No se guarda como archivo — se corre vía heredoc a stdin |
| Reusable (se va a volver a correr, alimenta un pipeline) | Carga de Reorden a Postgres, modelo dbt | Sí se guarda, en su carpeta correspondiente — nunca en el root de trivasa-bi-dev/ |
Regla de oro: si dudas si un script se va a volver a usar, probablemente no — no lo guardes.
2. Patrón de conexión
Antes de conectar, confirmar en Conexiones a TRIVASADB qué servidor corresponde — hay 3 copias con distinto rol/frescura y usar la equivocada da datos truncados o desfasados sin error visible.
Corrección 2026-08-05: una versión anterior de esta página decía que
connection.pyera el entorno de prueba.204por default. Ya no es así:connection.pyapunta a.200/TRIVASADB, la copia que Conexiones a TRIVASADB marca como desactualizada y “nunca usar”. Actualmente no existe unconnection_204.pydedicado.
Decisión: connection_200_trivasadb3.py (.200/TRIVASADB3) es el entorno de
pruebas recomendado por default para cualquier exploración nueva — vivo pero con
~1 mes de atraso respecto a producción. connection_207.py (.207, producción) solo
se usa cuando se pide explícitamente revisar o validar contra el entorno de
producción — no como punto de partida.
Los tres módulos exponen el mismo helper:
from connection_200_trivasadb3 import show, engine # default — .200/TRIVASADB3, ~1 mes atrasado
# from connection_207 import show, engine # solo si se pide validar contra producciónshow(sql) ya imprime el resultado como markdown y regresa el DataFrame — no
envolver en print() (causa doble output).
3. Patrón heredoc para exploraciones puntuales
En vez de crear un archivo .py, correr el bloque completo directo por stdin:
python3 << 'EOF'
from connection_200_trivasadb3 import show, engine
import pandas as pd
query = """
SELECT TOP 20 *
FROM Producto
WHERE Es_Cve_Estado = 'ACTI'
"""
df = pd.read_sql(query, engine)
print(df)
EOFEsto corre igual que un script, pero no deja ningún archivo en el directorio.
⚠️ Importante — no pegar bloques for/if línea por línea en el REPL interactivo
(>>>). Una línea en blanco dentro de un bloque indentado hace que el intérprete
cierre el bloque antes de tiempo, causando IndentationError en cada línea siguiente.
Los bloques con lógica (loops, condicionales) siempre van vía heredoc, nunca
pegados directo en >>>.
4. Patrón rich para salida con formato (exploración puntual)
Cuando el resultado incluye texto largo (ej. Pr_Descripcion) que se trunca feo en
show(), usar rich para tablas legibles en terminal:
from rich.console import Console
from rich.table import Table
console = Console()
table = Table(title="Título descriptivo", show_lines=False)
table.add_column("Columna A", style="cyan", no_wrap=True)
table.add_column("Descripción", style="white", max_width=45)
# ... más columnas
for _, row in df.iterrows():
table.add_row(str(row["col_a"]), str(row["descripcion"]))
console.print(table)Para resultados que abarcan varios grupos (ej. por año), separar en una tabla rich
por grupo:
for valor in sorted(df["columna_grupo"].unique()):
df_grupo = df[df["columna_grupo"] == valor]
table = Table(title=f"Título — {valor}")
# ...
console.print(table)
console.print()Este patrón inline sirve para exploración puntual. Para scripts reusables de validación que se van a volver a correr, usar el helper ya armado en vez de repetir este boilerplate cada vez — ver sección 7.
5. Organización del directorio
Vive en el root de trivasa-bi-dev/:
connection.py(⚠️ apunta a la copia desactualizada.200/TRIVASADB, ver sección 2),connection_207.py,connection_200_trivasadb3.pytrivasadb_context.md(contexto de schema curado)- Carpetas de proyecto por reporte (ej.
layout_gastos/) directamente en el root, no dentro de unexploracion/intermedio stack/: scaffolding de servicios BI explorados pero no adoptados en producción (trivasa_dbt/,postgres/,superset/,lightdash/,trivasa-evidence/) — ver Stack de BI explorado, no adoptadologs/
Va a scratch/ (gitignored):
- Scripts de exploración que por alguna razón sí se guardaron
- Nunca se versionan ni se documentan individualmente
6. Checklist antes de cualquier exploración nueva
- Confirmar que la tabla existe en el contexto documentado.
trivasadb_context.mdcubre ~50 tablas grandes + dominios de negocio principales, pero hay ~1600 tablas en total en TRIVASADB (muchas vacías, módulos no usados por Trivasa). Si la tabla no aparece en el doc, no asumir su estructura — ir directo aINFORMATION_SCHEMA.COLUMNS. - Validar columnas reales antes de escribir el join, incluso si la tabla sí está
documentada. El
.mdlista columnas representativas (“Cols: …”), no necesariamente todas las columnas de la tabla. Ejemplo real: PKs compuestas con columnas repetidas en el doc (ej.Cxc_Folio, Cxc_Folio, Pc_ID, Pc_ID, Im_Cve_Impuesto) probablemente reflejan cómo se generó el documento automáticamente — no tomarlas como la PK real sin confirmar. - Para tablas grandes (top 50 del doc), evitar envolver columnas de fecha en
funciones (
YEAR(col)) dentro delWHERE. Usar rango (col >= 'YYYY-01-01' AND col < 'YYYY+1-01-01') para permitir uso de índice. Para tablas pequeñas no es crítico. - Probar el join en pequeño antes de agregar. Si el query incluye
GROUP BYsobre una tabla grande, correr primero unSELECT TOP 20del join sin agregar, para confirmar que no hay explosión de filas por una PK compuesta mal entendida. - Confirmar el servidor correcto primero (sección 2). Nunca correr una
exploración nueva contra
.200/TRIVASADB(desactualizada). Empezar enconnection_200_trivasadb3(default) y pasar aconnection_207(producción) solo si se pide explícitamente validar contra producción.
7. Convención de formato de salida en scripts (helpers_output.py)
Estandariza cómo los scripts de validación reusables muestran resultados en
terminal, para que sean legibles de forma consistente sin que cada script reinvente su
propio formato de tabla. Nació en layout_gastos/ (segunda exploración,
2026-08-04) pero el patrón aplica a
cualquier script de validación del repo — copia literal del helper en
sources/trivasa/layout-gastos/helpers_output.py.
Empaquetado como skill (2026-08-10): rich-output-checks, en el repo
claude-skills — el
mismo helpers_output.py, generalizado sin dependencias de layout_gastos/,
disponible en ~/.claude/skills/rich-output-checks/ en cualquier host
onboardeado a ese repo (ctunlinux incluido). La copia de sources/ de este
repo queda como referencia histórica del origen; el skill es la fuente viva
para usarlo en un script nuevo.
Ancho de consola: auto-detectado, no fijo
rich.Console() se usa sin ancho fijo — auto-detecta el ancho real de la terminal
del usuario (confirmado 138 columnas en surface-wsl). Se probaron y descartaron 3
alternativas: Console(width=160) fijo rompe en bloques verticales ilegibles con
tablas de 8+ columnas si el ancho no coincide con la terminal real; pandas.to_string()
plano no aporta ventaja sobre rich; una vista vertical (1 fila = 1 bloque
campo/valor) solo sirve para drill-down de un folio individual, no para tablas de
comparación.
from helpers_output import console, mostrar_tabla, resumen
resumen(folios=1425, cuadran=1425, pct="100.0%")
mostrar_tabla(df, titulo="Resumen por ORIGEN")mostrar_tabla() trunca a 30 filas por default (max_filas=) y avisa si el DataFrame
tiene más, para no saturar la pantalla con tablas de cientos de filas.
Salida hacia Claude (aparte de la convención de terminal): para pegar resultados en
la conversación con Claude, preferir CSV plano (df.to_csv(index=False)) sobre las
tablas rich con bordes — más compacto en tokens, sin ambigüedad de columnas
truncadas. Las tablas rich son para legibilidad humana en pantalla; el CSV es lo que
se pega al LLM cuando se necesita que procese el resultado.
Separación de stream: tablas humanas a stderr, CSV a stdout
Problema encontrado al usar python3 script.py | cb (pipe al portapapeles para pegar
en la conversación con Claude): capturaba todo stdout, mezclando las tablas rich
(pensadas para lectura humana) con los bloques CSV destinados a Claude — obligaba a
copiar a mano solo la parte CSV cada vez.
Solución: las tablas rich van a stderr (console_err, mostrar_tabla_err,
resumen_err); los bloques CSV_PARA_CLAUDE van a stdout vía print() normal,
prefijados con print("--- CSV_PARA_CLAUDE: <nombre> ---"). Ambos streams se ven igual
en pantalla (comparten la tty), pero | cb solo engancha stdout — así
python3 script.py | cb deja en el portapapeles solo los CSV, sin flags ni copiar a
mano.
from helpers_output import console_err, mostrar_tabla_err, resumen_err
mostrar_tabla_err(df, titulo="Resumen por ORIGEN") # rich -> stderr, humano
resumen_err(folios=1425, cuadran=1425, pct="100.0%") # rich -> stderr, humano
print("--- CSV_PARA_CLAUDE: nombre_bloque ---")
print(df.to_csv(index=False)) # -> stdout, para ClaudeRegla: cualquier script nuevo de validación usa las variantes _err para pantalla y
el prefijo CSV_PARA_CLAUDE para lo que se necesite pegar a la conversación. El
helper original (console, mostrar_tabla, resumen, todo a stdout) se conserva
para scripts que se corren sin intención de hacerles pipe (exploración interactiva
rápida).
Véase también
- Conexiones a TRIVASADB — detalle de las 3 copias y cuál usar para qué.
- Layout de Gastos — segunda exploración (2026-08-04) — el proyecto que originó la convención de la sección 7.
sources/trivasa/layout-gastos/helpers_output.pyysources/trivasa/layout-gastos/convencion_output_scripts.md— copias literales.