Bagian 1 berakhir dengan enam kelompok masalah pada harga_komoditas.csv: delapan baris duplikat, sepuluh harga kosong, enam harga ekstrem, 12 bentuk penulisan kota, 8 bentuk penulisan satuan, dan 45 kombinasi tanggal-komoditas yang hilang. Data dalam keadaan seperti ini belum boleh dipakai menghitung perubahan harga, karena satu harga ganda saja sudah mendistorsi persentase kenaikan. Pada bagian ini kita membersihkan masalah-masalah itu dalam urutan langkah yang alasannya jelas, memantau efek tiap langkah, lalu mengemas prosesnya menjadi fungsi bersihkan() yang akan dipanggil Bagian 3 dan Bagian 4. Pendekatan ini merupakan penerapan langsung materi section Data Cleaning pada data harga.
Urutan Langkah Pembersihan #
Urutan langkah bukan pengaturan sewenang-wenang; setiap langkah membuat langkah berikutnya lebih sederhana.
drop_duplicates()menghapus delapan salinan persis. Harga satu komoditas di satu kota pada satu tanggal hanyalah satu kejadian pasar, bukan dua.str.strip()ditambah.str.title()dan.str.lower()menyeragamkan label:JAKARTA,jakarta, danJakartamenjadiJakarta, sedangkanKGdankgmenjadikg— materi Standardisasi Label Kategori.pd.to_datetime()mengubah kolomtanggaldaristrmenjadidatetime64[us]; pandas 3.0 memakai resolusi mikrodetik, bukan nanodetik.sort_values("tanggal")mengurutkan baris berdasarkan waktu, syarat wajib sebelum mengisi nilai kosong secara berurutan.- Pemotongan harga salah catat: harga di atas dua kali median harian antar kota dipotong ke median tersebut, teknik winsorization dari chapter Winsorization dan Clipping.
- Pengisian harga kosong:
ffill()lalubfill()di dalam setiap pasangan komoditas-kota mengisi sepuluh sel kosong dari harga terakhir yang tersedia — materi Forward Fill dan Backward Fill.
Menjalankan Langkah dengan Jejak Perubahan #
Skrip berikut menulis ulang dataset mentah dari Bagian 1, menjalankan keenam langkah sambil mencetak efek masing-masing, lalu membandingkan hasilnya dengan fungsi bersihkan() yang tersedia pada buat_dataset.py di folder yang sama.
"""Membersihkan harga_komoditas.csv langkah demi langkah dengan jejak perubahan."""
from pathlib import Path
import tempfile
import pandas as pd
from pandas.testing import assert_frame_equal
from buat_dataset import bersihkan, buat_dataset
if __name__ == "__main__":
with tempfile.TemporaryDirectory() as folder:
berkas = Path(folder) / "harga_komoditas.csv"
buat_dataset().to_csv(berkas, index=False)
mentah = pd.read_csv(berkas)
print(f"baris mentah : {len(mentah)}")
df = mentah.drop_duplicates().reset_index(drop=True)
print(f"setelah drop_duplicates : {len(df)} baris ({df.shape[1]} kolom)")
df["kota"] = df["kota"].str.strip().str.title()
df["satuan"] = df["satuan"].str.strip().str.lower()
print(f"label kota : {df['kota'].nunique()} nilai")
print(f"label satuan : {df['satuan'].nunique()} nilai")
df["tanggal"] = pd.to_datetime(df["tanggal"])
print(f"dtype tanggal : {df['tanggal'].dtype}")
df = df.sort_values("tanggal").reset_index(drop=True)
median_harian = df.groupby(["tanggal", "komoditas"])["harga"].transform("median")
salah_catat = df["harga"] > 2 * median_harian
df.loc[salah_catat, "harga"] = median_harian.loc[salah_catat]
print(f"harga salah catat dipotong: {salah_catat.sum()} baris")
grup = df.groupby(["komoditas", "kota"], sort=False)["harga"]
df["harga"] = grup.transform(lambda s: s.ffill().bfill())
df["harga"] = df["harga"].round().astype("int64")
print(f"harga kosong tersisa : {df['harga'].isna().sum()}")
print(f"hasil akhir : {df.shape[0]} baris x {df.shape[1]} kolom")
assert_frame_equal(df, bersihkan(mentah))
print("validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identik")Output yang Dihasilkan:
baris mentah : 1313
setelah drop_duplicates : 1305 baris (5 kolom)
label kota : 3 nilai
label satuan : 2 nilai
dtype tanggal : datetime64[us]
harga salah catat dipotong: 6 baris
harga kosong tersisa : 0
hasil akhir : 1305 baris x 5 kolom
validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identikMari kita telaah bagian-bagian pentingnya:
drop_duplicates(): jumlah baris turun dari 1313 menjadi 1305, persis delapan duplikat dari daftar Bagian 1.- Standardisasi label:
nunique()kota turun dari 12 bentuk menjadi 3 nilai, dan satuan dari 8 bentuk menjadi 2 nilai; jumlah baris tidak berubah karena hanya teksnya yang dibetulkan. pd.to_datetime(): dtypetanggalberubah menjadidatetime64[us]; sejak titik ini kolom tanggal bisa diurutkan, dibandingkan, dan dijadikan index deret waktu.- Pemotongan salah catat:
transform("median")menghitung median harga tiga kota untuk tiap pasangan tanggal-komoditas, lalu enam baris yang melampaui dua kali lipatnya diganti dengan median tersebut. - Pengisian kosong:
groupby(["komoditas", "kota"]).transform(lambda s: s.ffill().bfill())mengisi tiap sel kosong dari harga sebelumnya pada grup yang sama;bfill()menangkap sel kosong di awal periode. Hasilnya 0 NaN, laluround().astype("int64")mengembalikan harga ke bilangan bulat rupiah. - Baris validasi:
assert_frame_equal(df, bersihkan(mentah))membandingkan hasil langkah manual dengan fungsibersihkan()dan menyatakan keduanya identik — pola gerbang mutu dari section Pengujian dan Debugging.
Memilih Strategi Outlier yang Sesuai Konteks #
Kenapa harga ekstrem tidak dipotong dengan rentang IQR per komoditas seperti pada chapter Outlier dengan Metode IQR? Karena kedua kejadian yang bercampur di data ini perlu dipisahkan. Lonjakan cabai pada Februari (yang akan kita analisis di Bagian 3) terjadi di ketiga kota sehari yang sama dan merupakan harga pasar yang nyata; rentang IQR yang dihitung dari seluruh periode justru akan memotong puncak yang sah itu. Sebaliknya, kesalahan ketik terjadi di satu kota saja pada satu hari, sehingga median harga dua kota lain pada hari yang sama menjadi acuan pengganti yang wajar. Chapter Memilih Strategi Penanganan Data pada section Missing Data dan Outlier Lanjutan mengajarkan pertimbangan seperti ini: teknik yang sama tidak selalu tepat untuk dua jenis anomali yang berbeda.
Dataset bersih 1305 baris kini konsisten: satu baris per tanggal-komoditas-kota, label seragam, tanpa NaN. Namun 45 kombinasi yang hilang tetap tidak ada barisnya — tidak ada harga untuk diisi. Bagian 3 menjembatani celah itu dengan reindex() dan interpolate(), lalu menjawab pertanyaan inti proyek: komoditas mana yang naik paling tajam, kapan, dan seberapa besar.
Sumber bacaan resmi: pandas.DataFrame.drop_duplicates , pandas.to_datetime , dan pandas.DataFrame.ffill .