You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
330 lines
10 KiB
Python
330 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Callable, Iterable, Optional, Tuple, Union
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
|
|
FilterFn = Callable[[object], object]
|
|
|
|
def plot_xy(df, x_col, y_col, label:Optional[str] = None, ax=None,
|
|
xlim: Optional[Tuple[float, float]] = None,
|
|
ylim: Optional[Tuple[float, float]] = None,
|
|
marker: str = "o",
|
|
linestyle: str = "-",
|
|
legen_loc=None,):
|
|
|
|
"""
|
|
Gráfica y vs x para un DataFrame.
|
|
- ax: para superponer curvas
|
|
- xlim/ylim: zoom visual (No filtrado datos)
|
|
"""
|
|
|
|
if ax is None:
|
|
_, ax = plt.subplots(figsize=(7,5))
|
|
|
|
ax.plot(df[x_col], df[y_col], marker=marker, linestyle=linestyle, label=label)
|
|
ax.set_xlabel(x_col)
|
|
ax.set_ylabel(y_col)
|
|
ax.grid(True)
|
|
|
|
if xlim is not None:
|
|
ax.set_xlim(xlim)
|
|
if ylim is not None:
|
|
ax.set_ylim(ylim)
|
|
|
|
if legen_loc is not None:
|
|
ax.legend(loc=legen_loc)
|
|
elif label:
|
|
ax.legend()
|
|
|
|
return ax
|
|
|
|
def comparar_rondas(data: dict, experimento: int, x_col: str, y_col: str,
|
|
rondas: Iterable[int] = (1,2),
|
|
filter_fn: Optional[FilterFn] = None,
|
|
xlim: Optional[Tuple[float, float]] = None,
|
|
ylim: Optional[Tuple[float, float]] = None,
|
|
title: Optional[str] = None,
|
|
legend_loc: Optional[str] = None,):
|
|
"""
|
|
Compara el mismo experimento entre rondas:
|
|
data[(ronda, experimento)] -> df
|
|
|
|
filter_fn: funcion opcional que recibe df y devuelve df filtrado.
|
|
Ejemplo: filter_fn = lambda df: apply_filter(df, row_start=10, row_end=40)
|
|
"""
|
|
fig, ax = plt.subplots(figsize=(7,5))
|
|
|
|
for ronda in rondas:
|
|
df = data[(ronda, experimento)]
|
|
if filter_fn is not None:
|
|
df = filter_fn(df)
|
|
|
|
plot_xy(df, x_col, y_col, label=f"Ronda {ronda}", ax=ax,
|
|
xlim=xlim, ylim=ylim, legen_loc=legend_loc)
|
|
|
|
if title is "False":
|
|
pass
|
|
else:
|
|
ax.set_title(title or f"Experimento {experimento} - Comparación de Rondas")
|
|
|
|
plt.show()
|
|
|
|
return fig, ax
|
|
|
|
def comparar_experimentos(data: dict, ronda: int, x_col: str, y_col: str,
|
|
experimentos: Iterable[int] = (1,2,3,4),
|
|
show: bool = False,
|
|
filter_fn: Optional[FilterFn] = None,
|
|
xlim: Optional[Tuple[float, float]] = None,
|
|
ylim: Optional[Tuple[float, float]] = None,
|
|
title: Optional[str] = None,
|
|
legend_loc: Optional[str] = None,
|
|
):
|
|
"""
|
|
Comparar varios experimentos dentro de una misma ronda.
|
|
"""
|
|
|
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
|
|
for exp in experimentos:
|
|
df = data[(ronda, exp)]
|
|
if filter_fn is not None:
|
|
df = filter_fn(df)
|
|
|
|
plot_xy(df, x_col, y_col, label=f"Experimento {exp}", ax=ax, xlim=xlim, ylim=ylim, legen_loc=legend_loc)
|
|
if title is "False":
|
|
pass
|
|
else:
|
|
ax.set_title(title or f"Ronda {ronda} - Comparación de Experimentos")
|
|
|
|
if show is True:
|
|
plt.show()
|
|
return fig, ax
|
|
|
|
def plot_con_ajuste(df, x_col: str, y_col: str, label_datos: str = "Datos",
|
|
label_ajuste: str = "Ajuste lineal",
|
|
show: bool = True
|
|
):
|
|
"""
|
|
Grafica scatter + ajuste lineal (y = mx + b). Devuelve (m, b)
|
|
"""
|
|
|
|
x = np.asarray(df[x_col].values, dtype=float)
|
|
y = np.asarray(df[y_col].values, dtype=float)
|
|
|
|
m, b = np.polyfit(x, y, 1)
|
|
|
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
ax.scatter(x, y, label=label_datos)
|
|
ax.plot(x, m * x + b, label=f"{label_ajuste} (m={m:.4f})")
|
|
|
|
ax.set_xlabel(x_col)
|
|
ax.set_ylabel(y_col)
|
|
ax.grid(True)
|
|
ax.legend()
|
|
|
|
if show:
|
|
plt.show()
|
|
|
|
return m, b, fig, ax
|
|
|
|
def plot_3D(df, x_col: str, y_col: str, z_col: str,
|
|
title: Optional[str] = None,):
|
|
"""
|
|
Gráfica 3D: (x, y, z) como dispersión.
|
|
"""
|
|
|
|
from mpl_toolkits.mplot3d import Axes3D #noqa: F401
|
|
|
|
fig = plt.figure(figsize=(8, 6))
|
|
ax = fig.add_subplot(111, projection="3d")
|
|
|
|
ax.scatter(df[x_col], df[y_col], df[z_col])
|
|
ax.set_xlabel(x_col)
|
|
ax.set_ylabel(y_col)
|
|
ax.set_zlabel(z_col)
|
|
ax.set_title(title or f"3D: {x_col} vs {y_col} vs {z_col}")
|
|
|
|
plt.show()
|
|
return fig, ax
|
|
|
|
def plot_color_map(df, x_col: str, y_col: str, z_col: str,
|
|
title: Optional[str] = None, ):
|
|
|
|
"""
|
|
Scatter 2D con tercera variable como color
|
|
"""
|
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
sc = ax.scatter(df[x_col], df[y_col], c=df[z_col], cmap="viridis")
|
|
|
|
cbar = plt.colorbar(sc, ax=ax)
|
|
cbar.set_label(z_col)
|
|
|
|
ax.set_xlabel(x_col)
|
|
ax.set_ylabel(y_col)
|
|
ax.grid(True)
|
|
ax.set_title(title or f"{y_col} vs {x_col} (color={z_col})")
|
|
|
|
plt.show()
|
|
return fig, ax
|
|
|
|
def plot_dual_axis(df, x_col: str, y1_col: str, y2_col: str, ax=None,
|
|
label1: Optional[str] = None,
|
|
label2: Optional[str] = None,
|
|
title: Optional[str] = None,
|
|
show: bool = True):
|
|
|
|
"""
|
|
Gráfica con doble eje Y:
|
|
y1 vs x (izquierda) y y2 vs x (derecha)
|
|
"""
|
|
if ax is None:
|
|
fig, ax1 = plt.subplots(figsize=(7, 5))
|
|
else:
|
|
ax1 = ax
|
|
fig = ax1.figure
|
|
|
|
ax2 = ax1.twinx()
|
|
|
|
ax1.plot(df[x_col], df[y1_col], marker="o", linestyle="-", color="b",label= label1 or y1_col)
|
|
ax2.plot(df[x_col], df[y2_col], marker="s", linestyle="--", color="r",label= label2 or y2_col)
|
|
|
|
ax1.set_xlabel(x_col)
|
|
ax1.set_ylabel(y1_col)
|
|
ax2.set_ylabel(y2_col)
|
|
|
|
ax1.grid(True)
|
|
|
|
ax1.set_title(title or f"{y1_col} y {y2_col} vs {x_col}")
|
|
|
|
# Leyendas Combinadas
|
|
lines, labels = [], []
|
|
for a in (ax1, ax2):
|
|
l, lab = a.get_legend_handles_labels()
|
|
lines += l
|
|
labels += lab
|
|
ax1.legend(lines, labels, loc="best")
|
|
|
|
if show is not True:
|
|
plt.show()
|
|
return fig, ax1, ax2
|
|
|
|
def marcar_zonas(ax, limites, labels=None, colors=None):
|
|
"""
|
|
Marca zonas en una gráfica con líneas verticales.
|
|
|
|
limites: lista de voltajes donde cambian las zonas
|
|
lables: nombre de cada zona
|
|
colors: colores opcionales
|
|
"""
|
|
|
|
if colors is None:
|
|
colors = ["gray", "blue", "green", "red"]
|
|
|
|
for i, v in enumerate(limites):
|
|
|
|
ax.axvline(v, linestyle="--", color="black", alpha=0.6)
|
|
|
|
if labels and i < len(labels):
|
|
ax.text(
|
|
v,
|
|
ax.get_ylim()[1]*0.65,
|
|
labels[i],
|
|
rotation=90,
|
|
verticalalignment="top",
|
|
horizontalalignment="right",
|
|
fontsize=12
|
|
)
|
|
def sombrear_zonas(ax, limites, colores=None, alpha=0.15):
|
|
"""
|
|
Sombrea las zonas del experimento
|
|
"""
|
|
|
|
if colores is None:
|
|
colores = ["gray", "blue", "green", "red"]
|
|
|
|
for i in range(len(limites)-1):
|
|
ax.axvspan(
|
|
limites[i],
|
|
limites[i+1],
|
|
color=colores[i],
|
|
alpha=alpha
|
|
)
|
|
|
|
def percent_change(data:dict, ronda: int, x_col: str, y_col: str,
|
|
experimentos: Iterable[int] = (1,2,3,4),
|
|
show: bool = False,
|
|
btween_exp: bool = False,
|
|
filter_fn: Optional[FilterFn] = None,
|
|
xlim: Optional[Tuple[float, float]] = None,
|
|
ylim: Optional[Tuple[float, float]] = None,
|
|
title: Optional[str] = None, ):
|
|
|
|
fig, ax = plt.subplots(figsize=(7,5))
|
|
|
|
if btween_exp is False:
|
|
change_value = {}
|
|
sum_mean = 0
|
|
|
|
for exp in experimentos:
|
|
df = data[(ronda, exp)]
|
|
if filter_fn is not None:
|
|
df = filter_fn(df)
|
|
|
|
R = df[y_col].values
|
|
x_ = df[x_col].values
|
|
for i in range(len(R) - 1):
|
|
change_value[i] = ((R[i+1] - R[i]) / R[i]) * 100
|
|
pct = change_value[i]
|
|
sum_mean = sum_mean + abs(pct)
|
|
print(f"Δ% Experiment:{exp} between data {i} and {i+1}: {pct:.2f}%")
|
|
if show is True:
|
|
ax.annotate(f"{pct:.1f}%",
|
|
(x_[i+1], R[i+1]),
|
|
textcoords="offset points",
|
|
xytext=(0,8),
|
|
ha='center',
|
|
fontsize=8,
|
|
color="red")
|
|
mean = sum_mean/len(change_value)
|
|
change_mx_min = ((R[(len(R)-1)] - R[0])/R[0]) * 100
|
|
print(f"Last_Value:{R[(len(R)-1)]:.3f} Fst_value:{R[0]:.3f}")
|
|
print(f"The mean percent change value in Experiment {exp} are : {mean:.2f}%")
|
|
print(f"The percent change between the last and first value of experiment {exp}: {change_mx_min:.2f}%")
|
|
plot_xy(df, x_col, y_col, label=f"Experimento {exp}", ax=ax, xlim=xlim, ylim=ylim)
|
|
sum_mean = 0
|
|
else:
|
|
change_value = {}
|
|
df = {}
|
|
i = 0
|
|
for exp in experimentos:
|
|
df[i] = data[(ronda, exp)]
|
|
if filter_fn is not None:
|
|
df[i] = filter_fn(df[i])
|
|
i = i + 1
|
|
R1 = df[0][y_col].values
|
|
R2 = df[1][y_col].values
|
|
x_1 = df[0][x_col].values
|
|
x_2 = df[1][x_col].values
|
|
for x in range(len(R1)):
|
|
change_value[x] = ((R2[x] - R1[x]) / R1[x]) * 100
|
|
pct = change_value[x]
|
|
print(f"Δ% Punto {x}: Exp{experimentos[0]} vs Exp{experimentos[1]} = {pct:.2f}%")
|
|
if show is True:
|
|
# Line that connect each point
|
|
ax.plot([x_1[x], x_2[x]], [R1[x], R2[x]], color="black", linestyle="-", linewidth=1)
|
|
# Text with the change percent in the middle of the line
|
|
ax.annotate(f"{pct:.1f}%",
|
|
xy=((x_1[x]+x_2[x])/2, (R1[x]+R2[x])/2),
|
|
textcoords="offset points",
|
|
xytext=(0,3),
|
|
ha='center', fontsize=8, color="red")
|
|
plot_xy(df[0], x_col, y_col, label=f"Experimento {experimentos[0]}", ax=ax, xlim=xlim, ylim=ylim)
|
|
plot_xy(df[1], x_col, y_col, label=f"Experimento {experimentos[1]}", ax=ax, xlim=xlim, ylim=ylim)
|
|
|
|
ax.set_title(title or f"Round {ronda} - Experiments Comparative")
|
|
|
|
if show is True:
|
|
plt.show()
|
|
return fig, ax |