Buzzy2045
Deploy
541fa1f
Raw
History Blame Contribute Delete
4.31 kB
from __future__ import annotations
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import RocCurveDisplay, confusion_matrix
from .config import ID2LABEL, STOPWORDS
from .text import tokenize
def ensure_parent(path: str | Path) -> Path:
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
return path
def plot_confusion(y_true, y_pred, path: str | Path, *, title: str) -> Path:
path = ensure_parent(path)
cm = confusion_matrix(y_true, y_pred, labels=[0, 1])
plt.figure(figsize=(5.8, 4.8))
sns.heatmap(
cm,
annot=True,
fmt="d",
cmap="Blues",
xticklabels=[ID2LABEL[0], ID2LABEL[1]],
yticklabels=[ID2LABEL[0], ID2LABEL[1]],
)
plt.xlabel("Prediksi")
plt.ylabel("Aktual")
plt.title(title)
plt.tight_layout()
plt.savefig(path, dpi=180)
plt.close()
return path
def plot_roc_curve(y_true, y_score, path: str | Path, *, title: str) -> Path | None:
if y_score is None or len(np.unique(y_true)) < 2:
return None
path = ensure_parent(path)
plt.figure(figsize=(5.8, 4.8))
RocCurveDisplay.from_predictions(y_true, y_score, name="Positif")
plt.title(title)
plt.tight_layout()
plt.savefig(path, dpi=180)
plt.close()
return path
def plot_training_history(log_history: list[dict], path: str | Path, *, title: str) -> Path | None:
train = [
(item.get("epoch", item.get("step")), item["loss"])
for item in log_history
if "loss" in item and item.get("loss") is not None
]
evals = [
(item.get("epoch", item.get("step")), item["eval_loss"])
for item in log_history
if "eval_loss" in item and item.get("eval_loss") is not None
]
if not train and not evals:
return None
path = ensure_parent(path)
plt.figure(figsize=(7.4, 4.6))
if train:
xs, ys = zip(*train)
plt.plot(xs, ys, marker="o", label="train loss")
if evals:
xs, ys = zip(*evals)
plt.plot(xs, ys, marker="o", label="eval loss")
plt.xlabel("Epoch/step")
plt.ylabel("Loss")
plt.title(title)
plt.legend()
plt.tight_layout()
plt.savefig(path, dpi=180)
plt.close()
return path
def plot_top_words(top_words: pd.DataFrame, path: str | Path, *, title: str) -> Path | None:
if top_words.empty:
return None
path = ensure_parent(path)
frame = top_words.copy()
frame["signed_weight"] = np.where(
frame["label_name"].eq("Positif"),
frame["weight"].abs(),
-frame["weight"].abs(),
)
frame = pd.concat(
[
frame[frame["label_name"].eq("Negatif")].sort_values("signed_weight").head(20),
frame[frame["label_name"].eq("Positif")].sort_values("signed_weight", ascending=False).head(20),
]
)
plt.figure(figsize=(8, 8))
colors = frame["label_name"].map({"Negatif": "#c44536", "Positif": "#2f8f46"})
plt.barh(frame["term"], frame["signed_weight"], color=colors)
plt.axvline(0, color="#333333", linewidth=0.8)
plt.xlabel("Bobot terhadap kelas")
plt.title(title)
plt.tight_layout()
plt.savefig(path, dpi=180)
plt.close()
return path
def write_wordcloud(texts: list[str], path: str | Path, *, title: str | None = None) -> Path | None:
try:
from wordcloud import WordCloud
except ImportError:
return None
tokens: list[str] = []
for text in texts:
tokens.extend(tokenize(text, remove_stopwords=True, min_len=3))
tokens = [t for t in tokens if t not in STOPWORDS]
if not tokens:
return None
path = ensure_parent(path)
wordcloud = WordCloud(
width=1100,
height=650,
background_color="white",
colormap="viridis",
max_words=120,
collocations=True,
stopwords=STOPWORDS,
random_state=42,
).generate(" ".join(tokens))
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
if title:
plt.title(title)
plt.tight_layout(pad=0)
plt.savefig(path, dpi=180, bbox_inches="tight")
plt.close()
return path