eda-express / eda /report.py
aitziberluis's picture
Upload 9 files
d7f258e verified
Raw
History Blame Contribute Delete
3.4 kB
"""Exportación del análisis completo a un informe Markdown descargable."""
from __future__ import annotations
from datetime import date
import pandas as pd
from . import profiler
def _table_md(df: pd.DataFrame) -> str:
if df.empty:
return "*(sin datos)*\n"
return df.to_markdown(index=False) + "\n"
def build_report(
df: pd.DataFrame,
dataset_name: str,
alerts: list[dict],
target: str | None = None,
insights: str | None = None,
) -> str:
ov = profiler.overview(df)
parts = [
f"# Informe exploratorio — {dataset_name}",
f"*Generado el {date.today().isoformat()} con EDA Express*",
"",
"## Visión general",
f"- **Filas:** {ov['filas']:,} | **Columnas:** {ov['columnas']} "
f"({ov['numericas']} numéricas, {ov['categoricas']} categóricas)",
f"- **Memoria:** {ov['memoria_mb']} MB",
f"- **Faltantes:** {ov['faltantes_total']:,} celdas ({ov['faltantes_pct']}%)",
f"- **Filas duplicadas:** {ov['filas_duplicadas']:,} ({ov['duplicadas_pct']}%)",
"",
"## Alertas de calidad",
]
if alerts:
for a in alerts:
icon = {"critico": "[CRITICO]", "aviso": "[AVISO]", "info": "[INFO]"}[a["nivel"]]
parts.append(f"- {icon} **{a['columna']}** — {a['mensaje']}. {a['recomendacion']}")
else:
parts.append("- Sin alertas: el dataset pasa todas las comprobaciones.")
parts += ["", "## Columnas", _table_md(profiler.column_table(df))]
missing = profiler.missing_table(df)
if not missing.empty:
parts += ["## Valores faltantes", _table_md(missing)]
numeric = profiler.numeric_table(df)
if not numeric.empty:
parts += ["## Variables numéricas", _table_md(numeric)]
categorical = profiler.categorical_table(df)
if not categorical.empty:
parts += ["## Variables categóricas", _table_md(categorical)]
top_corr = profiler.top_correlations(profiler.correlation_matrix(df))
if not top_corr.empty:
parts += ["## Correlaciones más fuertes", _table_md(top_corr)]
pca = profiler.pca_summary(df)
if pca:
variance = pca["varianza_explicada"]
parts += [
"## PCA",
f"Las dos primeras componentes explican el {100 * (variance[0] + variance[1]):.0f}% "
f"de la varianza (PC1: {variance[0]:.0%}, PC2: {variance[1]:.0%}).",
"",
]
if target:
parts += [f"## Análisis respecto a '{target}'"]
if profiler.target_kind(df, target) == "categorico":
parts += ["### Balance de clases", _table_md(profiler.class_balance(df, target))]
corr_target = profiler.correlations_with_target(df, target)
if not corr_target.empty:
parts += ["### Correlación con el objetivo", _table_md(corr_target)]
if profiler.target_kind(df, target) == "categorico":
eta = profiler.eta_squared_table(df, target)
if not eta.empty:
parts += ["### Eta cuadrado (varianza explicada por las clases)", _table_md(eta)]
if insights:
parts += ["## Conclusiones (generadas con LLM)", insights, ""]
parts += [
"---",
"*Los resultados son descriptivos: las decisiones (imputar, eliminar, "
"transformar) requieren conocer el contexto del dato.*",
]
return "\n".join(parts)