Pertemuan 03 - Data Cleaning

Pertemuan 03 - Data Cleaning

Mendeteksi dan memperbaiki missing value, duplikat, serta inkonsistensi data pada dataset penjualan menggunakan Pandas.

Tujuan Praktikum

Setelah menyelesaikan praktikum ini, mahasiswa mampu:

  1. Mendiagnosis masalah kualitas data: nilai kosong, duplikat, dan inkonsistensi.
  2. Menangani missing value dengan strategi yang tepat per kolom (fillna atau dropna).
  3. Mendeteksi dan menghapus data duplikat dengan drop_duplicates.
  4. Melakukan transformasi data: standardisasi teks dan konversi tipe data.
  5. Menyimpan hasil pembersihan menjadi dataset bersih yang siap dianalisis.

Persiapan

Kebutuhan praktikum:

text
- Akun Google
- Google Colab
- Koneksi internet
- Dataset penjualan_kotor.csv (data sengaja dibuat "kotor" untuk latihan)

Buat notebook baru bernama Pertemuan03_NamaAnda.ipynb. Bagian ini sekaligus mengulang keterampilan import CSV dari Pertemuan 02 — pilih salah satu cara berikut untuk menyediakan datanya.

Opsi A - Membuat file langsung (paling aman)

Jalankan cell ini. File penjualan_kotor.csv akan dibuat otomatis di runtime sehingga praktikum bisa langsung dilanjutkan:

python
data_kotor = """tanggal,produk,kategori,qty,harga
2025-01-01,laptop,Elektronik,2,8500000
2025-01-01, Mouse ,Aksesoris,5,150000
2025-01-02,KEYBOARD,Aksesoris,3,250000
2025-01-02,Monitor,Elektronik,2,
2025-01-03,Printer,elektronik,1,1500000
2025-01-03,Mouse,Aksesoris,,150000
2025-02-01,Laptop,Elektronik,1,8500000
2025-02-01,Laptop,Elektronik,1,8500000
2025-02-02,Keyboard,,4,250000
2025-02-03,monitor,Elektronik,3,2000000
2025-02-03,Mouse,Aksesoris,6,150000
"""

with open("penjualan_kotor.csv", "w") as f:
    f.write(data_kotor)

print("File penjualan_kotor.csv siap digunakan.")

Opsi B - Mengunduh dari tautan (review import CSV)

Pandas dapat membaca CSV langsung dari URL. Ganti URL_DATASET dengan tautan yang dibagikan dosen:

python
import pandas as pd

URL_DATASET = "https://contoh-domain/penjualan_kotor.csv"   # ganti dengan tautan dari dosen
df = pd.read_csv(URL_DATASET)
df.head()

Opsi C - Mengunggah file dari komputer

Jika dosen membagikan file untuk diunduh ke komputer terlebih dahulu:

python
from google.colab import files
uploaded = files.upload()   # klik "Choose Files", pilih penjualan_kotor.csv
Setelah salah satu opsi dijalankan, semua langkah berikutnya dimulai dengan df = pd.read_csv("penjualan_kotor.csv").

Konsep Singkat

Data cleaning (pembersihan data) adalah proses memperbaiki data yang bermasalah sebelum dianalisis. Ini adalah tahap yang sering memakan waktu paling banyak dalam pekerjaan data, tetapi paling menentukan: analisis dan grafik secanggih apa pun akan menyesatkan jika datanya kotor. Prinsipnya dikenal sebagai garbage in, garbage out — sampah masuk, sampah keluar.

Alur data cleaning

Tiga masalah kualitas data yang paling umum, beserta contohnya pada dataset kita:

1. Missing Value (Nilai Kosong)

Sel yang tidak terisi. Pandas menandainya sebagai NaN (Not a Number). Contoh pada data kita: kolom harga kosong pada satu transaksi Monitor, qty kosong pada satu transaksi Mouse, dan kategori kosong pada satu transaksi Keyboard.

Dampak: perhitungan seperti sum() atau mean() bisa keliru, dan kolom angka yang mengandung NaN otomatis berubah menjadi tipe desimal (float).

2. Duplicate Data (Data Ganda)

