def graficar_curse(ps, medias, contrastes):
fig, axes = plt.subplots(1, 2, figsize=(11, 4.2), constrained_layout=True)
axes[0].plot(ps, medias, marker="o")
axes[0].set_xlabel("Dimensión p")
axes[0].set_ylabel("Distancia media entre pares")
axes[0].set_title("Las distancias crecen con p")
axes[1].plot(ps, contrastes, marker="o", color="C1")
axes[1].set_xlabel("Dimensión p")
axes[1].set_ylabel("(máx − mín) / media")
axes[1].set_title("El contraste cerca/lejos se aplana")
fig.suptitle("Alta dimensionalidad: n fijo, p creciente")
plt.show()
def graficar_nube_2d(X, titulo, centro=None, clases=None, ejes=("x₁", "x₂")):
fig, ax = plt.subplots(figsize=(6.6, 6.2))
if clases is None:
ax.scatter(X[:, 0], X[:, 1], s=18, alpha=0.65)
else:
for lab in np.unique(clases):
m = clases == lab
ax.scatter(X[m, 0], X[m, 1], s=18, alpha=0.7, label=f"Clase {lab}")
ax.legend(fontsize=8)
if centro is not None:
ax.scatter(centro[0], centro[1], s=80, marker="x", zorder=3, label="centro")
ax.set_aspect("equal", adjustable="box")
ax.set_xlabel(ejes[0])
ax.set_ylabel(ejes[1])
ax.set_title(titulo)
plt.tight_layout()
plt.show()
def graficar_nube_con_pcs(Xc, W, lambdas, titulo, nombres_ejes=("x₁ centrada", "x₂ centrada")):
fig, ax = plt.subplots(figsize=(6.8, 6.4))
ax.scatter(Xc[:, 0], Xc[:, 1], s=16, alpha=0.55)
escala = 2.4 * np.sqrt(lambdas)
for j, color in enumerate(["C3", "C2"]):
v = W[:, j] * escala[j]
ax.arrow(0, 0, v[0], v[1], head_width=0.12, head_length=0.12,
length_includes_head=True, color=color, linewidth=2,
label=f"PC{j+1} (λ={lambdas[j]:.2f})")
ax.arrow(0, 0, -v[0], -v[1], head_width=0.08, head_length=0.08,
length_includes_head=True, color=color, linewidth=1.2, alpha=0.7)
ax.axhline(0, color="0.6", linewidth=0.7)
ax.axvline(0, color="0.6", linewidth=0.7)
ax.set_aspect("equal", adjustable="box")
ax.set_xlabel(nombres_ejes[0])
ax.set_ylabel(nombres_ejes[1])
ax.set_title(titulo)
ax.legend(fontsize=8)
plt.tight_layout()
plt.show()
def graficar_scree(lambdas, titulo="Varianza por componente"):
p = len(lambdas)
ks = np.arange(1, p + 1)
pi = lambdas / lambdas.sum()
acum = np.cumsum(pi)
fig, axes = plt.subplots(1, 2, figsize=(11, 4.2), constrained_layout=True)
axes[0].bar(ks, lambdas)
axes[0].plot(ks, lambdas, marker="o")
axes[0].set_xticks(ks)
axes[0].set_xlabel("Componente k")
axes[0].set_ylabel("Autovalor λ_k")
axes[0].set_title("Scree")
axes[1].bar(ks, pi, alpha=0.7, label="π_k")
axes[1].plot(ks, acum, marker="o", color="C1", label="Π_k acumulada")
axes[1].axhline(0.9, linestyle="--", linewidth=1, color="0.4")
axes[1].set_xticks(ks)
axes[1].set_xlabel("Componente k")
axes[1].set_ylabel("Fracción de varianza")
axes[1].set_title("Varianza explicada")
axes[1].legend(fontsize=8)
fig.suptitle(titulo)
plt.show()
return pi, acum
def graficar_scores_1d(Z, titulo):
fig, axes = plt.subplots(1, 2, figsize=(11, 3.6), constrained_layout=True)
for j, ax in enumerate(axes):
ax.scatter(Z[:, j], np.zeros_like(Z[:, j]), s=16, alpha=0.55)
ax.axhline(0, color="0.6", linewidth=0.6)
ax.set_xlabel(f"PC{j+1}")
ax.set_yticks([])
ax.set_title(f"Proyección sobre PC{j+1} var = {Z[:, j].var(ddof=1):.3f}")
fig.suptitle(titulo)
plt.show()
def graficar_3d(X, titulo, vistas):
"""Nube 3D vista desde varias direcciones; cada vista es (dirección de la cámara, subtítulo)."""
fig = plt.figure(figsize=(6.0 * len(vistas), 5.4))
for i, (direccion, subtitulo) in enumerate(vistas, start=1):
d = np.asarray(direccion, dtype=float) / np.linalg.norm(direccion)
ax = fig.add_subplot(1, len(vistas), i, projection="3d")
ax.scatter(X[:, 0], X[:, 1], X[:, 2], s=10, alpha=0.5)
ax.view_init(elev=np.degrees(np.arcsin(d[2])), azim=np.degrees(np.arctan2(d[1], d[0])))
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_title(subtitulo, fontsize=10)
fig.suptitle(titulo)
plt.tight_layout()
plt.show()
def graficar_reconstruccion(X, Xhat, titulo):
fig, axes = plt.subplots(1, 2, figsize=(11, 4.8), constrained_layout=True)
axes[0].scatter(X[:, 0], X[:, 1], s=14, alpha=0.55)
axes[0].set_title("Original")
axes[1].scatter(Xhat[:, 0], Xhat[:, 1], s=14, alpha=0.55, color="C1")
axes[1].set_title("Reconstrucción")
for ax in axes:
ax.set_aspect("equal", adjustable="box")
ax.set_xlabel("x₁")
ax.set_ylabel("x₂")
fig.suptitle(titulo)
plt.show()
def graficar_mse_k(ks, mses, titulo):
fig, ax = plt.subplots(figsize=(7.2, 4.0))
ax.plot(ks, mses, marker="o")
ax.set_xticks(ks)
ax.set_xlabel("k componentes retenidos")
ax.set_ylabel("Error cuadrático medio de reconstrucción")
ax.set_title(titulo)
plt.tight_layout()
plt.show()
def graficar_cargas(W, nombres, titulo, k=2):
fig, axes = plt.subplots(1, k, figsize=(5.2 * k, 4.0), constrained_layout=True)
if k == 1:
axes = [axes]
for j, ax in enumerate(axes):
ax.barh(nombres, W[:, j])
ax.axvline(0, color="0.4", linewidth=0.8)
ax.set_title(f"Cargas PC{j+1}")
ax.set_xlabel("peso en el componente")
fig.suptitle(titulo)
plt.show()
def graficar_escala_lado_a_lado(X_a, X_b, titulo_a, titulo_b):
fig, axes = plt.subplots(1, 2, figsize=(11, 4.8), constrained_layout=True)
axes[0].scatter(X_a[:, 0], X_a[:, 1], s=14, alpha=0.55)
axes[0].set_title(titulo_a)
axes[1].scatter(X_b[:, 0], X_b[:, 1], s=14, alpha=0.55)
axes[1].set_title(titulo_b)
for ax in axes:
ax.set_xlabel("PC1")
ax.set_ylabel("PC2")
fig.suptitle("La misma nube, dos decisiones de escala")
plt.show()