184 lines
7.9 KiB
Python
184 lines
7.9 KiB
Python
from pathlib import Path
|
|
from zipfile import ZipFile, ZIP_DEFLATED
|
|
import html
|
|
import shutil
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
BASE_DIR = Path(__file__).resolve().parent
|
|
INPUT_DOCX = BASE_DIR / "Penjelasan_Rinci_KMeans_Rekomendasi_JMW_Gabungan_Update.docx"
|
|
OUTPUT_DOCX = BASE_DIR / "Penjelasan_Rinci_KMeans_Rekomendasi_JMW_Gabungan_Update_Centroid.docx"
|
|
|
|
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
ET.register_namespace("w", W_NS)
|
|
|
|
|
|
def qn(tag):
|
|
return f"{{{W_NS}}}{tag}"
|
|
|
|
|
|
def paragraph(text, style=None, bold=False):
|
|
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)
|
|
r = ET.SubElement(p, qn("r"))
|
|
if bold:
|
|
r_pr = ET.SubElement(r, qn("rPr"))
|
|
ET.SubElement(r_pr, qn("b"))
|
|
t = ET.SubElement(r, qn("t"))
|
|
t.text = text
|
|
return p
|
|
|
|
|
|
def table(rows):
|
|
tbl = ET.Element(qn("tbl"))
|
|
tbl_pr = ET.SubElement(tbl, qn("tblPr"))
|
|
tbl_style = ET.SubElement(tbl_pr, qn("tblStyle"))
|
|
tbl_style.set(qn("val"), "TableGrid")
|
|
tbl_w = ET.SubElement(tbl_pr, qn("tblW"))
|
|
tbl_w.set(qn("w"), "0")
|
|
tbl_w.set(qn("type"), "auto")
|
|
|
|
for row_index, row in enumerate(rows):
|
|
tr = ET.SubElement(tbl, qn("tr"))
|
|
for cell in row:
|
|
tc = ET.SubElement(tr, qn("tc"))
|
|
tc_pr = ET.SubElement(tc, qn("tcPr"))
|
|
tc_w = ET.SubElement(tc_pr, qn("tcW"))
|
|
tc_w.set(qn("w"), "2400")
|
|
tc_w.set(qn("type"), "dxa")
|
|
tc.append(paragraph(str(cell), bold=(row_index == 0)))
|
|
return tbl
|
|
|
|
|
|
def append_to_docx():
|
|
if not INPUT_DOCX.exists():
|
|
raise FileNotFoundError(f"Dokumen sumber tidak ditemukan: {INPUT_DOCX}")
|
|
|
|
shutil.copyfile(INPUT_DOCX, OUTPUT_DOCX)
|
|
|
|
with ZipFile(INPUT_DOCX, "r") as zin:
|
|
document_xml = zin.read("word/document.xml")
|
|
files = {name: zin.read(name) for name in zin.namelist() if name != "word/document.xml"}
|
|
|
|
root = ET.fromstring(document_xml)
|
|
body = root.find(qn("body"))
|
|
sect_pr = body.find(qn("sectPr"))
|
|
insert_index = list(body).index(sect_pr) if sect_pr is not None else len(list(body))
|
|
|
|
content = []
|
|
content.append(paragraph("18. Perhitungan Manual Centroid Awal Sesuai Sistem", style="Heading1"))
|
|
content.append(paragraph(
|
|
"Pada sistem rekomendasi mobil JMW, centroid awal tidak dipilih secara acak. "
|
|
"Kode pada Flask API terlebih dahulu mengurutkan data mobil berdasarkan harga, "
|
|
"kemudian memilih beberapa data yang posisinya tersebar dari harga terendah sampai harga tertinggi. "
|
|
"Data yang dipilih tersebut dijadikan centroid awal dalam bentuk vector normalisasi."
|
|
))
|
|
content.append(paragraph(
|
|
"Atribut yang digunakan dalam proses clustering pada sistem adalah harga, tahun, dan mesin_cc. "
|
|
"Sebelum menjadi centroid, setiap nilai atribut dinormalisasi terlebih dahulu menggunakan Min-Max Normalization "
|
|
"agar semua atribut berada pada rentang 0 sampai 1."
|
|
))
|
|
content.append(paragraph("Rumus normalisasi yang digunakan:", bold=True))
|
|
content.append(paragraph("X' = (X - nilai minimum) / (nilai maksimum - nilai minimum)"))
|
|
content.append(paragraph("Rumus pemilihan posisi centroid awal pada sistem:", bold=True))
|
|
content.append(paragraph("posisi = round(cluster_index * (jumlah_data - 1) / (jumlah_cluster - 1))"))
|
|
content.append(paragraph(
|
|
"Contoh berikut menggunakan jumlah cluster k=4 sesuai nilai yang saat ini dikirim oleh Laravel ke Flask API. "
|
|
"Jika sistem diubah menjadi k=6, rumusnya tetap sama, tetapi centroid awal yang dipilih berjumlah 6 data."
|
|
))
|
|
|
|
content.append(paragraph("Contoh data mobil yang sudah layak dipakai clustering:", bold=True))
|
|
raw_rows = [
|
|
["Mobil", "Harga", "Tahun", "Mesin CC"],
|
|
["Hyundai Ioniq", "122.000.000", "2021", "1197"],
|
|
["Toyota Avanza", "125.000.000", "2019", "1500"],
|
|
["Toyota Innova", "250.000.000", "2020", "2000"],
|
|
["Toyota Fortuner", "500.000.000", "2021", "2500"],
|
|
]
|
|
content.append(table(raw_rows))
|
|
|
|
content.append(paragraph(
|
|
"Dari contoh data tersebut, nilai minimum dan maksimum setiap atribut adalah: "
|
|
"harga minimum 122.000.000 dan harga maksimum 500.000.000; tahun minimum 2019 dan tahun maksimum 2021; "
|
|
"mesin minimum 1197 cc dan mesin maksimum 2500 cc."
|
|
))
|
|
|
|
content.append(paragraph("Hasil normalisasi setiap data:", bold=True))
|
|
norm_rows = [
|
|
["Mobil", "Harga Normalisasi", "Tahun Normalisasi", "Mesin Normalisasi", "Vector Normalisasi"],
|
|
["Hyundai Ioniq", "0,000", "1,000", "0,000", "[0,000; 1,000; 0,000]"],
|
|
["Toyota Avanza", "0,008", "0,000", "0,233", "[0,008; 0,000; 0,233]"],
|
|
["Toyota Innova", "0,339", "0,500", "0,616", "[0,339; 0,500; 0,616]"],
|
|
["Toyota Fortuner", "1,000", "1,000", "1,000", "[1,000; 1,000; 1,000]"],
|
|
]
|
|
content.append(table(norm_rows))
|
|
|
|
content.append(paragraph("Contoh perhitungan normalisasi:", bold=True))
|
|
content.append(paragraph(
|
|
"Harga Toyota Avanza = (125.000.000 - 122.000.000) / (500.000.000 - 122.000.000) "
|
|
"= 3.000.000 / 378.000.000 = 0,008."
|
|
))
|
|
content.append(paragraph(
|
|
"Tahun Toyota Avanza = (2019 - 2019) / (2021 - 2019) = 0 / 2 = 0,000."
|
|
))
|
|
content.append(paragraph(
|
|
"Mesin Toyota Avanza = (1500 - 1197) / (2500 - 1197) = 303 / 1303 = 0,233."
|
|
))
|
|
content.append(paragraph(
|
|
"Maka vector normalisasi Toyota Avanza adalah [0,008; 0,000; 0,233]. "
|
|
"Vector inilah yang digunakan sistem untuk menghitung jarak ke centroid."
|
|
))
|
|
|
|
content.append(paragraph("Perhitungan posisi centroid awal untuk k=4:", bold=True))
|
|
content.append(paragraph("Jumlah data = 4, jumlah cluster = 4."))
|
|
centroid_rows = [
|
|
["Cluster", "cluster_index", "Perhitungan Posisi", "Posisi", "Centroid Awal"],
|
|
["C1", "0", "round(0 * (4 - 1) / (4 - 1))", "0", "Hyundai Ioniq"],
|
|
["C2", "1", "round(1 * (4 - 1) / (4 - 1))", "1", "Toyota Avanza"],
|
|
["C3", "2", "round(2 * (4 - 1) / (4 - 1))", "2", "Toyota Innova"],
|
|
["C4", "3", "round(3 * (4 - 1) / (4 - 1))", "3", "Toyota Fortuner"],
|
|
]
|
|
content.append(table(centroid_rows))
|
|
|
|
content.append(paragraph(
|
|
"Berdasarkan perhitungan tersebut, centroid awal yang digunakan adalah vector normalisasi dari data pada posisi 0, 1, 2, dan 3."
|
|
))
|
|
final_rows = [
|
|
["Centroid", "Vector Centroid Awal"],
|
|
["C1", "[0,000; 1,000; 0,000]"],
|
|
["C2", "[0,008; 0,000; 0,233]"],
|
|
["C3", "[0,339; 0,500; 0,616]"],
|
|
["C4", "[1,000; 1,000; 1,000]"],
|
|
]
|
|
content.append(table(final_rows))
|
|
|
|
content.append(paragraph(
|
|
"Setelah centroid awal terbentuk, sistem menghitung jarak setiap mobil ke semua centroid menggunakan Euclidean Distance. "
|
|
"Mobil akan masuk ke cluster yang memiliki jarak paling dekat. Setelah semua data masuk ke cluster, sistem menghitung ulang centroid baru "
|
|
"berdasarkan rata-rata vector anggota cluster. Proses ini diulang sampai jumlah iterasi selesai atau posisi cluster tidak banyak berubah."
|
|
))
|
|
content.append(paragraph(
|
|
"Dengan demikian, centroid awal pada sistem bukan angka yang dibuat manual oleh pengguna, tetapi berasal dari data mobil yang sudah dinormalisasi. "
|
|
"Pemilihan awalnya mengikuti urutan harga agar centroid mewakili sebaran harga dari rendah sampai tinggi, lalu atribut tahun dan mesin tetap ikut mempengaruhi "
|
|
"jarak dan pembentukan cluster."
|
|
))
|
|
|
|
for offset, element in enumerate(content):
|
|
body.insert(insert_index + offset, element)
|
|
|
|
new_xml = ET.tostring(root, encoding="utf-8", xml_declaration=True)
|
|
|
|
with ZipFile(OUTPUT_DOCX, "w", ZIP_DEFLATED) as zout:
|
|
for name, data in files.items():
|
|
zout.writestr(name, data)
|
|
zout.writestr("word/document.xml", new_xml)
|
|
|
|
print(OUTPUT_DOCX)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
append_to_docx()
|