68f4ddabce
Añade el capítulo `relaciones` al motor AutomaticEDA: analiza las relaciones de clave de la tabla/base y se coloca tras `correlacion`, antes de `modelos`, en CHAPTER_ORDER. Capas que renderiza (solo las que aplican; None si no hay nada que decir): - Claves declaradas: PK/FK/UNIQUE reales del esquema DuckDB, vía la nueva función `detect_declared_keys_duckdb` (lee `duckdb_constraints()`). - Candidatos a clave primaria: los `key_candidates` del TableProfile. - FK candidatas inter-tabla: reusa `infer_fk_containment_duckdb` (containment + señal de nombre) y `build_join_graph` (roles de nodos + diagrama Mermaid pegable). Solo si la fuente DuckDB tiene varias tablas. - FK candidatas intra-tabla: heurística nombre + cardinalidad, vía la nueva función pura `suggest_intratable_fk_candidates`, marcada como sugerencia. Engancha al glosario clicable los términos PK, FK, containment/inclusión y cardinalidad (contrato §11.1) y usa Group (keep-together) para el grafo. Funciones nuevas del registry (grupo `eda`): - detect_declared_keys_duckdb (impure, datascience) + test. - suggest_intratable_fk_candidates (pure, datascience) + test. Tests: relaciones_test.py (golden intra + inter, edges, no-cut render) + los tests de ambas funciones. Suite automatic_eda + render_automatic_eda verde (89 passed). Golden end-to-end con el pipeline render_automatic_eda verificado sobre titanic (intra) y una BD customers/orders (inter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.6 KiB
5.6 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | params | output | tested | tests | test_file_path | file_path | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| suggest_intratable_fk_candidates | function | py | datascience | 1.0.0 | pure | def suggest_intratable_fk_candidates(profile: dict, max_candidates: int = 20) -> list | Sobre el TableProfile de UNA tabla (el dict de profile_table), sugiere por heuristica de nombre + cardinalidad que columnas PARECEN una clave foranea hacia otra tabla, cuando no hay relaciones inter-tabla que medir (una sola tabla). Es una SUGERENCIA, no una afirmacion: el ref_table_guess es el stem del nombre (customer_id -> customer) y NO confirma containment. Pura: solo lee el dict, sin I/O; nunca lanza (devuelve []). |
|
false |
|
list (posiblemente vacia) de dicts, uno por columna sugerida, con claves: `column` (nombre), `ref_table_guess` (tabla conjeturada por el stem del nombre, p.ej. customer_id -> 'customer'), `reason` (frase humana que deja claro que es heuristica sin confirmar containment), `distinct_count` (int|None), `unique_pct` (float|None, fraccion 0-1 tal como viene del profile), `inferred_type` (str), `physical_type` (str). Nunca lanza. | true |
|
python/functions/datascience/suggest_intratable_fk_candidates_test.py | python/functions/datascience/suggest_intratable_fk_candidates.py |
Ejemplo
from datascience import suggest_intratable_fk_candidates
# TableProfile de UNA tabla (tipo titanic): customer_id es FK N:1; id es la PK;
# amount es una medida float; name es categorica sin sufijo de id.
profile = {
"n_rows": 891,
"key_candidates": ["id"],
"columns": [
{"name": "id", "inferred_type": "numeric", "physical_type": "BIGINT",
"distinct_count": 891, "unique_pct": 1.0, "flags": ["possible_id"]},
{"name": "customer_id", "inferred_type": "numeric", "physical_type": "BIGINT",
"distinct_count": 137, "unique_pct": 0.15, "flags": []},
{"name": "amount", "inferred_type": "numeric", "physical_type": "DOUBLE",
"distinct_count": 400, "unique_pct": 0.45, "flags": []},
{"name": "name", "inferred_type": "categorical", "physical_type": "VARCHAR",
"distinct_count": 700, "unique_pct": 0.78, "flags": []},
],
}
out = suggest_intratable_fk_candidates(profile)
[c["column"] for c in out] # -> ["customer_id"]
out[0]["ref_table_guess"] # -> "customer"
out[0]["reason"]
# -> "el nombre termina en '_id' y es N:1 (137 valores distintos < 891 filas):
# parece (heuristica por nombre, sin confirmar containment) una referencia a
# una tabla «customer»"
Cuando usarla
Cuando el EDA tiene SOLO UNA tabla y, por tanto, no se puede inferir una FK
inter-tabla por containment (no hay otra tabla cuyos valores contener). Es el plan B
del capitulo RELACIONES de AutomaticEDA: en vez de medir solapamiento de valores
entre tablas (lo correcto cuando hay varias, ver infer_fk_containment_duckdb /
build_join_graph), conjetura por el NOMBRE de la columna (<algo>_id) y por su
CARDINALIDAD N:1 que columnas parecen apuntar a una entidad externa. Usala para
enriquecer el reporte con "estas columnas parecen referencias a otras tablas" sin
prometer que esa tabla exista. NO la uses si tienes varias tablas: ahi mide
containment de verdad.
Gotchas
- Es heuristica, no una verdad: produce falsos positivos (una columna
period_idque en realidad es un codigo libre, no una FK) y falsos negativos (una FK que no se llama*_id, p.ej.parent,owner,sku). No la trates como una afirmacion de esquema. ref_table_guesses una conjetura por el nombre (el stem sin el sufijo id):customer_id->customer,AlbumId->album,manager_staff_id->manager_staff. Puede no coincidir con el nombre real de la tabla (plurales, prefijos, alias). Es una pista, no un join garantizado.- NO confirma containment: no comprueba que los valores de la columna existan en
ninguna otra tabla (no puede — solo recibe el perfil de una tabla). Para confirmar
una FK real con varias tablas usa
infer_fk_containment_duckdb. - Excluye deliberadamente: el
id/Id/IDgenerico a secas (suele ser la PK propia, no una referencia), las columnas constantes, las que parecen unicas (unique_pct >= 0.99, mas PK que FK) y los tipos no-clave (float/decimal son medidas; date/time/timestamp y boolean no son claves). En camelCase,paid,valid,grid(coniden minuscula y sin separador) NO se confunden con FK. unique_pctse interpreta como fraccion 0-1 (tal como la emite el profile), no como porcentaje 0-100.