Spaces:
Sleeping
Sleeping
| 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 | |