763e06c127
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
104 lines
5.6 KiB
Markdown
104 lines
5.6 KiB
Markdown
---
|
|
name: profile_database
|
|
kind: pipeline
|
|
lang: py
|
|
domain: pipelines
|
|
purity: impure
|
|
version: "1.0.0"
|
|
signature: "def profile_database(db_path: str, tables: list = None, sample: int = 5000, report_dir: str = \"reports\", write_report: bool = True, min_inclusion: float = 0.9) -> dict"
|
|
description: "Orquestador one-shot del grupo eda a nivel de BASE: perfila TODA una base DuckDB (todas las tablas o las indicadas) componiendo profile_table por tabla, infiere las relaciones FK inter-tabla por containment y construye el join graph con diagrama Mermaid. Ensambla un DatabaseProfile (resumen por tabla + TableProfiles completos + fk_candidates + join_graph) y opcionalmente emite un report markdown DB-level + JSON sidecar. Es la composicion canonica para hazme un EDA de esta base de datos y entender su esquema relacional."
|
|
tags: [eda, relations, duckdb, profiling, data-quality, pipeline, dataops]
|
|
uses_functions:
|
|
- profile_table_py_pipelines
|
|
- infer_fk_containment_duckdb_py_datascience
|
|
- build_join_graph_py_datascience
|
|
- duckdb_list_tables_py_infra
|
|
- render_eda_markdown_py_datascience
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: error_go_core
|
|
imports: []
|
|
tested: true
|
|
tests:
|
|
- "profile_database_two_related_tables"
|
|
- "profile_database_writes_report"
|
|
test_file_path: "python/functions/pipelines/profile_database_test.py"
|
|
file_path: "python/functions/pipelines/profile_database.py"
|
|
params:
|
|
- name: db_path
|
|
desc: "Ruta al archivo DuckDB (read-only, debe existir; no se crea)."
|
|
- name: tables
|
|
desc: "Lista de tablas a perfilar. None (default) usa todas las del esquema main via duckdb_list_tables."
|
|
- name: sample
|
|
desc: "Maximo de valores no nulos muestreados por columna en el perfil de cada tabla (se pasa a profile_table). Default 5000."
|
|
- name: report_dir
|
|
desc: "Directorio donde escribir los reports DB-level si write_report. Default 'reports'. Se crea si no existe."
|
|
- name: write_report
|
|
desc: "Si True (default) escribe report markdown DB-level + JSON sidecar timestamped en report_dir; si False no toca disco y los paths del retorno son None."
|
|
- name: min_inclusion
|
|
desc: "Umbral minimo de inclusion (0-1) para emitir una FK candidata (se pasa a infer_fk_containment_duckdb). Default 0.9."
|
|
output: "dict {status:'ok', db_profile:<DatabaseProfile con db_path, profiled_at, n_tables, tables[resumen], table_profiles[completos], fk_candidates, join_graph{nodes,edges,mermaid,hubs}, errors>, report_md_path:str|None, report_json_path:str|None} o {status:'error', error:str} (dict-no-throw)."
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```python
|
|
import os
|
|
import tempfile
|
|
import duckdb
|
|
from pipelines.profile_database import profile_database
|
|
|
|
# Base DuckDB de juguete en /tmp: customers <- orders (relacionadas).
|
|
db = os.path.join(tempfile.mkdtemp(), "shop.duckdb")
|
|
con = duckdb.connect(db)
|
|
con.execute("CREATE TABLE customers (id INTEGER, name VARCHAR, city VARCHAR)")
|
|
con.execute("INSERT INTO customers VALUES (1,'Ana','Madrid'),(2,'Luis','Sevilla'),(3,'Marta','Bilbao')")
|
|
con.execute("CREATE TABLE orders (order_id INTEGER, customer_id INTEGER, total DOUBLE)")
|
|
con.execute("INSERT INTO orders VALUES (10,1,99.5),(11,1,12.0),(12,2,45.0),(13,3,7.25)")
|
|
con.close()
|
|
|
|
r = profile_database(db, write_report=False)
|
|
print(r["status"], r["db_profile"]["n_tables"]) # ok 2
|
|
print([fk["from_table"]+"."+fk["from_col"]+"->"+fk["to_table"]+"."+fk["to_col"]
|
|
for fk in r["db_profile"]["fk_candidates"]])
|
|
# ['orders.customer_id->customers.id'] -> FK inferida por containment
|
|
print(r["db_profile"]["join_graph"]["mermaid"].splitlines()[0]) # graph LR
|
|
|
|
# Con report DB-level a disco (markdown con diagrama Mermaid + JSON sidecar):
|
|
r = profile_database(db, report_dir="reports")
|
|
print(r["report_md_path"], r["report_json_path"])
|
|
# reports/eda_db_20260620-101500.md reports/eda_db_20260620-101500.json
|
|
```
|
|
|
|
## Cuando usarla
|
|
|
|
Cuando necesites entender una BASE de datos entera de un golpe: el perfil de
|
|
todas sus tablas mas su esquema relacional (que tabla referencia a cual, con que
|
|
cardinalidad) en una sola llamada. Usala al recibir una base DuckDB desconocida,
|
|
para documentar un data warehouse, para descubrir el star schema (las tablas hub
|
|
del join graph) o antes de escribir joins sin tener el modelo declarado. Es el
|
|
escalon DB-level sobre `profile_table` (que perfila una sola tabla): aqui ademas
|
|
se infieren las FK y se dibuja el diagrama de relaciones.
|
|
|
|
## Gotchas
|
|
|
|
- Impura: con `write_report=True` (default) ESCRIBE dos archivos a `report_dir`
|
|
(markdown DB-level + JSON sidecar). Pasa `write_report=False` para un dry-run
|
|
sin tocar disco.
|
|
- Las FK se infieren por CONTAINMENT, es una HEURISTICA: A->B es candidata si los
|
|
valores distintos de A estan contenidos en B (>= `min_inclusion`) y B parece
|
|
clave (alta unicidad en su tabla). Puede dar falsos positivos (columnas que
|
|
comparten dominio sin ser FK real, p.ej. dos columnas de codigos de pais) o
|
|
perder FK reales si `min_inclusion` es muy alto o los datos estan sucios. Es un
|
|
punto de partida para mapear el esquema, no un DDL autoritativo.
|
|
- Perfila TODAS las tablas por defecto: en bases grandes (muchas tablas o tablas
|
|
muy anchas) puede TARDAR. Acota con `tables=[...]` o baja `sample`. La
|
|
inferencia de FK ademas salta pares hacia tablas con mas de 200k filas (lado
|
|
caro del INTERSECT); esas relaciones quedan sin evaluar.
|
|
- Tolera fallos por tabla: si el perfil de una tabla concreta falla, se anota en
|
|
`db_profile["errors"]` y se sigue con las demas; `n_tables` cuenta solo las
|
|
perfiladas con exito. Revisa `errors` para saber que quedo fuera.
|
|
- `db_path` debe existir: DuckDB read-only NO crea la base. El muestreo de cada
|
|
tabla usa el sandbox read-only por defecto (sin acceso a FS/red).
|