Files
fn_registry/python/functions/datascience/summarize_outlier_dims.md
T
egutierrez 6f88f184f1 feat(eda): capítulo OUTLIERS — valores atípicos univariantes + multivariantes
Nuevo capítulo dedicado `outliers` para el motor AutomaticEDA que reúne y
profundiza en un solo sitio el análisis de valores atípicos, hoy disperso entre
`num_distr` (conteo por columna) y `modelos` (IsolationForest). Se registra en
`chapters_registry.py` entre `missingness` y `correlacion` (bloque de calidad de
datos: calidad → missingness → outliers).

Contenido del capítulo:
- Resumen univariante por columna: nº y % de atípicos por Tukey (1.5·IQR) y por
  z-score (|z| > 3), con vallas inferior/superior y valores extremos. Ordenado
  por contaminación y marcando las columnas más afectadas. Reusa las funciones
  del registry `build_boxplot_stats` (vallas desde los percentiles del profile)
  y `detect_outliers` (regla z-score sobre la muestra cruda de `ctx`).
- Boxplots de Tukey de las columnas más contaminadas (caja, bigotes y puntos
  atípicos), delegados a la función nueva `build_boxplots_figure`.
- Multivariante: filas anómalas considerando todas las columnas a la vez con
  `isolation_forest_outliers` — nº y % de filas, las más anómalas con su score y
  las dimensiones que las hacen raras (top columnas por |z|, vía la función nueva
  `summarize_outlier_dims`). El detector se corre en vivo sobre `raw_numeric`
  para que el indexado de filas coincida exactamente con el de las dimensiones;
  cae al bloque precomputado del perfil cuando no hay muestra cruda (preset lite).
- Interpretación exploratoria: un atípico no es necesariamente un error
  (distingue error de dato vs dato real extremo) y recomendaciones (revisar,
  winsorizar o re-expresar, enlazando con la re-expresión de Tukey del perfil).

Términos clicables registrados en el glosario compartido: `outlier`,
`tukey_fence`, `zscore`, `isolation_forest`.

Funciones nuevas del registry (dominio datascience, grupo eda):
- `build_boxplots_figure_py_datascience` (figure helper, impura)
- `summarize_outlier_dims_py_datascience` (pura)

El capítulo se activa con ≥1 columna numérica y devuelve None en su ausencia;
lee todo defensivo y nunca lanza. Tests: capítulo (golden + edges + error path +
render PDF/PPTX) y ambas funciones nuevas. Suite de no-regresión de AutomaticEDA
verde. Verificado end-to-end con el dataset Titanic (Fare/Parch/SibSp como las
columnas más contaminadas).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 21:12:40 +02:00

4.8 KiB

name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags params output uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path
summarize_outlier_dims function py datascience 1.0.0 pure def summarize_outlier_dims(raw_numeric: dict, outlier_rows: list, top_k: int = 3) -> list Explica QUE columnas hacen rara cada fila anomala detectada por isolation_forest_outliers. Para cada {row_index, score} reconstruye la fila valida (mismo filtro de columnas numericas y mismo descarte de filas con None que el detector, asi row_index coincide) y devuelve las top_k columnas de mayor |z-score| poblacional (ddof=0). Capa de explicabilidad del paso de outliers multivariante en EDA. Pura y determinista; ante entradas vacias/invalidas o sin filas validas devuelve [] sin petar.
eda
models
outliers
anomaly-detection
explainability
z-score
multivariate
name desc
raw_numeric dict {nombre_columna: [valores]} alineado por fila (como ctx['raw_numeric'] del motor AutomaticEDA). Solo se usan columnas con todos los valores numericos (None permitido por fila; bool/str/NaN/Inf descartan la columna entera) — filtro IDENTICO al de isolation_forest_outliers para que row_index coincida.
name desc
outlier_rows Lista de {row_index, score} tal cual la devuelve isolation_forest_outliers. row_index cuenta SOLO las filas validas (sin None) en orden de aparicion, base 0. Entradas fuera de rango o malformadas se ignoran defensivamente.
name desc
top_k Numero de columnas (las de mayor |z-score|) a reportar por outlier. Default 3. Valores invalidos (no-int, bool, <1) caen a 3.
Lista paralela a outlier_rows (mismo orden) de dicts {row_index: int, score: float, dims: [{col: str, value: float, z: float}, ...]}. dims trae hasta top_k columnas ordenadas por |z| descendente, con z (z-score poblacional, ddof=0) redondeado a 3 decimales; si una columna tiene std==0 su z es 0. Las entradas de outlier_rows fuera de rango/malformadas se omiten. Ante raw_numeric vacio/no-dict, outlier_rows no-lista, 0 columnas numericas o 0 filas validas devuelve [].
false
true
test_row_index_skips_none_rows
test_extreme_row_flagged_via_isolation
test_out_of_range_row_index_is_ignored
test_degrades_to_empty_on_invalid_inputs
python/functions/datascience/summarize_outlier_dims_test.py python/functions/datascience/summarize_outlier_dims.py

Ejemplo

from datascience import isolation_forest_outliers, summarize_outlier_dims

# Nube densa alrededor del origen + 1 fila con un valor extremo en "c".
raw_numeric = {
    "a": [0.1, 0.2, -0.1, 0.0, 0.3, -0.2, 0.15, -0.05, 0.25, 0.2, -0.3, 0.1],
    "b": [1.0, 1.1, 0.9, 1.2, 0.8, 1.0, 1.1, 0.95, 1.05, 0.9, 1.15, 1.0],
    "c": [5.0, 5.2, 4.8, 5.1, 4.9, 5.0, 4.95, 5.05, 4.9, 500.0, 5.1, 5.0],
}

result = isolation_forest_outliers(raw_numeric, contamination=0.1)
summary = summarize_outlier_dims(raw_numeric, result["outlier_rows"], top_k=3)

for item in summary:
    top = item["dims"][0]
    print(item["row_index"], top["col"], top["value"], top["z"])
# La fila del valor 500 sale con dim top "c" y |z| alto: es lo que la hace rara.

Cuando usarla

Justo despues de isolation_forest_outliers, cuando ya sabes QUE filas son anomalas y quieres explicar POR QUE: en que columnas se desvian mas respecto al resto. Util para rellenar la seccion de outliers de un report/notebook EDA con "la fila 9 es rara sobre todo por c (z=+3.3)" en lugar de solo un row_index opaco. Pasa el mismo raw_numeric que diste al detector y su outlier_rows intacto; el row_index apunta a la misma fila porque ambas funciones aplican el mismo filtro de columnas y el mismo descarte de filas con None.

Gotchas

  • Mismo raw_numeric que el detector: el row_index solo coincide si pasas el mismo dict de columnas (mismo orden, mismas listas) con el que llamaste a isolation_forest_outliers. Si cambias las columnas o el orden, los indices dejan de mapear.
  • row_index es relativo a las filas validas: las filas con None en cualquier columna usada se descartan y los indices se recalculan sobre las que quedan (base 0, orden de aparicion). No mapea 1:1 con las listas de entrada si hay None.
  • z-score poblacional (ddof=0): se usa la desviacion tipica poblacional, consistente con el escalado del detector. Columnas con std==0 (todos los valores iguales) dan z=0, asi que nunca aparecen como "raras".
  • Devuelve [] en vez de petar: entrada no-dict/no-lista, 0 columnas numericas, 0 filas validas, o todas las entradas fuera de rango -> lista vacia. No lanza excepciones.
  • No llama a isolation_forest_outliers: solo consume su salida. Es una funcion independiente (no la importa), por eso uses_functions esta vacio.