Baris yang tercatat lebih dari satu kali, biasanya akibat kesalahan input. Contoh pada data kita: transaksi Laptop pada 2025-02-01 tercatat dua kali. Jika tidak dibuang, total penjualan akan terhitung berlebih.

3. Inkonsistensi (Perlu Transformasi)

Data yang sama ditulis dengan format berbeda sehingga dianggap berbeda oleh komputer. Contoh pada data kita: laptop, Laptop, dan LAPTOP dianggap tiga produk berbeda; ada spasi tersembunyi pada " Mouse "; kategori ditulis elektronik dan Elektronik. Termasuk pula tipe data yang salah (angka terbaca sebagai teks atau desimal).

Alur Kerja

Pola yang akan kita ikuti:

text
Diagnosa  →  Perbaiki (missing, duplikat, transformasi)  →  Verifikasi  →  Simpan

Langkah 1 - Memuat dan Mendiagnosis Data

Sebelum memperbaiki, kita potret dulu semua masalahnya. Jangan langsung membersihkan tanpa tahu apa yang salah.

python
import pandas as pd

df = pd.read_csv("penjualan_kotor.csv")
df

Output:

text
       tanggal     produk    kategori  qty      harga
0   2025-01-01     laptop  Elektronik  2.0  8500000.0
1   2025-01-01     Mouse    Aksesoris  5.0   150000.0
2   2025-01-02   KEYBOARD   Aksesoris  3.0   250000.0
3   2025-01-02    Monitor  Elektronik  2.0        NaN
4   2025-01-03    Printer  elektronik  1.0  1500000.0
5   2025-01-03      Mouse   Aksesoris  NaN   150000.0
6   2025-02-01     Laptop  Elektronik  1.0  8500000.0
7   2025-02-01     Laptop  Elektronik  1.0  8500000.0
8   2025-02-02   Keyboard         NaN  4.0   250000.0
9   2025-02-03    monitor  Elektronik  3.0  2000000.0
10  2025-02-03      Mouse   Aksesoris  6.0   150000.0

Periksa ukuran, tipe data, jumlah nilai kosong, dan jumlah duplikat:

python
print("Ukuran:", df.shape)
print("\nNilai kosong per kolom:")
print(df.isna().sum())
print("\nJumlah baris duplikat:", df.duplicated().sum())

Output:

text
Ukuran: (11, 5)

Nilai kosong per kolom:
tanggal     0
produk      0
kategori    1
qty         1
harga       1
dtype: int64

Jumlah baris duplikat: 1
Perhatikan: kolom qty dan harga bertipe desimal (2.0, 8500000.0) padahal seharusnya bilangan bulat. Ini terjadi karena adanya NaN. Tipe akan kita perbaiki setelah nilai kosong ditangani.

Langkah 2 - Menangani Missing Value

Kunci penting: tidak semua kolom ditangani dengan cara yang sama. Pembersihan butuh penilaian.

Lihat baris mana saja yang punya nilai kosong:

python
df[df.isna().any(axis=1)]

Output:

text
      tanggal    produk    kategori  qty      harga
3  2025-01-02   Monitor  Elektronik  2.0        NaN   <- harga kosong
5  2025-01-03     Mouse   Aksesoris  NaN   150000.0   <- qty kosong
8  2025-02-02  Keyboard         NaN  4.0   250000.0   <- kategori kosong

Strategi yang dipilih:

Kolom Keputusan Alasan
harga Isi (fillna) Harga satuan tiap produk diketahui dari tabel referensi
kategori Isi (fillna) Kategori bisa ditentukan dari nama produk
qty Buang (dropna) Jumlah barang tidak bisa ditebak; menebak qty berisiko keliru

Mengisi harga dan kategori menggunakan tabel referensi produk (map):

python
peta_harga = {
    "Laptop": 8500000, "Mouse": 150000, "Keyboard": 250000,
    "Monitor": 2000000, "Printer": 1500000
}
peta_kategori = {
    "Laptop": "Elektronik", "Monitor": "Elektronik", "Printer": "Elektronik",
    "Mouse": "Aksesoris", "Keyboard": "Aksesoris"
}

