TIF_E41253385/api_slearn/export_research_tfidf.py

681 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================================
# EXPORT DATA PENELITIAN TF-IDF UNTUK WEBSITE S-LEARN
# VERSI FINAL — TF-IDF TANPA NORMALISASI L2 (norm=None)
# ============================================================
from pathlib import Path
from collections import Counter
from datetime import datetime
import json
import shutil
import joblib
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
# ============================================================
# 1. PATH PROJECT
# ============================================================
BASE_DIR = Path(__file__).resolve().parent
PROJECT_DIR = BASE_DIR.parent
ARTIFACT_DIR = BASE_DIR / "artifacts_slearn"
VECTORIZER_PATH = (
ARTIFACT_DIR / "tfidf_vectorizer.joblib"
)
PUBLIC_DATA_DIR = (
PROJECT_DIR
/ "web_slearn"
/ "public"
/ "data"
)
CSV_PATH = (
PUBLIC_DATA_DIR
/ "hasil_preprocessing_slearn.csv"
)
OUTPUT_PATH = (
PUBLIC_DATA_DIR
/ "tfidf_research.json"
)
# ============================================================
# 2. CEK FILE YANG DIBUTUHKAN
# ============================================================
print("=" * 80)
print("EXPORT DATA TF-IDF PENELITIAN S-LEARN")
print("=" * 80)
if not VECTORIZER_PATH.exists():
raise FileNotFoundError(
f"Vectorizer tidak ditemukan:\n{VECTORIZER_PATH}"
)
if not CSV_PATH.exists():
raise FileNotFoundError(
f"CSV preprocessing tidak ditemukan:\n{CSV_PATH}"
)
PUBLIC_DATA_DIR.mkdir(
parents=True,
exist_ok=True
)
# ============================================================
# 3. MEMBACA DATA PREPROCESSING
# ============================================================
print("\nMembaca data preprocessing...")
df = pd.read_csv(
CSV_PATH,
sep=None,
engine="python",
encoding="utf-8-sig"
)
kolom_wajib = {
"komentar",
"label",
"stemmed_text"
}
kolom_hilang = kolom_wajib - set(df.columns)
if kolom_hilang:
raise ValueError(
"Kolom berikut tidak ditemukan pada CSV: "
+ ", ".join(sorted(kolom_hilang))
)
df["komentar"] = (
df["komentar"]
.fillna("")
.astype(str)
)
df["stemmed_text"] = (
df["stemmed_text"]
.fillna("")
.astype(str)
)
df["label"] = pd.to_numeric(
df["label"],
errors="raise"
).astype(int)
if not df["label"].isin([0, 1]).all():
raise ValueError(
"Label harus hanya berisi 0 atau 1."
)
if len(df) != 571:
raise ValueError(
f"Jumlah data seharusnya 571, "
f"tetapi ditemukan {len(df)}."
)
print(f"✓ Jumlah data: {len(df)}")
# ============================================================
# 4. MEMUAT TF-IDF VECTORIZER FINAL
# ============================================================
print("\nMemuat TF-IDF vectorizer final...")
tfidf = joblib.load(
VECTORIZER_PATH
)
norm_tfidf = tfidf.get_params().get("norm")
print(
f"Norm TF-IDF: {norm_tfidf}"
)
if norm_tfidf is not None:
raise ValueError(
"Vectorizer yang digunakan masih memakai "
f"normalisasi TF-IDF: {norm_tfidf}. "
"Vectorizer final seharusnya norm=None."
)
kata_unik = tfidf.get_feature_names_out()
print(
f"✓ Jumlah fitur TF-IDF: "
f"{len(kata_unik)}"
)
if len(kata_unik) != 675:
raise ValueError(
f"Jumlah fitur seharusnya 675, "
f"tetapi ditemukan {len(kata_unik)}."
)
# ============================================================
# 5. MEMBENTUK ULANG SPLIT 80:20
# SESUAI PENELITIAN
# ============================================================
print("\nMembentuk ulang split penelitian...")
semua_index = df.index.to_numpy()
train_index, test_index = train_test_split(
semua_index,
test_size=0.2,
random_state=42,
stratify=df["label"].to_numpy()
)
print(
f"✓ Data training: {len(train_index)}"
)
print(
f"✓ Data testing : {len(test_index)}"
)
if len(train_index) != 456:
raise ValueError(
"Jumlah training tidak sama dengan 456."
)
if len(test_index) != 115:
raise ValueError(
"Jumlah testing tidak sama dengan 115."
)
# ============================================================
# 6. TRANSFORM TF-IDF
# TIDAK MELAKUKAN FIT ULANG
# ============================================================
print(
"\nMengubah teks menjadi bobot TF-IDF "
"menggunakan vectorizer final..."
)
X_train_text = (
df.loc[train_index, "stemmed_text"]
.astype(str)
)
X_test_text = (
df.loc[test_index, "stemmed_text"]
.astype(str)
)
# HANYA TRANSFORM.
# Vectorizer sudah difit pada penelitian final.
X_train = tfidf.transform(
X_train_text
)
X_test = tfidf.transform(
X_test_text
)
print(
f"✓ Matriks training: {X_train.shape}"
)
print(
f"✓ Matriks testing : {X_test.shape}"
)
if X_train.shape != (456, 675):
raise ValueError(
"Ukuran matriks training tidak sesuai."
)
if X_test.shape != (115, 675):
raise ValueError(
"Ukuran matriks testing tidak sesuai."
)
# ============================================================
# 7. MENGHITUNG DF DARI DATA TRAINING
# ============================================================
print(
"\nMenghitung Document Frequency (DF) "
"dari data training..."
)
df_per_fitur = np.asarray(
(X_train > 0).sum(axis=0)
).ravel().astype(int)
idf_per_fitur = np.asarray(
tfidf.idf_
)
if len(df_per_fitur) != len(kata_unik):
raise ValueError(
"Jumlah DF tidak sama dengan jumlah fitur."
)
if len(idf_per_fitur) != len(kata_unik):
raise ValueError(
"Jumlah IDF tidak sama dengan jumlah fitur."
)
print("✓ DF dan IDF berhasil disiapkan.")
# ============================================================
# 8. MEMBUAT POSISI TRAINING DAN TESTING
# ============================================================
posisi_training = {
int(index): urutan
for urutan, index
in enumerate(
train_index,
start=1
)
}
posisi_testing = {
int(index): urutan
for urutan, index
in enumerate(
test_index,
start=1
)
}
# ============================================================
# 9. MEMBENTUK DETAIL TF-IDF PER DATA
# ============================================================
def buat_data_tfidf(
index_array,
matrix,
jenis_data
):
hasil = []
for posisi_matrix, index_asli in enumerate(
index_array
):
index_asli = int(index_asli)
baris_matrix = matrix.getrow(
posisi_matrix
)
stemmed_text = str(
df.at[
index_asli,
"stemmed_text"
]
).strip()
hitung_kata = Counter(
stemmed_text.split()
)
bobot_tfidf = []
for kolom, bobot in zip(
baris_matrix.indices,
baris_matrix.data
):
kata = str(
kata_unik[kolom]
)
tf = int(
hitung_kata.get(
kata,
0
)
)
df_kata = int(
df_per_fitur[kolom]
)
idf = float(
idf_per_fitur[kolom]
)
bobot = float(
bobot
)
bobot_tfidf.append({
"kata": kata,
"tf": tf,
"df": df_kata,
"idf": idf,
"bobot": bobot
})
# Bobot terbesar ditampilkan lebih dulu.
bobot_tfidf.sort(
key=lambda item: (
-item["bobot"],
item["kata"]
)
)
if jenis_data == "Training":
urutan_split = posisi_training[
index_asli
]
else:
urutan_split = posisi_testing[
index_asli
]
hasil.append({
"no_data": index_asli + 1,
"ulasan_asli": str(
df.at[
index_asli,
"komentar"
]
),
"stemmed_text": stemmed_text,
"label": int(
df.at[
index_asli,
"label"
]
),
"jenis_data": jenis_data,
"urutan_split": int(
urutan_split
),
"jumlah_fitur_nonzero": int(
baris_matrix.nnz
),
"bobot_tfidf": bobot_tfidf
})
return hasil
print(
"\nMenyusun detail TF-IDF "
"seluruh 571 data..."
)
data_training = buat_data_tfidf(
train_index,
X_train,
"Training"
)
data_testing = buat_data_tfidf(
test_index,
X_test,
"Testing"
)
data_semua = (
data_training
+ data_testing
)
# Agar website menampilkan
# Data ke-1 sampai Data ke-571.
data_semua.sort(
key=lambda item: item["no_data"]
)
if len(data_semua) != 571:
raise ValueError(
"Jumlah hasil TF-IDF bukan 571 data."
)
print(
"✓ Detail TF-IDF 571 data berhasil dibuat."
)
# ============================================================
# 10. MEMBENTUK JSON FINAL
# ============================================================
hasil_json = {
"summary": {
"jumlah_data": 571,
"jumlah_fitur": int(
len(kata_unik)
),
"data_training": int(
len(train_index)
),
"data_testing": int(
len(test_index)
),
"ukuran_matriks_training": [
int(X_train.shape[0]),
int(X_train.shape[1])
],
"ukuran_matriks_testing": [
int(X_test.shape[0]),
int(X_test.shape[1])
],
"test_size": 0.2,
"random_state": 42,
"stratify": "label",
"norm": None,
"smooth_idf": True,
"use_idf": True,
"sublinear_tf": False,
"keterangan": (
"TF-IDF dihitung tanpa normalisasi L2 "
"(norm=None). Bobot akhir merupakan "
"hasil TF × IDF."
)
},
"data": data_semua
}
# ============================================================
# 11. BACKUP JSON LAMA
# ============================================================
if OUTPUT_PATH.exists():
timestamp = datetime.now().strftime(
"%Y%m%d_%H%M%S"
)
backup_path = (
PUBLIC_DATA_DIR
/ f"tfidf_research_backup_{timestamp}.json"
)
shutil.copy2(
OUTPUT_PATH,
backup_path
)
print(
"\n✓ JSON lama dibackup:"
)
print(
f" {backup_path.name}"
)
# ============================================================
# 12. MENYIMPAN JSON BARU
# ============================================================
with open(
OUTPUT_PATH,
"w",
encoding="utf-8"
) as file:
json.dump(
hasil_json,
file,
ensure_ascii=False,
indent=2
)
print(
"\n✓ JSON TF-IDF baru berhasil disimpan:"
)
print(
f" {OUTPUT_PATH}"
)
# ============================================================
# 13. VERIFIKASI FILE HASIL
# ============================================================
with open(
OUTPUT_PATH,
"r",
encoding="utf-8"
) as file:
verifikasi = json.load(
file
)
if len(
verifikasi.get(
"data",
[]
)
) != 571:
raise ValueError(
"Verifikasi gagal: jumlah data JSON bukan 571."
)
summary_verifikasi = verifikasi.get(
"summary",
{}
)
if summary_verifikasi.get(
"jumlah_fitur"
) != 675:
raise ValueError(
"Verifikasi gagal: jumlah fitur bukan 675."
)
if summary_verifikasi.get(
"norm",
"SALAH"
) is not None:
raise ValueError(
"Verifikasi gagal: norm bukan None."
)
# ============================================================
# 14. MENCARI BOBOT TF-IDF TERBESAR
# ============================================================
bobot_terbesar = 0.0
kata_bobot_terbesar = "-"
data_bobot_terbesar = None
for item in data_semua:
for fitur in item["bobot_tfidf"]:
if fitur["bobot"] > bobot_terbesar:
bobot_terbesar = fitur["bobot"]
kata_bobot_terbesar = fitur["kata"]
data_bobot_terbesar = item["no_data"]
# ============================================================
# 15. OUTPUT FINAL
# ============================================================
print("\n" + "=" * 80)
print("EXPORT TF-IDF PENELITIAN BERHASIL")
print("=" * 80)
print(
f"Jumlah data : "
f"{len(data_semua)}"
)
print(
f"Data training : "
f"{len(train_index)}"
)
print(
f"Data testing : "
f"{len(test_index)}"
)
print(
f"Jumlah fitur : "
f"{len(kata_unik)}"
)
print(
f"Ukuran matriks training : "
f"{X_train.shape}"
)
print(
f"Ukuran matriks testing : "
f"{X_test.shape}"
)
print(
f"Normalisasi TF-IDF : "
f"{norm_tfidf}"
)
print(
"Bobot akhir : "
"TF × IDF"
)
print(
f"Bobot terbesar : "
f"{bobot_terbesar:.6f}"
)
print(
f"Kata bobot terbesar : "
f"{kata_bobot_terbesar}"
)
print(
f"Berada pada Data ke : "
f"{data_bobot_terbesar}"
)
print(
f"\nFile output:\n{OUTPUT_PATH}"
)
print(
"\n✓ TF-IDF website sekarang menggunakan "
"hasil penelitian TANPA normalisasi L2."
)