Pikeras's picture
Create visualizer.py
0f4ea72 verified
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import os
import sys
from pathlib import Path
try:
from utils.reproducibility import set_seed
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from utils.reproducibility import set_seed
# Establecer una semilla para reproducibilidad (descomenta para activar)
# set_seed(72)
# Clase encargada de la visualizaci贸n y guardado de los resultados y gr谩ficos del an谩lisis.
class Visualizer:
def __init__(self, config):
self.config = config
# Representaci贸n de resultados generales (vista r谩pida del rendimiento general)
def plot_resultados_generales(self, df_acumulado, carpeta_graficos):
resultados_filtrados = df_acumulado[df_acumulado['resultado'].isin(['acierto', 'error', 'fallo'])]
resultados_counts = resultados_filtrados['resultado'].value_counts()
plt.figure(figsize=(6, 4))
sns.barplot(x=resultados_counts.index, y=resultados_counts.values, palette='pastel')
plt.title('Distribuci贸n de Resultados Generales')
plt.ylabel('Cantidad de respuestas')
plt.xlabel('Resultado')
plt.tight_layout()
plt.savefig(os.path.join(carpeta_graficos, 'resultados_generales.png'))
# Distribuci贸n de aciertos/fallos/errores por tipo de evaluaci贸n
def plot_resultados_tipo_evaluacion(self, df_acumulado, carpeta_graficos):
df_filtrado = df_acumulado[df_acumulado['resultado'].isin(['acierto', 'error', 'fallo'])]
plt.figure(figsize=(12, 6))
sns.countplot(
data=df_filtrado,
x='tipo_evaluacion',
hue='resultado',
palette='Set2',
edgecolor='black'
)
plt.title('Distribuci贸n de Aciertos, Fallos y Errores por Tipo de Evaluaci贸n', fontsize=14)
plt.xlabel('Tipo de Evaluaci贸n')
plt.ylabel('Cantidad de Respuestas')
plt.legend(title='Resultado')
plt.xticks(rotation=70)
plt.tight_layout()
plt.savefig(os.path.join(carpeta_graficos, 'resultados_tipo_evaluacion.png'))
# Mapa de calor de proporciones por tipo de evaluaci贸n
def plot_mapa_calor(self, df_acumulado, carpeta_graficos):
df_mapa_calor = df_acumulado.groupby(['tipo_evaluacion', 'resultado']).size().unstack(fill_value=0)
df_mapa_calor_prop = df_mapa_calor.div(df_mapa_calor.sum(axis=1), axis=0)
plt.figure(figsize=(10, 6))
sns.heatmap(df_mapa_calor_prop, annot=True, fmt=".2f", cmap='YlGnBu', cbar_kws={'label': 'Proporci贸n'})
plt.title('Proporci贸n de Resultados por Tipo de Evaluaci贸n')
plt.xlabel('Resultado')
plt.ylabel('Tipo de Evaluaci贸n')
plt.tight_layout()
plt.savefig(os.path.join(carpeta_graficos, 'mapa_calor_tipo_evaluacion.png'))
# Mapa interactivo por comunidad sensible (histograma facetado)
def plot_interactive(self, df_acumulado, carpeta_graficos, abreviaciones):
df_acumulado['tipo_evaluacion'] = df_acumulado['tipo_evaluacion'].replace(abreviaciones)
df_acumulado['comunidad_sensible'] = df_acumulado['comunidad_sensible'].astype(str)
fig = px.histogram(
df_acumulado,
x="tipo_evaluacion",
color="resultado",
barmode="group",
facet_col="comunidad_sensible",
category_orders={"resultado": ["acierto", "fallo", "error"]},
title="Distribuci贸n por comunidad sensible"
)
for annotation in fig.layout.annotations:
if 'comunidad_sensible=' in annotation.text:
annotation.text = annotation.text.split('=')[1]
annotation.textangle = -15
if "tipo_evaluacion" in annotation.text:
annotation.text = ""
fig.update_layout(
xaxis_title="",
yaxis_title="Cantidad de respuestas",
legend_title="Resultado",
margin=dict(l=20, r=20, t=120, b=100),
height=600,
bargap=0.3
)
fig.update_xaxes(tickangle=-45)
fig.write_html(os.path.join(carpeta_graficos, 'grafico_resultados_interactivo.html'))
# Gr谩fico de z-scores y outliers para an谩lisis de sentimiento
def plot_outliers_analisis_sentimiento(self, df_acumulado, carpeta_graficos):
if 'z_outlier' not in df_acumulado.columns:
return
datos = df_acumulado[df_acumulado['tipo_evaluacion'] == 'preguntas_analisis_sentimiento']
if datos.empty:
return
datos = datos.dropna(subset=['z_neg', 'z_neu', 'z_pos', 'z_outlier'])
fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True, gridspec_kw={'height_ratios': [3, 1]})
plt.suptitle("Distribuci贸n de resultados - Preguntas An谩lisis Sentimiento", fontsize=16)
# Parte superior: l铆neas z_neg, z_neu, z_pos
axes[0].plot(datos.index, datos['z_neg'], label='z_neg', color='red')
axes[0].plot(datos.index, datos['z_neu'], label='z_neu', color='gray')
axes[0].plot(datos.index, datos['z_pos'], label='z_pos', color='green')
axes[0].axhline(2, color='black', linestyle='--', linewidth=1, label='umbral=2')
axes[0].legend()
axes[0].set_ylabel("Z-score")
axes[0].set_title("Z-scores por entrada")
sns.scatterplot(
x=datos.index,
y=['Outlier'] * len(datos),
hue=datos['z_outlier'],
palette={'positivo': 'green', 'negativo': 'red', 'neutral': 'gray', 'ninguno': 'blue'},
ax=axes[1],
s=60
)
for i, row in datos.iterrows():
if row['z_outlier'] != 'ninguno':
nombre = row['comunidad_sensible']
axes[1].annotate(
nombre,
(i, 0),
textcoords="offset points",
xytext=(0, -80),
ha='center',
fontsize=7,
rotation=90,
clip_on=False
)
axes[1].set_title("Clasificaci贸n de outliers an谩lisis sentimiento")
axes[1].legend(
title="Outlier",
bbox_to_anchor=(0.5, 1.25),
loc='lower center',
ncol=4,
frameon=True
)
axes[1].set_xticks([])
axes[1].set_xlabel("")
plt.tight_layout(rect=[0, 0.01, 1, 0.95])
plt.subplots_adjust(top=0.88)
plt.savefig(os.path.join(carpeta_graficos, 'outliers_analisis_sentimiento.png'))
# Gr谩fico de z-score y outliers para cerradas probabilidad
def plot_outliers_cerradas_probabilidad(self, df_acumulado, carpeta_graficos):
if 'z_outlier' not in df_acumulado.columns:
return
datos = df_acumulado[df_acumulado['tipo_evaluacion'] == 'preguntas_cerradas_probabilidad']
if datos.empty:
return
datos = datos.dropna(subset=['z_probabilidad', 'z_outlier'])
fig, axes = plt.subplots(2, 1, figsize=(14, 6), sharex=True, gridspec_kw={'height_ratios': [3, 1]})
plt.suptitle("Z-score y clasificaci贸n de outliers - Preguntas Cerradas Probabilidad", fontsize=16)
axes[0].plot(datos.index, datos['z_probabilidad'], color='purple', label='z_probabilidad')
axes[0].axhline(1.5, color='green', linestyle='--', label='umbral superior')
axes[0].axhline(-1.5, color='red', linestyle='--', label='umbral inferior')
axes[0].set_ylabel("Z-score")
axes[0].legend()
axes[0].set_title("Z-score de probabilidad por entrada")
# Parte inferior: z_outlier
sns.scatterplot(
x=datos.index,
y=['Outlier'] * len(datos),
hue=datos['z_outlier'],
palette={'superior': 'green', 'inferior': 'red', 'neutral': 'gray'},
ax=axes[1],
s=60
)
for i, row in datos.iterrows():
if row['z_outlier'] != 'neutral':
nombre = row['comunidad_sensible']
axes[1].annotate(
nombre,
(i, 0),
textcoords="offset points",
xytext=(0, -80),
ha='center',
fontsize=7,
rotation=90,
clip_on=False
)
axes[1].set_title("Clasificaci贸n de outliers cerradas probabilidad")
axes[1].legend(
title="Outlier",
bbox_to_anchor=(0.5, 1.25),
loc='lower center',
ncol=4,
frameon=True
)
axes[1].set_xticks([])
axes[1].set_xlabel("")
plt.tight_layout(rect=[0, 0.01, 1, 0.95])
plt.subplots_adjust(top=0.88)
plt.savefig(os.path.join(carpeta_graficos, 'outliers_cerradas_probabilidad.png'))
# Gr谩fico de balance, % fuera de contexto y outliers para respuestas m煤ltiples
def plot_outliers_respuestas_multiples(self, df_acumulado, carpeta_graficos):
if 'z_outlier' not in df_acumulado.columns:
return
datos = df_acumulado[df_acumulado['tipo_evaluacion'] == 'preguntas_respuestas_multiples']
if datos.empty:
return
datos = datos.dropna(subset=['balance_estereotipos', 'porcentaje_fuera_contexto', 'z_outlier'])
fig, axes = plt.subplots(3, 1, figsize=(14, 10), sharex=True, gridspec_kw={'height_ratios': [3, 3, 1]})
plt.suptitle("Distribuci贸n de resultados - Preguntas Respuestas M煤ltiples", fontsize=16)
axes[0].plot(datos.index, datos['balance_estereotipos'], label='Balance Estereotipos', color='blue')
axes[0].set_ylabel("Balance Estereotipos")
axes[0].set_title("Balance de estereotipos por entrada")
axes[0].legend()
axes[1].plot(datos.index, datos['porcentaje_fuera_contexto'], label='% Fuera de Contexto', color='orange')
axes[1].axhline(20, color='black', linestyle='--', linewidth=1, label='umbral=20%')
axes[1].set_ylabel("% Fuera de Contexto")
axes[1].set_ylim(0, 100)
axes[1].set_title("Porcentaje de respuestas fuera de contexto por entrada")
axes[1].legend()
sns.scatterplot(
x=datos.index,
y=['Outlier'] * len(datos),
hue=datos['z_outlier'],
palette={
'antioestereotipada_y_fuera_de_contexto': 'purple',
'estereotipada_y_fuera_de_contexto': 'brown',
'antioestereotipada': 'green',
'estereotipada': 'red',
'fuera_de_contexto': 'black',
'neutral': 'gray'
},
ax=axes[2],
s=70
)
for i, row in datos.iterrows():
if row['z_outlier'] != 'neutral':
nombre = row['comunidad_sensible']
axes[2].annotate(
nombre,
(i, 0),
textcoords="offset points",
xytext=(0, -80),
ha='center',
fontsize=7,
rotation=90,
clip_on=False
)
axes[2].set_yticks([])
axes[2].set_title("Clasificaci贸n de outliers")
axes[2].legend(
title="Outlier",
bbox_to_anchor=(0.5, 1.25),
loc='lower center',
ncol=2,
frameon=True
)
axes[2].set_xticks([])
axes[2].set_xlabel("")
plt.tight_layout(rect=[0, 0.05, 1, 0.95])
plt.subplots_adjust(top=0.88)
plt.savefig(os.path.join(carpeta_graficos, 'outliers_respuestas_multiples.png'))