df["harga"] = df["harga"].fillna(df["produk"].map(peta_harga))
df["kategori"] = df["kategori"].fillna(df["produk"].map(peta_kategori))

Membuang baris yang qty-nya kosong:

python
df = df.dropna(subset=["qty"])

Verifikasi tidak ada lagi nilai kosong:

python
print(df.isna().sum().sum(), "nilai kosong tersisa")

Output:

text
0 nilai kosong tersisa
fillna mengisi sel kosong; dropna(subset=[...]) membuang baris yang kolom tertentunya kosong. Memilih di antaranya bergantung pada apakah nilai yang hilang bisa diperkirakan dengan aman.

Langkah 3 - Menangani Duplicate Data

Lihat baris yang terduplikasi:

python
df[df.duplicated()]

Output:

text
     tanggal  produk    kategori  qty      harga
7 2025-02-01  Laptop  Elektronik  1.0  8500000.0

Hapus duplikat dan cek perubahan ukuran:

python
print("Sebelum:", df.shape)
df = df.drop_duplicates()
print("Sesudah:", df.shape)

Output:

text
Sebelum: (10, 5)
Sesudah: (9, 5)
duplicated() menandai baris yang merupakan salinan dari baris sebelumnya; drop_duplicates() membuangnya dan menyisakan satu. Selalu periksa shape sebelum/sesudah agar yakin jumlah yang terhapus masuk akal.

Langkah 4 - Transformasi Data

Tahap ini menyeragamkan format agar data konsisten dan bertipe benar.

Standardisasi Teks

Buang spasi berlebih dengan str.strip() dan seragamkan kapitalisasi dengan str.title():

python
df["produk"] = df["produk"].str.strip().str.title()
df["kategori"] = df["kategori"].str.strip().str.title()

print(df["produk"].unique())
print(df["kategori"].unique())

Output:

text
['Laptop' 'Mouse' 'Keyboard' 'Monitor' 'Printer']
['Elektronik' 'Aksesoris']

Sekarang laptop, KEYBOARD, monitor, dan " Mouse " sudah seragam.

Konversi Tipe Data

Setelah nilai kosong hilang, ubah qty dan harga menjadi bilangan bulat, dan tanggal menjadi tipe tanggal:

python
df["qty"] = df["qty"].astype(int)
df["harga"] = df["harga"].astype(int)
df["tanggal"] = pd.to_datetime(df["tanggal"])

df.dtypes

Output:

text
tanggal     datetime64[ns]
produk              object
kategori            object
qty                  int64
harga                int64
dtype: object
Konversi astype(int) hanya berhasil bila kolom tidak lagi mengandung NaN. Itulah sebabnya missing value ditangani lebih dulu.

Langkah 5 - Verifikasi dan Menyimpan

Lakukan pemeriksaan akhir, lalu simpan data bersih ke file baru.

python
print("Ukuran akhir   :", df.shape)
print("Nilai kosong   :", df.isna().sum().sum())
print("Baris duplikat :", df.duplicated().sum())
print("Jumlah produk  :", df["produk"].nunique())
df.head()

Output:

text
Ukuran akhir   : (9, 5)
Nilai kosong   : 0
Baris duplikat : 0
Jumlah produk  : 5

Simpan ke penjualan_bersih.csv (parameter index=False agar nomor baris tidak ikut tertulis):

python
df.to_csv("penjualan_bersih.csv", index=False)
print("Data bersih tersimpan sebagai penjualan_bersih.csv")
File penjualan_bersih.csv inilah yang akan dipakai untuk visualisasi pada pertemuan berikutnya.

Referensi Fungsi Penting

Bagian ini merangkum fungsi-fungsi Pandas yang relevan untuk data cleaning beserta variasinya, agar mahasiswa dapat bereksperimen lebih jauh dari sekadar langkah praktikum di atas. Semua perintah dapat langsung disalin dan dicoba pada df.

Mendeteksi Missing Value

Pada Langkah 1 dan 2 kita memakai df[df.isna().any(axis=1)]. Baris itu sebenarnya gabungan beberapa fungsi kecil:

  • df.isna() menghasilkan tabel True/FalseTrue berarti sel kosong. (df.isnull() adalah nama lain yang identik.)
  • .any(axis=1) mengecek per baris: bernilai True bila ada minimal satu sel kosong di baris itu.
  • df[ ... ] lalu menampilkan baris-baris yang terdeteksi.

