306 lines
12 KiB
Python
306 lines
12 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.docx")
|
|
OUT_DOC = Path("docs/Penjelasan_Rinci_KMeans_Rekomendasi_JMW_Gabungan_Update.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("17. Penjelasan Kode Clustering dan Kegunaannya dalam Sistem", "Heading1"))
|
|
items.append(paragraph(
|
|
"Bagian ini menjelaskan fungsi setiap blok kode clustering K-Means dan hubungannya dengan fitur rekomendasi "
|
|
"mobil berdasarkan budget pada sistem JMW Showroom. Kode clustering digunakan untuk membentuk kelompok mobil "
|
|
"berdasarkan kemiripan harga, tahun, dan kapasitas mesin."
|
|
))
|
|
|
|
items.append(paragraph("17.1 Import Library", "Heading2"))
|
|
items.extend(code_block("""
|
|
import pandas as pd
|
|
|
|
from sklearn.preprocessing import StandardScaler
|
|
from sklearn.cluster import KMeans
|
|
from sklearn.metrics import silhouette_score
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini digunakan untuk memanggil library yang diperlukan. pandas dipakai untuk membaca dan mengolah dataset. "
|
|
"StandardScaler digunakan untuk menstandarkan nilai atribut agar skala harga, tahun, dan mesin_cc menjadi "
|
|
"seimbang. KMeans digunakan untuk melakukan proses clustering, sedangkan silhouette_score digunakan untuk "
|
|
"menilai kualitas pembentukan cluster."
|
|
))
|
|
items.append(paragraph(
|
|
"Kegunaannya dalam sistem adalah menyiapkan alat utama untuk mengolah data mobil sebelum data tersebut dipakai "
|
|
"sebagai dasar rekomendasi."
|
|
))
|
|
|
|
items.append(paragraph("17.2 Membaca Dataset", "Heading2"))
|
|
items.extend(code_block("""
|
|
df = pd.read_csv("dataset_final.csv")
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini membaca file dataset_final.csv yang berisi data mobil. Dataset tersebut memuat informasi seperti "
|
|
"merk, model, harga, tahun, mesin_cc, transmisi, bahan bakar, dan atribut lainnya."
|
|
))
|
|
items.append(paragraph(
|
|
"Dalam sistem, dataset ini menjadi sumber data utama untuk membentuk cluster. Jika data mobil berubah atau "
|
|
"ditambah, proses clustering dapat dijalankan kembali agar hasil rekomendasi mengikuti data terbaru."
|
|
))
|
|
|
|
items.append(paragraph("17.3 Menghapus Cluster Lama", "Heading2"))
|
|
items.extend(code_block("""
|
|
if "cluster" in df.columns:
|
|
df = df.drop(columns=["cluster"])
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini mengecek apakah dataset sudah memiliki kolom cluster. Jika ada, kolom tersebut dihapus terlebih "
|
|
"dahulu. Tujuannya agar proses clustering tidak memakai hasil lama, tetapi menghitung ulang cluster dari awal."
|
|
))
|
|
items.append(paragraph(
|
|
"Kegunaannya dalam sistem adalah menjaga agar hasil cluster selalu sesuai dengan data dan proses perhitungan "
|
|
"terbaru, bukan hasil clustering sebelumnya."
|
|
))
|
|
|
|
items.append(paragraph("17.4 Menyamakan Nama Kolom Harga", "Heading2"))
|
|
items.extend(code_block("""
|
|
if "Harga" in df.columns:
|
|
df = df.rename(columns={"Harga": "harga"})
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini menyamakan nama kolom Harga menjadi harga. Penyamaan nama kolom penting karena kode clustering "
|
|
"mengambil atribut dengan nama harga, tahun, dan mesin_cc."
|
|
))
|
|
items.append(paragraph(
|
|
"Dalam sistem, hal ini membantu mencegah error ketika dataset menggunakan nama kolom dengan huruf besar, "
|
|
"misalnya Harga, sedangkan kode membaca kolom harga."
|
|
))
|
|
|
|
items.append(paragraph("17.5 Memilih Tiga Atribut Utama", "Heading2"))
|
|
items.extend(code_block("""
|
|
fitur = df[
|
|
[
|
|
"harga",
|
|
"tahun",
|
|
"mesin_cc"
|
|
]
|
|
]
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini memilih tiga atribut yang digunakan untuk clustering, yaitu harga, tahun, dan mesin_cc. Ketiga atribut "
|
|
"tersebut dipakai karena berhubungan langsung dengan rekomendasi mobil bekas. Harga menentukan kesesuaian "
|
|
"dengan budget, tahun menunjukkan usia kendaraan, dan mesin_cc menunjukkan kapasitas mesin."
|
|
))
|
|
items.append(paragraph(
|
|
"Kegunaannya dalam sistem adalah menentukan dasar pengelompokan mobil. Mobil dengan harga, tahun, dan mesin "
|
|
"yang mirip akan cenderung masuk ke cluster yang sama."
|
|
))
|
|
|
|
items.append(paragraph("17.6 Standarisasi Data", "Heading2"))
|
|
items.extend(code_block("""
|
|
scaler = StandardScaler()
|
|
fitur_scaled = scaler.fit_transform(fitur)
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini melakukan standarisasi data. Standarisasi diperlukan karena nilai harga sangat besar dibandingkan "
|
|
"tahun dan mesin_cc. Jika tidak distandarisasi, perhitungan jarak pada K-Means akan terlalu dipengaruhi oleh "
|
|
"harga, sehingga atribut tahun dan mesin_cc menjadi kurang berpengaruh."
|
|
))
|
|
items.append(paragraph(
|
|
"Dalam sistem, standarisasi membuat proses clustering lebih adil karena semua atribut berada pada skala yang "
|
|
"seimbang sebelum dihitung jaraknya."
|
|
))
|
|
|
|
items.append(paragraph("17.7 Mencari Jumlah Cluster Terbaik", "Heading2"))
|
|
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 ini mencoba beberapa jumlah cluster, mulai dari 2 sampai 6. Setiap nilai k diuji dengan K-Means, lalu "
|
|
"hasilnya dinilai menggunakan silhouette_score. Nilai silhouette digunakan untuk melihat seberapa baik data "
|
|
"terpisah ke dalam cluster."
|
|
))
|
|
items.append(paragraph(
|
|
"Kegunaannya dalam sistem adalah membantu menentukan jumlah cluster yang paling sesuai. Dengan cara ini, jumlah "
|
|
"cluster tidak dipilih secara asal, tetapi berdasarkan hasil evaluasi."
|
|
))
|
|
|
|
items.append(paragraph("17.8 Membuat Model K-Means Akhir", "Heading2"))
|
|
items.extend(code_block("""
|
|
kmeans = KMeans(
|
|
n_clusters=4,
|
|
random_state=42,
|
|
n_init=10
|
|
)
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini membuat model K-Means akhir dengan jumlah cluster sebanyak 4. Angka 4 disesuaikan dengan konfigurasi "
|
|
"pada sistem rekomendasi yang mengirim parameter jumlah_cluster = 4 ke Flask API."
|
|
))
|
|
items.append(paragraph(
|
|
"Dalam sistem, model ini berfungsi untuk membagi data mobil ke dalam empat kelompok. Setiap kelompok berisi "
|
|
"mobil yang memiliki karakteristik harga, tahun, dan mesin_cc yang relatif mirip."
|
|
))
|
|
|
|
items.append(paragraph("17.9 Menambahkan Hasil Cluster ke Dataset", "Heading2"))
|
|
items.extend(code_block("""
|
|
df["cluster"] = kmeans.fit_predict(fitur_scaled)
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini menjalankan K-Means pada data yang sudah distandarisasi, lalu menyimpan hasil cluster ke kolom baru "
|
|
"bernama cluster. Setiap mobil akan mendapatkan label cluster, misalnya 0, 1, 2, atau 3."
|
|
))
|
|
items.append(paragraph(
|
|
"Kegunaannya dalam sistem adalah memberikan identitas kelompok pada setiap mobil. Label cluster ini dapat "
|
|
"dipakai untuk memilih kandidat mobil yang paling sesuai dengan budget pengguna."
|
|
))
|
|
|
|
items.append(paragraph("17.10 Menampilkan dan Menyimpan Hasil", "Heading2"))
|
|
items.extend(code_block("""
|
|
print(
|
|
df[
|
|
[
|
|
"merk",
|
|
"model",
|
|
"harga",
|
|
"tahun",
|
|
"mesin_cc",
|
|
"cluster"
|
|
]
|
|
].head(20)
|
|
)
|
|
|
|
df.to_csv("dataset_final_clustered.csv", index=False)
|
|
"""))
|
|
items.append(paragraph(
|
|
"Kode ini menampilkan sebagian hasil clustering agar peneliti dapat melihat apakah setiap mobil sudah memiliki "
|
|
"label cluster. Setelah itu, dataset disimpan ke file baru bernama dataset_final_clustered.csv."
|
|
))
|
|
items.append(paragraph(
|
|
"Dalam sistem, file hasil clustering dapat digunakan sebagai dataset yang sudah siap dipakai untuk proses "
|
|
"rekomendasi. Data tersebut berisi informasi mobil beserta cluster-nya."
|
|
))
|
|
|
|
items.append(paragraph("17.11 Hubungan Kode dengan Fitur Rekomendasi Budget", "Heading2"))
|
|
items.append(paragraph(
|
|
"Secara keseluruhan, kode clustering ini berfungsi untuk mengelompokkan mobil sebelum proses rekomendasi "
|
|
"dilakukan. Ketika pengguna memasukkan budget, sistem dapat mencari kelompok mobil yang karakteristiknya paling "
|
|
"mendekati budget tersebut. Setelah cluster yang sesuai ditemukan, sistem menyaring mobil yang harganya tidak "
|
|
"melebihi budget pengguna dan mengurutkan hasil rekomendasi."
|
|
))
|
|
items.append(paragraph(
|
|
"Dengan demikian, K-Means tidak hanya menampilkan mobil berdasarkan harga terdekat, tetapi membantu sistem "
|
|
"mempertimbangkan kemiripan karakteristik kendaraan berdasarkan harga, tahun, dan kapasitas mesin."
|
|
))
|
|
|
|
items.append(paragraph("Kalimat Ringkas untuk Laporan", "Heading2"))
|
|
items.append(paragraph(
|
|
"Kode clustering digunakan untuk mengelompokkan data mobil berdasarkan atribut harga, tahun, dan mesin_cc. "
|
|
"Sebelum dilakukan clustering, data distandarisasi agar setiap atribut memiliki skala yang seimbang. Setelah "
|
|
"model K-Means dijalankan, setiap mobil memperoleh label cluster. Label cluster tersebut digunakan sistem "
|
|
"sebagai dasar dalam memilih kandidat rekomendasi mobil yang sesuai dengan budget pengguna."
|
|
))
|
|
|
|
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()
|