Validasi pada Bagian 3 meninggalkan daftar masalah yang jelas: satu baris duplikat, satu sel harga kosong, dan satu nama komoditas yang tidak seragam. Bagian ini memperbaiki ketiganya dengan perlakuan yang berbeda-beda — bukan sekadar menghapus yang terlihat kotor — lalu memverifikasi ulang hasilnya sampai data layak dianalisis. Setiap keputusan pembersihan kita catat alasannya, karena cara data dibersihkan ikut menentukan batas tafsir hasil akhirnya.

Tiga Masalah, Tiga Perlakuan #

Tidak semua kotoran dibuang. Tiap masalah mendapat perlakuan yang paling sesuai dengan sebab kemunculannya:

MasalahPerlakuanAlasan
Baris duplikat M3 cabai merahBuang baris keduanyaBaris ganda tidak membawa informasi baru; justru memberi bobot ganda pada M3
Sel harga kosong M6 bawang merahIsi dengan interpolasi linearHarga bergerak per waktu; pekan yang hilang lebih dekat ke perilaku pekan tetangganya
Nama Cabai Merah pada M7Seragamkan ke huruf kecilKesalahan ketik kapitalisasi, bukan komoditas baru

Perlakuan pada sel kosong adalah keputusan yang paling perlu dipertanggungjawabkan, jadi kita bahas lebih dalam pada bagian khusus di bawah.

Membuang Duplikat dan Menyeragamkan Nama #

Dua langkah pertama berjalan bersama karena keduanya murah dan tidak berisiko:

python
import pandas as pd

df = pd.read_csv("../data/harga_komoditas.csv")

# Langkah 1: buang baris duplikat — satu pekan satu komoditas cukup satu baris.
bersih = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
print("Setelah buang duplikat :", bersih.shape)

# Langkah 2: seragamkan nama komoditas (huruf kecil, tanpa spasi tepi).
bersih["komoditas"] = bersih["komoditas"].str.strip().str.lower()
print("Nama komoditas unik     :", sorted(bersih["komoditas"].unique()))

Output yang Dihasilkan:

text
Setelah buang duplikat : (40, 3)
Nama komoditas unik     : ['ayam broiler', 'bawang merah', 'beras', 'cabai merah', 'gula pasir']

Dua keputusan pada blok di atas perlu dijelaskan:

  1. subset=["minggu", "komoditas"]: duplikat kita definisikan berdasarkan pasangan kunci pekan-komoditas, bukan kesamaan seluruh kolom. Dengan begitu, bila suatu saat ada dua baris M3 cabai dengan harga berbeda (salah ketik), keduanya tetap terdeteksi sebagai duplikat dan baris pertama yang dipertahankan (keep="first").
  2. .str.strip().str.lower(): spasi tepi dibuang dulu baru huruf dikecilkan. “Cabai Merah”, “cabai merah “, dan “CABAI MERAH” berakhir menjadi satu identitas yang sama.

Mengisi Sel Kosong dengan Interpolasi #

Sel harga M6 bawang merah berisi -. Kita punya tiga pilihan, dan pilihan ketiga yang kita ambil:

  1. Membuang barisnya — data bawang tinggal 7 pekan; perbandingan antar komoditas jadi timpang.
  2. Mengisi dengan rata-rata komoditas (32.94) — mengabaikan bahwa harga bergerak mengikuti waktu; bawang sedang menurun saat itu, jadi rata-rata justru terasa terlalu tinggi.
  3. Interpolasi linear antar pekan berdekatan — M6 diisi nilai tengah antara M5 (33.0) dan M7 (32.0), yaitu 32.5; menghormati urutan waktu tanpa mengarang tren baru.
python
import pandas as pd

df = pd.read_csv("../data/harga_komoditas.csv")
bersih = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
bersih["komoditas"] = bersih["komoditas"].str.strip().str.lower()

# Langkah 3: '-' menjadi NaN, lalu kolom harga menjadi numerik.
bersih["harga_rata_ribu"] = pd.to_numeric(bersih["harga_rata_ribu"], errors="coerce")
print(f"{'Sel kosong sesudah konversi':<32}:", bersih["harga_rata_ribu"].isna().sum())