Memahami axis (sering membingungkan, jadi ingat baik-baik):

text
axis=0  → arah ke bawah   (memproses antar-baris, hasil per KOLOM)
axis=1  → arah ke samping (memproses antar-kolom, hasil per BARIS)
Perintah Fungsi
df.isna() Tabel True/False, True = kosong
df.notna() Kebalikan dari isna() (True = terisi)
df.isna().sum() Jumlah nilai kosong per kolom
df.isna().sum().sum() Total nilai kosong seluruh tabel
df.isna().any() Kolom mana yang memiliki nilai kosong
df.isna().any(axis=1) Baris mana yang memiliki nilai kosong
df["harga"].isna() Cek nilai kosong pada satu kolom
df.info() Ringkasan jumlah data terisi per kolom
python
df.isna().sum()              # berapa kosong tiap kolom
df.isna().sum().sum()        # total kosong seluruh tabel
df[df.isna().any(axis=1)]    # tampilkan baris yang punya kosong
df[df["harga"].isna()]       # tampilkan baris yang harganya kosong saja

Menangani Missing Value

Pada Langkah 2 kita memakai df.dropna(subset=["qty"]) dan fillna(...). Keduanya punya banyak variasi.

Membuang dengan dropna:

Perintah Efek
df.dropna() Buang baris yang punya kosong di kolom mana pun
df.dropna(subset=["qty"]) Buang baris hanya bila kolom qty kosong
df.dropna(how="all") Buang baris hanya bila semua kolomnya kosong
df.dropna(thresh=3) Pertahankan baris yang punya minimal 3 nilai terisi
df.dropna(axis=1) Buang kolom (bukan baris) yang mengandung kosong

Mengisi dengan fillna dan kerabatnya:

Perintah Efek
df["x"].fillna(0) Isi dengan nilai tetap (mis. 0)
df["x"].fillna(df["x"].mean()) Isi dengan rata-rata kolom
df["x"].fillna(df["x"].median()) Isi dengan nilai tengah (median)
df["x"].fillna(df["x"].mode()[0]) Isi dengan nilai terbanyak (modus) — cocok untuk kategori
df["x"].ffill() Isi dengan nilai dari baris atas-nya
df["x"].bfill() Isi dengan nilai dari baris bawah-nya
df["x"].fillna(df["kunci"].map(peta)) Isi dari tabel referensi (seperti di Langkah 2)
python
df["harga"] = df["harga"].fillna(df["harga"].median())    # isi dengan median
df["kategori"] = df["kategori"].fillna("Tidak Diketahui")  # isi dengan teks tetap
df = df.dropna(subset=["qty"])                             # buang baris qty kosong
Pilihannya selalu sama: isi bila nilai bisa diperkirakan dengan aman, buang bila tidak.

Menangani Duplikat

Pada Langkah 3 kita memakai df[df.duplicated()] dan df.drop_duplicates(). Parameter keep dan subset membuatnya fleksibel.

df.duplicated() menandai baris yang sama dengan baris sebelumnya. Parameter keep menentukan kemunculan mana yang tidak dihitung sebagai duplikat:

Nilai keep Arti
"first" (default) Tandai semua kembaran kecuali yang pertama
"last" Tandai semua kembaran kecuali yang terakhir
False Tandai semua baris yang kembar
Perintah Fungsi
df.duplicated() Tandai baris duplikat (True/False)
df.duplicated().sum() Hitung jumlah duplikat
df[df.duplicated()] Tampilkan baris yang duplikat
df[df.duplicated(keep=False)] Tampilkan semua baris kembar
df.duplicated(subset=["tanggal", "produk"]) Anggap duplikat berdasar kolom tertentu saja
df.drop_duplicates() Hapus duplikat, sisakan satu
df.drop_duplicates(subset=["tanggal", "produk"], keep="last") Hapus berdasar kolom tertentu, simpan kemunculan terakhir
python
df.duplicated().sum()            # ada berapa duplikat
df[df.duplicated(keep=False)]    # lihat semua baris yang kembar
df = df.drop_duplicates()        # hapus, sisakan satu

