---
name: summarize_outlier_dims
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: pure
signature: "def summarize_outlier_dims(raw_numeric: dict, outlier_rows: list, top_k: int = 3) -> list"
description: "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."
tags: [eda, models, outliers, anomaly-detection, explainability, z-score, multivariate]
params:
  - name: raw_numeric
    desc: "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: outlier_rows
    desc: "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: top_k
    desc: "Numero de columnas (las de mayor |z-score|) a reportar por outlier. Default 3. Valores invalidos (no-int, bool, <1) caen a 3."
output: "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 []."
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
tested: true
tests: ["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"]
test_file_path: "python/functions/datascience/summarize_outlier_dims_test.py"
file_path: "python/functions/datascience/summarize_outlier_dims.py"
---

## Ejemplo

```python
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.
