eaca41a532
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>
101 lines
2.8 KiB
Python
101 lines
2.8 KiB
Python
"""Tests para relationship_scatter_figure (scatter de un par numérico, grupo eda).
|
|
|
|
Usa el backend Agg sin pyplot global; no muestra ni guarda figuras. Cada test
|
|
cierra explícitamente la Figure construida (matplotlib.pyplot.close) para no
|
|
acumular estado entre tests.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
import matplotlib
|
|
|
|
matplotlib.use("Agg")
|
|
|
|
import matplotlib.pyplot as plt # noqa: E402
|
|
from matplotlib.collections import PathCollection # noqa: E402
|
|
from matplotlib.figure import Figure # noqa: E402
|
|
|
|
from relationship_scatter_figure import relationship_scatter_figure
|
|
|
|
|
|
def _scatter_offsets(fig):
|
|
"""Return the plotted points of the first PathCollection (scatter) found."""
|
|
for ax in fig.axes:
|
|
for coll in ax.collections:
|
|
if isinstance(coll, PathCollection):
|
|
return coll.get_offsets()
|
|
return None
|
|
|
|
|
|
def test_returns_figure():
|
|
xs = [float(i) for i in range(20)]
|
|
ys = [2.0 * x + 1.0 for x in xs] # y = 2x + 1
|
|
classification = {
|
|
"tipo": "lineal",
|
|
"pearson": 1.0,
|
|
"r2_linear": 1.0,
|
|
"spearman": 1.0,
|
|
"r2_poly2": 1.0,
|
|
"r2_poly3": 1.0,
|
|
"best_degree": 1,
|
|
"coeffs": [2.0, 1.0],
|
|
}
|
|
fig = relationship_scatter_figure(
|
|
xs, ys, x_label="a", y_label="b", classification=classification
|
|
)
|
|
assert hasattr(fig, "savefig")
|
|
assert len(fig.axes) >= 1
|
|
plt.close(fig)
|
|
|
|
|
|
def test_downsample_determinista():
|
|
n = 5000
|
|
xs = [float(i) for i in range(n)]
|
|
ys = [0.5 * x for x in xs]
|
|
classification = {
|
|
"tipo": "lineal",
|
|
"pearson": 1.0,
|
|
"r2_linear": 1.0,
|
|
"spearman": 1.0,
|
|
"r2_poly2": 1.0,
|
|
"r2_poly3": 1.0,
|
|
"best_degree": 1,
|
|
"coeffs": [0.5, 0.0],
|
|
}
|
|
fig = relationship_scatter_figure(
|
|
xs, ys, x_label="x", y_label="y", classification=classification, max_points=1000
|
|
)
|
|
assert isinstance(fig, Figure)
|
|
offsets = _scatter_offsets(fig)
|
|
assert offsets is not None
|
|
# El nº de puntos dibujados no debe exceder el cap.
|
|
assert len(offsets) <= 1000
|
|
plt.close(fig)
|
|
|
|
|
|
def test_empty_no_lanza():
|
|
fig = relationship_scatter_figure([], [], x_label="x", y_label="y")
|
|
assert isinstance(fig, Figure)
|
|
plt.close(fig)
|
|
|
|
|
|
def test_classification_none():
|
|
# Solo se ejecuta si el módulo hermano classify_relationship_type existe.
|
|
try:
|
|
import classify_relationship_type # noqa: F401
|
|
except Exception:
|
|
import pytest
|
|
|
|
pytest.skip("classify_relationship_type aún no disponible")
|
|
xs = [float(i) for i in range(30)]
|
|
ys = [3.0 * x - 2.0 for x in xs]
|
|
fig = relationship_scatter_figure(
|
|
xs, ys, x_label="a", y_label="b", classification=None
|
|
)
|
|
assert isinstance(fig, Figure)
|
|
assert len(fig.axes) >= 1
|
|
plt.close(fig)
|