7ac69ab4fb
Bloque del grupo eda (sesion ausente EDA-benchmark): - 8 funciones nuevas: adf_kpss_stationarity, acf_pacf, stl_decompose, to_returns, fdr_correction, suggest_reexpression, exploratory_caveats, render_eda_pdf - integracion: profile_table (run_series, emit_pdf), association_matrix (FDR Benjamini-Hochberg), render_eda_markdown (secciones series/reexpresion/caveats) - slash commands /eda y /capitulos - issues 0173-0177: mejoras del /eda derivadas del benchmark sobre 12 datasets reales (outlier_pct x100, periodo estacional, FK inference, render models, tipos id-like) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
5.9 KiB
Markdown
90 lines
5.9 KiB
Markdown
---
|
||
id: "0175"
|
||
title: "EDA relational: precisión de FK inference (falsos positivos) + filtrar VIEWs + test ATTACH"
|
||
status: pendiente
|
||
type: bugfix
|
||
domain:
|
||
- registry-quality
|
||
scope: registry-only
|
||
priority: alta
|
||
depends: []
|
||
blocks: []
|
||
related: ["0173", "0174", "0176", "0177"]
|
||
created: 2026-06-29
|
||
updated: 2026-06-29
|
||
tags: [eda, datascience, infer_fk_containment_duckdb, build_join_graph, profile_database, duckdb, benchmark]
|
||
---
|
||
# 0175 — EDA relational: precisión de FK inference + filtrar VIEWs
|
||
|
||
## Contexto
|
||
|
||
El benchmark `/eda` (29/06/2026, `temp/eda_benchmark/EVALUATION.md`) confirmó que la inferencia de
|
||
claves foráneas a nivel de base es **inútil por falsos positivos masivos** y que las VISTAS se
|
||
perfilan como tablas base. El join graph resultante necesita filtrado manual para ser legible.
|
||
|
||
Hallazgos cubiertos:
|
||
|
||
| Hallazgo | Severidad | Evidencia del benchmark |
|
||
|---|---|---|
|
||
| H3 — FK inference por contención: 10-20× falsos positivos | crítico | chinook 111 candidatas vs ~11 reales; sakila 565 vs ~30. Casos absurdos: `InvoiceLine.Quantity→Album.AlbumId`, `Genre.GenreId→{Album,Artist,Customer,…}` |
|
||
| H5 — VIEWs perfiladas como tablas base | alto | sakila `n_tables=21` incluye 5 VISTAS (`customer_list`, `film_list` 5462 filas, `staff_list`, `sales_by_store`, `sales_by_film_category`) + `film_text` (FTS, 0 filas) |
|
||
| H10 — coste relacional gastado en computar FK falsas | medio | sakila 31.82s: la mayoría en INTERSECT de los 565 pares candidatos, casi todos falsos |
|
||
| H14 — bug `sqlite_master does not exist` tras ATTACH (ya parcheado, falta test) | bajo (resuelto) | `_run.log`: `profile_database` falló con `Catalog Error: src.sqlite_master`; re-run posterior `ok` |
|
||
|
||
### Causa raíz (verificada en código, READ-ONLY)
|
||
|
||
- `python/functions/datascience/infer_fk_containment_duckdb.py:217-285` emite una FK candidata si
|
||
`inclusion(A⊆B) ≥ min_inclusion` **y** B "parece clave" (unicidad ≥0.95). **No usa el nombre de
|
||
la columna**, que es la señal más fuerte de FK (`AlbumId→Album.AlbumId`), ni excluye columnas
|
||
no-clave (cantidades, importes) como ORIGEN. Enteros pequeños (`GenreId` 1..25) están contenidos
|
||
en casi todo → ruido.
|
||
- `python/functions/pipelines/profile_database.py:155-159` lista tablas con `duckdb_list_tables`
|
||
sin filtrar `table_type` → perfila VIEWs y tablas FTS como base (H5), lo que infla el universo de
|
||
pares y multiplica las FK falsas (relaciona H10).
|
||
- H10 es el **mismo cambio** que H3: filtrar candidatos por nombre **antes** del INTERSECT reduce
|
||
pares (más rápido) y falsos positivos (más preciso) a la vez.
|
||
|
||
## Tareas
|
||
|
||
1. **H3+H10 — señal de nombre en `infer_fk_containment_duckdb.py:217-285`:** antes de lanzar el
|
||
INTERSECT, exigir coincidencia/patrón de nombre entre origen y destino (`from_col` casa con
|
||
`to_table`/`to_col`, patrón `<X>Id → <X>.<X>Id`; case-insensitive). Excluir como ORIGEN columnas
|
||
claramente no-clave (cantidades, importes, flags) por heurística de nombre/tipo. Esto poda el
|
||
O(tablas²×columnas²) y elimina la mayoría de los falsos positivos. Validar mejor la cardinalidad
|
||
(los `1:1` imposibles del benchmark).
|
||
2. **H5 — filtrar VIEWs** antes de perfilar e inferir FK: filtrar `table_type='BASE TABLE'` vía
|
||
`information_schema.tables` / `duckdb_tables()`. Decidir (a confirmar al implementar) si el filtro
|
||
va como flag nuevo en `duckdb_list_tables` (infra, reutilizable) o en `profile_database.py` tras
|
||
listar. Preferir el flag en `duckdb_list_tables` si no rompe consumidores.
|
||
3. **H3 — propagar al join graph:** verificar que `build_join_graph.py` recibe la lista ya filtrada
|
||
y que el diagrama Mermaid resultante es legible (sin nodos VIEW ni aristas espurias).
|
||
4. **H14 — test de regresión:** añadir test (en `profile_database_test.py` o
|
||
`infer_fk_containment_duckdb_test.py`) que haga `ATTACH` de una base SQLite pequeña en DuckDB y
|
||
perfile, confirmando que se usa `information_schema`/`duckdb_tables()` y nunca `sqlite_master`.
|
||
(A confirmar: localizar la función que hace el ATTACH —probablemente `summarize_table_duckdb.py`
|
||
o una primitiva infra `duckdb_*`— para cubrirla.)
|
||
5. Tests: casos sintéticos con tablas que tengan columnas tipo `XId` (FK real) y columnas de
|
||
cantidad contenidas en claves (falso positivo) → confirmar que solo emite las reales.
|
||
|
||
## Definition of Done
|
||
|
||
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|
||
|---|---|---|---|
|
||
| Golden: FK reales sin ruido | e2e | re-correr `profile_database` sobre chinook | ~11 FK candidatas (no 111); incluyen `Album.ArtistId→Artist.ArtistId`, `Invoice.CustomerId→Customer.CustomerId`; NO incluyen `InvoiceLine.Quantity→Album.AlbumId` |
|
||
| Edge: VIEWs excluidas | e2e | re-correr `profile_database` sobre sakila | `n_tables` cuenta solo BASE TABLE (sin `customer_list`/`film_list`/…); FK candidatas ≪ 565 |
|
||
| Edge: cantidad vs clave | unit | `infer_fk_containment_duckdb_test.py` con columna `Quantity` contenida en una clave | NO emite FK desde `Quantity` |
|
||
| Error: ATTACH SQLite | unit | test de regresión ATTACH SQLite→DuckDB | perfila sin `sqlite_master does not exist`; usa information_schema |
|
||
| Rendimiento (H10) | e2e | medir duración de `profile_database` sobre sakila | menor que el baseline 31.82s (menos INTERSECT) |
|
||
| Mecánica | — | `./fn run infer_fk_containment_duckdb_py_datascience`, `./fn run profile_database_py_pipelines`; `fn index` | tests verdes; índice limpio |
|
||
|
||
Re-correr el benchmark sobre chinook y sakila y confirmar que las FK reales son distinguibles del
|
||
ruido y que las VIEWs no se cuentan como tablas.
|
||
|
||
## Notas
|
||
|
||
Issue derivado de `temp/eda_benchmark/EDA_ISSUES.md`. Tres síntomas (H3/H5/H10) con un núcleo común:
|
||
la capa de inferencia de relaciones inter-tabla. Atacarlos juntos en una rama; filtrar VIEWs reduce
|
||
el universo de pares y filtrar candidatos por nombre arregla precisión y velocidad a la vez. H14 ya
|
||
está parcheado en producción; este issue solo añade el test de regresión que faltaba.
|
||
Hermanos: 0173, 0174, 0176, 0177.
|