Bagian 1 berakhir dengan daftar lima masalah pada penjualan.csv: enam baris duplikat, enam harga tidak valid, delapan kategori kosong, 37 tanggal berformat campur, dan 26 baris label kategori yang tidak seragam. Data dalam keadaan seperti ini belum boleh dipakai menghitung omzet, karena satu baris ganda saja sudah menggelembungkan hasil. Pada bagian ini kita membersihkan kelima masalah itu dalam satu urutan langkah yang alasannya jelas, memeriksa jumlah baris pada setiap langkah, lalu mengemas seluruh proses menjadi fungsi bersihkan() yang akan dipanggil Bagian 3 dan Bagian 4. Pendekatan langkah demi langkah ini merupakan penerapan langsung materi section Data Cleaning pada dataset proyek.

Urutan Langkah Pembersihan #

Urutan langkah bukan pengaturan sewenang-wenang; setiap langkah membuat langkah berikutnya lebih sederhana.

  1. drop_duplicates() menghapus enam salinan persis. Dua transaksi dengan id_transaksi sama hanyalah satu penjualan, sehingga penghitungan omzet tidak boleh menghitungnya dua kali.
  2. Filter harga > 0 membuang enam transaksi dengan harga 0 atau negatif. Nilai seperti ini tidak bisa “diperbaiki” ke harga sebenarnya tanpa informasi tambahan, maka barisnya dibuang — keputusan yang dibahas pada chapter Menangani Nilai Tidak Valid.
  3. pd.to_datetime(..., format="mixed") mengubah kolom tanggal dari str menjadi datetime64. Kolom ini memuat dua format sekaligus, 2025-01-15 dan 15/01/2025, sehingga butuh argumen format="mixed" yang mem-parse tiap elemen dengan formatnya masing-masing. Perilaku ini sejalan dengan chapter Memperbaiki Tipe Data.
  4. str.strip().str.title() menyeragamkan Bahan Pokok, BAHAN POKOK, dan bahan pokok menjadi Bahan Pokok, sesuai materi Standardisasi Label Kategori.
  5. fillna() dengan peta produk ke kategori mengisi delapan kategori kosong. Karena satu produk selalu berada di satu kategori, nilai yang benar bisa dikembalikan dari nama produknya — pola map() pada chapter Mengisi Nilai Kosong.

Langkah terakhir berupa reset_index(drop=True) merapikan index setelah dua langkah pembuangan baris, agar DataFrame hasil akhir kembali bernomor 0 sampai 233.

Implementasi Langkah demi Langkah #

Skrip berikut menulis ulang dataset mentah dari Bagian 1, menjalankan kelima langkah sambil mencetak efek masing-masing, lalu membandingkan hasilnya dengan fungsi bersihkan() yang tersedia pada buat_dataset.py.

python
"""Membersihkan penjualan.csv langkah demi langkah sampai siap analisis."""
from pathlib import Path
import tempfile

import pandas as pd
from pandas.testing import assert_frame_equal

from buat_dataset import PRODUK_KATEGORI, bersihkan, buat_dataset


if __name__ == "__main__":
    with tempfile.TemporaryDirectory() as folder:
        berkas = Path(folder) / "penjualan.csv"
        buat_dataset().to_csv(berkas, index=False)
        mentah = pd.read_csv(berkas)

    print(f"data mentah : {len(mentah)} baris, duplikat {mentah.duplicated().sum()}")

    df = mentah.drop_duplicates()
    print(f"1) drop_duplicates          -> {len(df)} baris")

    df = df[df["harga"] > 0]
    print(f"2) filter harga > 0         -> {len(df)} baris")

    df["tanggal"] = pd.to_datetime(df["tanggal"], format="mixed")
    print(
        f"3) to_datetime mixed        -> {df['tanggal'].dtype}, "
        f"NaT {df['tanggal'].isna().sum()}"
    )

    df["kategori"] = df["kategori"].str.strip().str.title()
    print(f"4) strip + title            -> {sorted(df['kategori'].dropna().unique())}")

    df["kategori"] = df["kategori"].fillna(df["produk"].map(PRODUK_KATEGORI))
    print(f"5) fillna dari peta produk  -> NaN kategori sisa {df['kategori'].isna().sum()}")

    df = df.reset_index(drop=True)
    print(
        f"hasil akhir: {df.shape[0]} baris x {df.shape[1]} kolom, "
        f"duplikat {df.duplicated().sum()}, NaN total {df.isna().sum().sum()}"
    )

    assert_frame_equal(df, bersihkan(mentah))
    print("validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identik")

