Files
fn_registry/python/functions/datascience/relationship_scatter_figure.md
T
egutierrez eaca41a532 feat(eda): scatters de pares más correlacionados + tipo de relación en capítulo CORRELACION
Añade al capítulo `correlacion` del AutomaticEDA la visualización con scatters de
los pares numérico-numérico más correlacionados (positiva y negativamente) y,
para cada uno, la clasificación del tipo de relación: lineal, polinómica
(grado 2/3), monótona no-lineal o débil/sin forma.

Funciones nuevas del registry (dominio datascience, grupo eda):
- classify_relationship_type_py_datascience (pura): dadas dos listas numéricas
  pareadas, cruza Pearson r (lineal), Spearman ρ (monótona) y ajustes
  polinómicos de grado 2 y 3 (numpy.polyfit + R² manual) para etiquetar la
  forma. Reusa pearson y spearman_corr del registry. Umbrales calibrados para
  datos reales discretos/ruidosos (orden: débil → monótona → polinómica →
  lineal). Devuelve los coeficientes del mejor modelo para pintar la curva.
  No-throw.
- relationship_scatter_figure_py_datascience (impure): construye la Figure
  matplotlib del scatter de un par con su recta/curva de ajuste y una anotación
  del tipo + métricas (r, ρ, R²lin, R²poly). Backend Agg sin pyplot global,
  downsample determinista de los puntos dibujados, tendencia ordenada (binned /
  por valor) para el caso monótona sin polinomio. Defensiva ante vacío.