# Langkah 4: isi sel kosong dengan interpolasi linear antar pekan berdekatan.
bersih = bersih.sort_values(["komoditas", "minggu"])
bersih["harga_rata_ribu"] = bersih.groupby("komoditas")["harga_rata_ribu"].transform(
    lambda s: s.interpolate()
)
print(f"{'Sel kosong sesudah interpolasi':<32}:", bersih["harga_rata_ribu"].isna().sum())
print()
bawang = bersih[bersih["komoditas"] == "bawang merah"]
print(bawang[["minggu", "harga_rata_ribu"]].to_string(index=False))

Output yang Dihasilkan:

text
Sel kosong sesudah konversi     : 1
Sel kosong sesudah interpolasi  : 0

minggu  harga_rata_ribu
    M1             30.0
    M2             34.0
    M3             31.0
    M4             36.0
    M5             33.0
    M6             32.5
    M7             32.0
    M8             35.0

Mari kita telaah langkah-langkah kuncinya:

  1. pd.to_numeric(..., errors="coerce"): tanda - yang gagal dikonversi berubah menjadi NaN — cara pandas menyatakan “tidak ada data” — dan sisanya menjadi angka. Kolom harga pun berubah tipe menjadi numerik.
  2. sort_values(["komoditas", "minggu"]) sebelum interpolasi: interpolasi linear mengisi NaN dari nilai di kiri-kanannya berdasarkan posisi baris. Tanpa pengurutan ini, “pekan tetangga” bisa berarti baris yang salah. M1–M8 berurut abjad dengan benar, jadi pengurutan tunggal ini sudah cukup.
  3. groupby("komoditas")...transform(lambda s: s.interpolate()): interpolasi dijalankan terpisah per komoditas, sehingga NaN bawang M6 hanya dipengaruhi harga bawang — bukan harga komoditas lain.
  4. Hasilnya 32.5: persis titik tengah antara 33.0 dan 32.0, dan tabel bawang kini utuh 8 pekan.

Memverifikasi Ulang Hasil #

Pembersihan belum selesai sebelum buktinya diperiksa. Blok berikut menjalankan resep yang sama secara diam, lalu mengulang seluruh pemeriksaan Bagian 3:

python
import pandas as pd

# Resep pembersihan lengkap Bagian 4, dijalankan ulang dalam diam.
df = pd.read_csv("../data/harga_komoditas.csv")
bersih = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
bersih["komoditas"] = bersih["komoditas"].str.strip().str.lower()
bersih["harga_rata_ribu"] = pd.to_numeric(bersih["harga_rata_ribu"], errors="coerce")
bersih = bersih.sort_values(["komoditas", "minggu"])
bersih["harga_rata_ribu"] = bersih.groupby("komoditas")["harga_rata_ribu"].transform(
    lambda s: s.interpolate()
)

print(f"{'Dimensi akhir':<16}:", bersih.shape)
print(f"{'Baris duplikat':<16}:", bersih.duplicated().sum())
print(f"{'Sel kosong':<16}:", bersih["harga_rata_ribu"].isna().sum())
print(f"{'Tipe kolom harga':<16}:", bersih["harga_rata_ribu"].dtype)
print()
print(bersih["komoditas"].value_counts().sort_index())

Output yang Dihasilkan:

text
Dimensi akhir   : (40, 3)
Baris duplikat  : 0
Sel kosong      : 0
Tipe kolom harga: float64

komoditas
ayam broiler    8
bawang merah    8
beras           8
cabai merah     8
gula pasir      8
Name: count, dtype: int64

Kelima pemeriksaan kini hijau: 40 baris tepat, tanpa duplikat, tanpa sel kosong, kolom harga bertipe float64, dan lima komoditas masing-masing tepat 8 pekan. Data bersih ini — dan resep lima baris pembersihannya — akan dipakai berulang kali pada Bagian 5 sampai 9.

Ringkasan Bagian 4 #

Tiga masalah diperbaiki dengan tiga perlakuan: duplikat dibuang, sel kosong diisi interpolasi linear (32.5 untuk bawang M6, dengan catatan bahwa nilai itu adalah imputasi), dan nama komoditas diseragamkan. Verifikasi ulang menutup bagian ini dengan data 40 baris yang rapi.

Dengan data yang sudah bisa dipercaya, saatnya bertanya pertanyaan pertama dari Bagian 1: bagaimana harga bergerak dari pekan ke pekan, dan di mana perubahannya paling keras? Kita kejar pada Bagian 5: Eksplorasi Perubahan Harga .