Output yang Dihasilkan:

text
data mentah : 246 baris, duplikat 6
1) drop_duplicates          -> 240 baris
2) filter harga > 0         -> 234 baris
3) to_datetime mixed        -> datetime64[us], NaT 0
4) strip + title            -> ['Bahan Pokok', 'Kebersihan', 'Minuman', 'Perawatan']
5) fillna dari peta produk  -> NaN kategori sisa 0
hasil akhir: 234 baris x 7 kolom, duplikat 0, NaN total 0
validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identik

Beberapa baris perlu digarisbawahi:

  1. mentah = pd.read_csv(berkas): dataset 246 baris dimuat ulang dari CSV agar latihan realistis; berkas ditulis dan dihapus dalam TemporaryDirectory yang sama.
  2. df[df["harga"] > 0]: filtering boolean mengembalikan DataFrame baru; dengan Copy-on-Write yang selalu aktif di pandas 3.0, penugasan pada DataFrame hasil filter ini aman tanpa perlu khawatir memengaruhi data asal.
  3. format="mixed": hasil parse berupa datetime64[us] — resolusi mikrodetik adalah bawaan pandas 3.0 — dan pemeriksaan NaT menghasilkan 0, artinya seluruh 234 tanggal berhasil dikonversi. Tanggal berformat DD/MM/YYYY pada dataset ini sengaja hanya hari 13 sampai 31 sehingga tidak ada keraguan urutan bulan-hari.
  4. str.strip().str.title(): kedua method string dihubungkan langsung; accessor .str membiarkan sel NaN tetap NaN, jadi baris kategori kosong tidak ikut berubah menjadi teks aneh.
  5. fillna(df["produk"].map(PRODUK_KATEGORI)): map() menerjemahkan nama produk menjadi kategori, lalu fillna() mengisinya persis pada baris yang kosong.

Memvalidasi Hasil Pembersihan #

Output skrip menunjukkan jejak jumlah baris: 246 mentah, 240 setelah drop_duplicates(), dan 234 setelah filter harga — total 12 baris dibuang, tepat enam duplikat ditambah enam harga tidak valid dari daftar Bagian 1. Kategori akhir hanya empat nilai: Bahan Pokok, Kebersihan, Minuman, Perawatan. Kategori kosong tersisa 0, dan total NaN pada seluruh DataFrame juga 0.

Baris validasi paling penting adalah assert_frame_equal(df, bersihkan(mentah)): hasil langkah manual yang baru saja kita jalankan dibandingkan dengan fungsi bersihkan() di buat_dataset.py, dan keduanya identik. Fungsi kemasan inilah yang membuat Bagian 3 dan Bagian 4 tidak perlu mengulang lima langkah tersebut. Pola perbandingan dua jalur seperti ini Anda kenal dari section Pengujian dan Debugging, hanya saja kini dipakai sebagai gerbang mutu proses pembersihan.

Dataset 234 baris yang bersih sudah siap dianalisis. Bagian 3 akan menambah kolom omzet, menggabungkan data dengan tabel harga modal, lalu menjawab pertanyaan inti proyek: produk, kategori, dan kota mana yang paling berkontribusi terhadap penjualan Toko Sembako Berkah.

Sumber bacaan resmi: pandas.DataFrame.drop_duplicates , pandas.to_datetime , dan pandas.Series.str.strip .