Capítulo correlacion.py (1.0.0 → 1.1.0): nueva sección "Relaciones más fuertes
(scatter)" tras la matriz + tablas top. Toma los top-K pares num↔num por |valor|
de profile['correlations']['pairs'], obtiene los datos crudos de cada par desde
ctx['raw_numeric'] y emite, por par, un Figure dentro de un Group keep-together
junto a una nota de texto con el tipo de relación (extraíble por pdftotext).
Solo num↔num: los pares cat↔cat (Cramér's V) y num↔cat (razón de correlación)
no llevan scatter. Cuando no hay raw_numeric (perfil lite/agregado o ctx None)
los scatters se omiten sin lanzar; la matriz + tablas siguen.

Verificado: golden EDA de titanic (run_models) — el capítulo Correlación del PDF
y PPTX incluye los scatters (pclass↔fare → monótona no-lineal, sibsp↔parch →
lineal, …) con su ajuste y etiqueta de tipo en texto. Tests de clasificación
sintética (lineal, y=x² → polinómica, y=exp(x) → monótona, ruido → débil) +
tests del capítulo (golden con raw_numeric, edge sin raw, par sin columna). Suite
automatic_eda + pipeline render_automatic_eda verde (141 passed). fn index sin
error.

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

7.4 KiB
Raw Blame History

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
relationship_scatter_figure_py_datascience relationship_scatter_figure function py datascience 1.0.0 impure def relationship_scatter_figure(xs: list, ys: list, x_label: str = "", y_label: str = "", classification: dict = None, max_points: int = 2000) -> "matplotlib.figure.Figure" Construye una figura matplotlib scatter de un par de variables numéricas con su curva/recta de ajuste y una anotación del tipo de relación (lineal, polinómica grado 2/3, monótona no-lineal, etc.) más sus métricas (r, ρ, R²lin, R²poly). Consume el dict de classify_relationship_type; si es None lo calcula internamente reusando esa función. Devuelve un matplotlib.figure.Figure listo para rasterizar por el renderer del informe EDA (PDF/PPTX). Backend Agg sin pyplot global; downsample determinista de los puntos dibujados; defensivo ante vacío/None.
eda
correlation
scatter
relationship
matplotlib
figure
visualization
datascience
impure
classify_relationship_type_py_datascience
false error_go_core
matplotlib
numpy
from relationship_scatter_figure import relationship_scatter_figure xs = [float(i) for i in range(100)] ys = [0.5 * x * x - x + 3 for x in xs] classification = { "tipo": "polinómica (grado 2)", "pearson": 0.97, "spearman": 0.99, "r2_linear": 0.92, "r2_poly2": 0.999, "r2_poly3": 0.999, "best_degree": 2, "coeffs": [0.5, -1.0, 3.0], } fig = relationship_scatter_figure(xs, ys, x_label="dosis", y_label="efecto", classification=classification) true
test_returns_figure
test_downsample_determinista
test_empty_no_lanza
test_classification_none
python/functions/datascience/relationship_scatter_figure_test.py python/functions/datascience/relationship_scatter_figure.py
name desc
xs Lista (o tupla) de valores x. Se emparejan por índice con ys. Valores None, bool, NaN o inf descartan ese par (lectura defensiva).
name desc
ys Lista (o tupla) de valores y, paralela a xs. Mismas reglas defensivas que xs.
name desc
x_label Etiqueta del eje/título para la variable x. Default "" (en el título cae a "x").
name desc
y_label Etiqueta del eje/título para la variable y. Default "" (en el título cae a "y").
name desc
classification Opcional. Dict de classify_relationship_type con claves tipo, pearson, r2_linear, spearman, r2_poly2, r2_poly3, best_degree, coeffs. Si es None se calcula internamente importando y llamando a classify_relationship_type sobre los pares limpios (self-contained). Si el módulo hermano no está disponible, se dibuja el scatter sin curva de ajuste ni anotación. Default None.
name desc
max_points Tope del nº de puntos DIBUJADOS. Si los pares limpios superan el tope, la nube se submuestrea por paso fijo ceil(n/max_points) tomando pairs[::step] — DETERMINISTA, no aleatorio, reproducible. La clasificación/ajuste usa SIEMPRE todos los pares limpios; el downsample solo adelgaza el dibujo. Valor no-positivo o no-int desactiva el downsample. Default 2000.
Un matplotlib.figure.Figure (figsize 6.4x4.0, dpi 150) con un Axes scatter (puntos semitransparentes alpha 0.5, color #4C72B0), la curva/recta de ajuste (numpy.polyval sobre coeffs, color #C44E52) cuando hay un ajuste polinómico disponible, título "{x_label} ↔ {y_label}", labels de ejes y una caja de anotación en la esquina superior izquierda con el tipo de relación y las métricas disponibles (r, ρ, R²lin, R²poly; se omiten las None). Si tras la limpieza hay menos de 2 pares válidos, devuelve igualmente una Figure con un texto centrado "Sin datos suficientes para el scatter" (nunca lanza). El caller rasteriza/cierra la figura; la función no la muestra ni la guarda.

Ejemplo

from relationship_scatter_figure import relationship_scatter_figure

# Par numérico con relación cuadrática y su clasificación (de
# classify_relationship_type). Pasándola explícita evitas recomputarla.
xs = [float(i) for i in range(100)]
ys = [0.5 * x * x - x + 3 for x in xs]
classification = {
    "tipo": "polinómica (grado 2)",
    "pearson": 0.97,
    "spearman": 0.99,
    "r2_linear": 0.92,
    "r2_poly2": 0.999,
    "r2_poly3": 0.999,
    "best_degree": 2,
    "coeffs": [0.5, -1.0, 3.0],
}

fig = relationship_scatter_figure(
    xs, ys, x_label="dosis", y_label="efecto", classification=classification
)

# El renderer del informe lo rasteriza; aquí solo persistimos para inspección.
fig.savefig("/tmp/scatter_dosis_efecto.png")

# Con classification=None la función la calcula internamente (self-contained):
fig2 = relationship_scatter_figure(xs, ys, x_label="dosis", y_label="efecto")

Cuando usarla

Úsala dentro del informe EDA automático cuando quieras visualizar de un vistazo la relación entre dos variables numéricas: la nube de puntos, la curva que mejor la ajusta y una etiqueta legible del tipo de relación con sus métricas. Es la pareja "vista humana" de classify_relationship_type: esa función decide el tipo y los coeficientes; esta los pinta en una Figure que el renderer del informe rasteriza a PDF/PPTX. Pásale el dict de clasificación si ya lo tienes calculado (evitas recomputar el ajuste); si no, déjalo en None y la función lo resuelve sola sobre los pares limpios. Pensada para móvil: anotación pequeña (fontsize 8) y nube adelgazada por max_points para que el PDF no pese.

Gotchas

  • Impura por matplotlib. Toca la maquinaria de render. Usa el backend Agg y la API orientada a objetos Figure/add_subplot — NUNCA pyplot.* aquí, para no tocar el estado global ni filtrar figuras entre llamadas. pyplot NO es thread-safe; esta función lo evita construyendo el Figure directamente, así que es segura de llamar en bucle desde el renderer.
  • El caller cierra la figura. Devuelve el Figure pero 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 de pares de columnas.
  • Downsample determinista, solo del dibujo. Cuando los pares limpios superan max_points, la nube DIBUJADA se adelgaza por paso fijo pairs[::step] (reproducible, no aleatorio). La clasificación y el ajuste usan SIEMPRE todos los pares limpios; el downsample no altera las métricas ni la curva.
  • classification=None ⇒ se calcula sola. Importa y llama a classify_relationship_type sobre los pares limpios. Si ese módulo hermano no está disponible (entorno incompleto), NO lanza: dibuja el scatter sin curva de ajuste ni anotación. Pasar la clasificación explícita es más barato (no recomputa el ajuste).
  • Sin curva para monótona no-lineal. Cuando coeffs es None o best_degree es None (p.ej. tipo "monótona no-lineal"), no se pinta recta polinómica — solo la nube y la anotación. Tampoco se dibuja la curva si el rango de x es nulo (todos los x iguales). Nunca falla por esto.
  • Defensiva, nunca lanza. xs=[], ys=[], menos de 2 pares válidos, ends None/bool/NaN/inf o coeffs malformado se manejan sin error: en el peor caso devuelve una Figure con "Sin datos suficientes para el scatter". No envuelvas la llamada en try/except por miedo a un raise — no lo hay.