TA/web_JMW/docs/append_k_determination_expl...

207 lines
7.8 KiB
Python

from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
import tempfile
import xml.etree.ElementTree as ET
SOURCE_DOC = Path("docs/Penjelasan_Rinci_KMeans_Rekomendasi_JMW_Gabungan_Update.docx")
OUT_DOC = Path("docs/Penjelasan_Rinci_KMeans_Rekomendasi_JMW_Gabungan_Update_K.docx")
WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
ET.register_namespace("w", WORD_NS)
def qn(name):
return f"{{{WORD_NS}}}{name}"
def paragraph(text="", style=None):
p = ET.Element(qn("p"))
if style:
p_pr = ET.SubElement(p, qn("pPr"))
p_style = ET.SubElement(p_pr, qn("pStyle"))
p_style.set(qn("val"), style)
run = ET.SubElement(p, qn("r"))
t = ET.SubElement(run, qn("t"))
t.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
t.text = text
return p
def code_paragraph(text):
p = ET.Element(qn("p"))
p_pr = ET.SubElement(p, qn("pPr"))
p_style = ET.SubElement(p_pr, qn("pStyle"))
p_style.set(qn("val"), "Code")
run = ET.SubElement(p, qn("r"))
t = ET.SubElement(run, qn("t"))
t.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
t.text = text
return p
def page_break():
p = ET.Element(qn("p"))
r = ET.SubElement(p, qn("r"))
br = ET.SubElement(r, qn("br"))
br.set(qn("type"), "page")
return p
def code_block(text):
return [code_paragraph(line) for line in text.strip("\n").splitlines()]
def new_section():
items = []
items.append(page_break())
items.append(paragraph("18. Cara Menentukan Nilai k pada K-Means", "Heading1"))
items.append(paragraph(
"Nilai k pada K-Means menunjukkan jumlah cluster atau jumlah kelompok yang akan dibentuk. Dalam sistem "
"rekomendasi mobil JMW Showroom, nilai k digunakan untuk membagi data mobil ke dalam beberapa kelompok "
"berdasarkan kemiripan harga, tahun, dan mesin_cc."
))
items.append(paragraph("18.1 Pengertian Nilai k", "Heading2"))
items.append(paragraph(
"Jika k bernilai 4, maka sistem akan membentuk empat kelompok mobil. Setiap mobil akan masuk ke salah satu "
"cluster berdasarkan jarak terdekat terhadap centroid. Semakin tepat nilai k yang digunakan, semakin baik pula "
"pembagian kelompok mobil yang dihasilkan."
))
items.append(paragraph("18.2 Nilai k pada Implementasi Sistem", "Heading2"))
items.append(paragraph(
"Pada implementasi sistem, nilai k yang digunakan adalah 4. Nilai ini dikirim oleh Laravel ke Flask API melalui "
"parameter jumlah_cluster. Dengan demikian, ketika fitur rekomendasi budget dijalankan, Flask API akan membuat "
"model K-Means dengan empat cluster."
))
items.extend(code_block("""
'filter' => [
'toleransi_harga_percent' => 10,
'jumlah_cluster' => 4,
],
"""))
items.append(paragraph(
"Parameter tersebut diterima oleh Flask API dan digunakan pada fungsi apply_kmeans_budget_filter(). Jika "
"parameter jumlah_cluster tersedia, maka nilai tersebut dipakai sebagai jumlah cluster. Jika tidak tersedia, "
"sistem menggunakan nilai default 4."
))
items.extend(code_block("""
model = build_kmeans_model(
cars,
cluster_count=to_int(filters.get("jumlah_cluster"), default=4)
)
"""))
items.append(paragraph("18.3 Pengujian Nilai k Menggunakan Silhouette Score", "Heading2"))
items.append(paragraph(
"Agar nilai k tidak dipilih secara sembarang, jumlah cluster dapat diuji menggunakan silhouette score. "
"Silhouette score digunakan untuk mengukur seberapa baik data berada di dalam cluster-nya dan seberapa jauh "
"data tersebut dari cluster lain. Nilai silhouette yang lebih tinggi menunjukkan hasil cluster yang lebih baik."
))
items.extend(code_block("""
for k in range(2, 7):
kmeans = KMeans(
n_clusters=k,
random_state=42,
n_init=10
)
labels = kmeans.fit_predict(fitur_scaled)
score = silhouette_score(fitur_scaled, labels)
print(f"Jumlah Cluster: {k}, Silhouette Score: {score}")
"""))
items.append(paragraph(
"Kode tersebut mencoba beberapa nilai k, yaitu 2 sampai 6. Setiap nilai k diuji dengan K-Means, kemudian "
"hasil cluster-nya dievaluasi menggunakan silhouette score. Dari hasil tersebut, peneliti dapat melihat nilai "
"k mana yang memberikan kualitas cluster terbaik."
))
items.append(paragraph("18.4 Contoh Hasil Pengujian", "Heading2"))
items.append(paragraph(
"Misalnya hasil pengujian silhouette score menghasilkan nilai sebagai berikut:"
))
items.extend(code_block("""
Jumlah Cluster: 2, Silhouette Score: 0,2281
Jumlah Cluster: 3, Silhouette Score: 0,2345
Jumlah Cluster: 4, Silhouette Score: 0,2294
Jumlah Cluster: 5, Silhouette Score: 0,2394
Jumlah Cluster: 6, Silhouette Score: 0,2570
"""))
items.append(paragraph(
"Dari contoh tersebut, k = 6 memiliki nilai silhouette score tertinggi. Namun pada implementasi sistem, k = 4 "
"digunakan karena sistem ingin membagi mobil ke dalam empat kelompok utama yang lebih mudah diterapkan pada "
"fitur rekomendasi budget, misalnya kelompok mobil ekonomis, menengah, tinggi, dan premium. Pemilihan k = 4 "
"juga disesuaikan dengan konfigurasi yang sudah digunakan pada Flask API."
))
items.append(paragraph("18.5 Alasan Sistem Menggunakan k = 4", "Heading2"))
items.append(paragraph(
"Nilai k = 4 digunakan karena pembagian empat cluster dianggap cukup untuk merepresentasikan kelompok mobil "
"berdasarkan harga, tahun, dan mesin_cc. Dengan empat cluster, sistem dapat memisahkan mobil ke dalam beberapa "
"kelompok karakteristik tanpa membuat pembagian terlalu banyak dan sulit diinterpretasikan."
))
items.append(paragraph(
"Secara sederhana, empat cluster dapat diinterpretasikan sebagai kelompok mobil dengan karakteristik berikut: "
"cluster harga rendah atau ekonomis, cluster harga menengah, cluster harga tinggi, dan cluster premium atau "
"spesifikasi lebih tinggi. Nama atau karakteristik akhir setiap cluster tetap perlu dilihat dari rata-rata "
"data pada masing-masing cluster."
))
items.append(paragraph("18.6 Kalimat untuk Laporan", "Heading2"))
items.append(paragraph(
"Nilai k pada metode K-Means ditentukan dengan mempertimbangkan hasil pengujian jumlah cluster dan kebutuhan "
"implementasi sistem. Pada sistem ini, jumlah cluster yang digunakan adalah k = 4. Nilai tersebut digunakan "
"untuk membagi data mobil menjadi empat kelompok berdasarkan atribut harga, tahun, dan mesin_cc. Pemilihan "
"k = 4 juga disesuaikan dengan konfigurasi sistem rekomendasi pada Flask API melalui parameter "
"jumlah_cluster = 4."
))
return items
def append_section():
OUT_DOC.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory() as tmp:
tmp_dir = Path(tmp)
with ZipFile(SOURCE_DOC, "r") as archive:
archive.extractall(tmp_dir)
document_path = tmp_dir / "word" / "document.xml"
root = ET.parse(document_path).getroot()
body = root.find(qn("body"))
sect_pr = None
for child in list(body):
if child.tag == qn("sectPr"):
sect_pr = child
body.remove(child)
break
for item in new_section():
body.append(item)
if sect_pr is not None:
body.append(sect_pr)
ET.ElementTree(root).write(document_path, encoding="utf-8", xml_declaration=True)
if OUT_DOC.exists():
OUT_DOC.unlink()
with ZipFile(OUT_DOC, "w", ZIP_DEFLATED) as archive:
for path in tmp_dir.rglob("*"):
if path.is_file():
archive.write(path, path.relative_to(tmp_dir).as_posix())
print(OUT_DOC.resolve())
if __name__ == "__main__":
append_section()