6f88f184f1
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>
8.1 KiB
8.1 KiB
id, name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, example, tested, tests, test_file_path, file_path, params, output
| id | name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | example | tested | tests | test_file_path | file_path | params | output | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| build_boxplots_figure_py_datascience | build_boxplots_figure | function | py | datascience | 1.0.0 | impure | def build_boxplots_figure(boxes: list, title: str = "", max_boxes: int = 12) -> "matplotlib.figure.Figure" | Construye una unica figura matplotlib con boxplots de Tukey HORIZONTALES (uno por columna) usando ax.bxp: caja Q1-Q3, bigotes hasta 1.5*IQR, linea de mediana y puntos atipicos. Consume la salida de build_boxplot_stats (un dict box por columna, leido con .get) mas una lista opcional de outliers crudos por columna; si vienen los dibuja como puntos (showfliers), si no marca solo box[min]/box[max] cuando hay outliers de cola (igual que num_distr). Dibuja como mucho max_boxes cajas (las primeras, ya ordenadas por contaminacion por el caller) y avisa de la truncacion con (mostrando N de M). Backend Agg sin pyplot global; alto adaptativo al nº de cajas. Defensiva: omite entradas invalidas y NUNCA lanza — sin cajas validas devuelve una figura placeholder (sin boxplots). Es la version small-multiples del capitulo num_distr para responder que columnas tienen mas outliers de un vistazo. |
|
false | error_go_core |
|
from datascience.build_boxplot_stats import build_boxplot_stats from datascience.build_boxplots_figure import build_boxplots_figure boxes = [ {"name": "ingresos", "box": build_boxplot_stats({"min": 1.0, "max": 9e3, "p25": 1e3, "median": 2e3, "p75": 3e3, "n_outliers": 7}), "fliers": None}, {"name": "edad", "box": build_boxplot_stats({"min": 0.0, "max": 99.0, "p25": 25.0, "median": 38.0, "p75": 52.0}), "fliers": None}, ] fig = build_boxplots_figure(boxes, title="Outliers por columna", max_boxes=12) | true |
|
python/functions/datascience/build_boxplots_figure_test.py | python/functions/datascience/build_boxplots_figure.py |
|
Un matplotlib.figure.Figure (figsize 7.0 x alto adaptativo = max(2.0, 0.5*n + 1.0), dpi 150) con un unico Axes que apila boxplots horizontales de Tukey (ax.bxp, orientation=horizontal con fallback vert=False), uno por columna valida, de arriba a abajo en el orden recibido. Cada caja: relleno #9ec6df, borde/bigotes/caps #5b8aa6, mediana #2e8b57, atipicos #c0392b. Etiquetas del eje Y = nombres de columna; eje X etiquetado "valor". Outliers dibujados desde fliers crudos (showfliers) o, si faltan, marcados en box[min]/box[max] segun has_low/high_outliers. Si no queda ninguna caja valida (lista vacia o todas invalidas) devuelve una Figure placeholder con texto centrado "(sin boxplots)"; cualquier error inesperado se captura y devuelve una Figure con el mensaje de error. NUNCA lanza. El caller rasteriza/cierra la figura; la funcion no la muestra ni la guarda. |
Ejemplo
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience.build_boxplot_stats import build_boxplot_stats
from datascience.build_boxplots_figure import build_boxplots_figure
# Un `box` por columna numérica, derivado del sub-bloque `numeric` del profile
# (salida de describe_numeric). El caller los pasa ya ordenados por outlier_pct.
boxes = [
{
"name": "ingresos",
"box": build_boxplot_stats({
"min": 1.0, "max": 9000.0,
"p25": 1000.0, "median": 2000.0, "p75": 3000.0,
"n_outliers": 7,
}),
"fliers": None, # valores crudos desconocidos -> se marca solo el extremo.
},
{
"name": "edad",
"box": build_boxplot_stats({
"min": 0.0, "max": 99.0,
"p25": 25.0, "median": 38.0, "p75": 52.0,
}),
"fliers": [88.0, 95.0, 99.0], # outliers crudos -> se dibujan como puntos.
},
]
fig = build_boxplots_figure(boxes, title="Outliers por columna", max_boxes=12)
# El renderer del informe lo rasteriza; aquí solo persistimos para inspección.
fig.savefig("/tmp/boxplots.png")
Cuando usarla
Úsala en el capítulo de outliers de un informe EDA cuando quieras comparar de un
vistazo qué columnas están más contaminadas por valores atípicos: a diferencia
de num_distr (que dibuja un histograma+boxplot por columna en figuras
separadas), aquí apilas todos los boxplots horizontales en una sola figura
(small multiples). Primero deriva el box de cada columna con
build_boxplot_stats, ordénalas por outlier_pct descendente, envuélvelas como
{"name", "box", "fliers"} y pásaselas. Si tienes los valores crudos fuera de
las vallas, métele la lista fliers y se dibujarán como puntos; si no, la
función marca solo los extremos min/max cuando hay cola.
Gotchas
- Impura por matplotlib. Toca la maquinaria de render. Usa el backend
Aggy la API orientada a objetosFigure/add_subplot— NUNCApyplot.*aquí, para no tocar el estado global ni filtrar figuras entre llamadas.pyplotNO es thread-safe; esta función construye elFiguredirectamente, así que es segura de llamar en bucle desde el renderer. - El caller cierra la figura. Devuelve el
Figurepero no lo muestra ni lo guarda. Quien la consume debe rasterizarla y luego liberarla (matplotlib.pyplot.close(fig)) para no acumular memoria en lotes grandes. fliersopcional, semántica distinta. Si pasas la lista de outliers crudos se dibujan todos como puntos (showfliers=True). Si esNone/ausente los valores son desconocidos y solo se marca un punto enbox["min"]/box["max"]cuandohas_low_outliers/has_high_outliers— mismo criterio quenum_distr. No inventes fliers a partir del profile: elboxno trae los valores crudos, solo si los extremos superan las vallas.- API de orientación de
ax.bxp. matplotlib reciente usaorientation="horizontal"; las versiones antiguas usanvert=False. La función prueba la primera y cae a la segunda enexcept TypeError, así que funciona en ambas. Sibxpfalla del todo, el Axes degrada a un texto "(boxplot no disponible)" en vez de propagar. - Truncación visible.
max_boxes(default 12) limita el nº de cajas para que ninguna se solape; si la lista trae más, las sobrantes se descartan pero se avisa en el título con "(mostrando N de M)". Pasa las columnas ya ordenadas por contaminación para que las descartadas sean las menos relevantes. - Defensiva, nunca lanza. Lista vacía, entradas no-dict, sin
box, o sinq1/median/q3se omiten sin propagar; sin cajas válidas devuelve un placeholder "(sin boxplots)" y cualquier error inesperado se captura en una figura con el texto del error. No envuelvas la llamada en try/except por miedo a un raise — no lo hay.