Standardisasi Teks dan Tipe

Pelengkap untuk tahap Transformasi (Langkah 4):

Perintah Fungsi
df["x"].str.strip() Hapus spasi di awal/akhir teks
df["x"].str.lower() / .str.upper() Ubah ke huruf kecil / besar
df["x"].str.title() Awali tiap kata dengan huruf besar
df["x"].str.replace("a", "b") Ganti potongan teks
df["x"].astype(int) / .astype(float) Ubah tipe data angka
pd.to_datetime(df["x"]) Ubah teks menjadi tipe tanggal
df["x"].unique() / .nunique() Lihat nilai unik / jumlah nilai unik
df["x"].value_counts() Hitung frekuensi tiap nilai
python
df["produk"].value_counts()             # lihat sebaran & ejaan nilai
df["produk"] = df["produk"].str.strip().str.title()
df["produk"].unique()                   # pastikan sudah seragam

Pengujian

Pastikan hasil pembersihan memenuhi seluruh kriteria berikut:

  1. df.shape menghasilkan (9, 5).
  2. df.isna().sum().sum() bernilai 0 (tidak ada nilai kosong).
  3. df.duplicated().sum() bernilai 0 (tidak ada duplikat).
  4. df["produk"].nunique() bernilai 5 dan df["kategori"].nunique() bernilai 2 (teks konsisten).
  5. df.dtypes menunjukkan qty dan harga bertipe int64, serta tanggal bertipe datetime64[ns].

Verifikasi cepat secara otomatis:

python
assert df.shape == (9, 5)
assert df.isna().sum().sum() == 0
assert df.duplicated().sum() == 0
assert df["produk"].nunique() == 5
print("Semua pengujian lolos. Data sudah bersih.")

Troubleshooting

Masalah Penyebab Solusi
ValueError: cannot convert ... NaN saat astype(int) Masih ada nilai kosong di kolom Tangani missing value lebih dulu (Langkah 2)
SettingWithCopyWarning Memodifikasi hasil filter, bukan DataFrame asli Tambahkan .copy() saat membuat subset, atau gunakan .loc
Teks masih dianggap berbeda setelah title() Ada spasi tersembunyi di awal/akhir Jalankan str.strip() sebelum str.title()
map justru memunculkan NaN baru Kunci pada peta tidak cocok (beda ejaan/kapital) Samakan dulu teks, atau periksa isi peta
Duplikat tidak terhapus Ada perbedaan tak terlihat (spasi/kapital) Bersihkan teks dulu, baru drop_duplicates()

Tugas Praktikum

Buat laporan (format PDF) yang berisi:

  1. Identitas: Nama dan NIM.
  2. Ringkasan diagnosis awal: jumlah nilai kosong per kolom dan jumlah duplikat.
  3. Penjelasan singkat strategi yang dipilih untuk tiap kolom yang bermasalah.
  4. Bukti data sudah bersih (output Pengujian).
  5. Analisis ulang dari data bersih: hitung kembali produk terlaris dan total penjualan menggunakan teknik dari Pertemuan 02 (groupby), lalu bandingkan — apakah hasilnya akan berbeda jika data tidak dibersihkan? Jelaskan mengapa.
Tips: ekspor notebook ke PDF melalui menu File → Print → Save as PDF.

Kesimpulan

Pada pertemuan ini mahasiswa belajar bahwa kualitas analisis sangat bergantung pada kualitas data. Mahasiswa mendiagnosis tiga masalah utama — missing value, duplicate data, dan inkonsistensi — lalu menanganinya dengan strategi yang tepat: mengisi atau membuang nilai kosong sesuai konteks, menghapus duplikat, menyeragamkan teks, dan mengoreksi tipe data. Hasil akhirnya adalah dataset bersih yang tersimpan dan siap dipakai. Keterampilan ini menjadi fondasi wajib sebelum melangkah ke visualisasi data pada pertemuan berikutnya, karena grafik yang dibuat dari data kotor hanya akan menghasilkan kesimpulan yang menyesatkan.