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