Data Science
Pembersihan Data: Missing Values dan Filtering Kondisional
Kuasai teknik pembersihan data tabular: deteksi dan tangani missing values, hapus duplikasi, perbaiki tipe data dengan astype, serta filter data kondisional ganda.

Sebuah pepatah klasik dalam ilmu komputer berbunyi: "Garbage In, Garbage Out" (sampah yang masuk akan menghasilkan sampah yang keluar). Pepatah ini menjadi sepuluh kali lipat lebih nyata dalam ranah data science. Bayangkan sebuah tim rumah sakit mengumpulkan data rekam medis pasien untuk memprediksi risiko penyakit kardiovaskular. Di dalam berkas yang terkumpul, terdapat kolom tekanan darah yang kosong karena pasien menolak diperiksa, umur pasien tertulis sebagai teks "Tiga Puluh Lima", formulir pendaftaran terkirim dua kali karena koneksi internet terputus, dan kolom penghasilan memiliki nilai minus. Jika data mentah ini langsung disodorkan ke algoritma machine learning, model akan langsung menghasilkan galat (error) komputasi atau membuat diagnosis medis yang keliru secara fatal. Sebelum kita dapat menggali wawasan atau membangun model prediktif, pembersihan data (data cleaning) dan penyaringan kondisi (conditional filtering) adalah gerbang wajib yang harus dilalui.
Yang akan dipelajari
Dalam artikel ini, Anda akan mempraktikkan teknik pembersihan data tabular esensial menggunakan Pandas:
- Mengidentifikasi keberadaan dan persebaran nilai kosong (missing values) menggunakan
.isna()dan agregasi.sum(). - Memilih strategi penanganan nilai hilang: menghapus baris/kolom dengan
.dropna()atau melakukan imputasi nilai pengganti dengan.fillna(). - Mendeteksi dan mengeliminasi catatan transaksi ganda menggunakan
.duplicated()dan.drop_duplicates(). - Mengoreksi dan mengubah tipe data kolom yang keliru menggunakan method
.astype(). - Menyaring baris data secara akurat menggunakan filter kondisional ganda (
&,|), method.isin(), serta operator rentang.between().
Mendeteksi Nilai Hilang (Missing Values)
Dalam Pandas, nilai yang hilang atau tidak diketahui secara standar direpresentasikan dengan konstanta NaN (Not a Number) atau None.
Langkah awal dari setiap audit kualitas data adalah menghitung berapa banyak nilai kosong yang terdapat pada setiap kolom.
import pandas as pd
import numpy as np
# Simulasi data survei pelanggan yang tidak lengkap
data_survei = {
"id_responden": [101, 102, 103, 104, 105, 106],
"nama": ["Rina", "Bambang", np.nan, "Dina", "Eko", "Fani"],
"usia": [24, np.nan, 31, 29, np.nan, 42],
"kota": ["Jakarta", "Surabaya", "Bandung", np.nan, "Jakarta", "Semarang"],
"skor_kepuasan": [4.5, 3.8, np.nan, 4.0, 5.0, 4.2],
"pengeluaran_bulanan": [1200000, 850000, 2100000, 1400000, np.nan, 3000000]
}
df_survei = pd.DataFrame(data_survei)
print("Dataset Survei Awal:")
print(df_survei)
# 1. Menghitung total nilai kosong per kolom
missing_per_kolom = df_survei.isna().sum()
print("\nJumlah Nilai Kosong per Kolom:")
print(missing_per_kolom)
# 2. Menghitung persentase nilai kosong
persentase_missing = (df_survei.isna().sum() / len(df_survei)) * 100
print("\nPersentase Data Hilang (%):")
print(persentase_missing.round(1))
# Output:
# Dataset Survei Awal:
# id_responden nama usia kota skor_kepuasan pengeluaran_bulanan
# 0 101 Rina 24.0 Jakarta 4.5 1200000.0
# 1 102 Bambang NaN Surabaya 3.8 850000.0
# 2 103 NaN 31.0 Bandung NaN 2100000.0
# 3 104 Dina 29.0 NaN 4.0 1400000.0
# 4 105 Eko NaN Jakarta 5.0 NaN
# 5 106 Fani 42.0 Semarang 4.2 3000000.0
#
# Jumlah Nilai Kosong per Kolom:
# id_responden 0
# nama 1
# usia 2
# kota 1
# skor_kepuasan 1
# pengeluaran_bulanan 1
# dtype: int64
#
# Persentase Data Hilang (%):
# id_responden 0.0
# nama 16.7
# usia 33.3
# kota 16.7
# skor_kepuasan 16.7
# pengeluaran_bulanan 16.7
# dtype: float64
Strategi Penanganan Missing Values
Setelah mengetahui kolom mana saja yang berlubang, kita memiliki dua strategi utama: Menghapus (Drop) atau Mengisi (Impute).
1. Menghapus Data: .dropna()
Penghapusan cocok digunakan jika persentase baris yang hilang sangat kecil (misal ) atau data identitas utama bernilai kosong sehingga baris tersebut tidak lagi valid.
df.dropna(): Menghapus baris jika ada satu saja nilai kosong di kolom mana pun.df.dropna(subset=['nama']): Hanya menghapus baris jika kolom'nama'yang kosong.df.dropna(how='all'): Hanya menghapus baris jika seluruh kolom pada baris tersebut bernilai kosong.
# Menghapus hanya baris yang tidak memiliki nama responden
df_bersih_nama = df_survei.dropna(subset=["nama"]).copy()
print("Setelah drop baris tanpa nama (total baris tersisa:", len(df_bersih_nama), ")")
2. Imputasi Data: .fillna()
Jika dataset berukuran terbatas, menghapus baris akan membuang informasi berharga lainnya. Solusinya adalah melakukan imputasi:
- Kolom numerik: Diganti dengan nilai Rata-rata (Mean) atau Median (nilai tengah).
- Kolom kategori/teks: Diganti dengan Modus (kategori paling sering muncul) atau label khusus seperti
"Tidak Diketahui".
# Membuat salinan data untuk proses imputasi
df_imputasi = df_survei.copy()
# A. Imputasi kolom numerik 'usia' dengan nilai median
median_usia = df_imputasi["usia"].median()
df_imputasi["usia"] = df_imputasi["usia"].fillna(median_usia)
# B. Imputasi kolom kategori 'kota' dengan modus teratas
modus_kota = df_imputasi["kota"].mode()[0]
df_imputasi["kota"] = df_imputasi["kota"].fillna(modus_kota)
# C. Imputasi nama responden yang kosong dengan teks penanda
df_imputasi["nama"] = df_imputasi["nama"].fillna("Anonim")
print("Hasil Imputasi Missing Values:")
print(df_imputasi[["nama", "usia", "kota"]])
# Output:
# Hasil Imputasi Missing Values:
# nama usia kota
# 0 Rina 24.0 Jakarta
# 1 Bambang 30.0 Surabaya
# 2 Anonim 31.0 Bandung
# 3 Dina 29.0 Jakarta
# 4 Eko 30.0 Jakarta
# 5 Fani 42.0 Semarang
Catatan: Median usia yang dihitung dari [24, 31, 29, 42] adalah 30.0, yang kemudian mengisi baris Bambang dan Eko secara otomatis.
Menghapus Duplikasi Data
Data duplikat kerap terjadi akibat masalah jaringan saat pengguna menekan tombol transaksi berkali-kali atau proses penggabungan berkas CSV yang berulang.
.duplicated(): Mengembalikan Series boolean penanda baris yang duplikat..drop_duplicates(): Menghapus baris duplikat dan mempertahankan catatan unik pertama.
# Simulasi dataset dengan baris duplikat
data_karyawan = {
"nik": ["A01", "A02", "A01", "A03", "A02"],
"nama": ["Rudi", "Siti", "Rudi", "Tono", "Siti"],
"divisi": ["Sales", "HRD", "Sales", "IT", "HRD"]
}
df_karyawan = pd.DataFrame(data_karyawan)
print("Jumlah baris asli:", len(df_karyawan))
print("Deteksi duplikasi:\n", df_karyawan.duplicated())
# Menghapus duplikasi secara permanen
df_karyawan_unik = df_karyawan.drop_duplicates(subset=["nik"]).reset_index(drop=True)
print("\nDataFrame Setelah Duplikasi Dihapus:")
print(df_karyawan_unik)
# Output:
# Jumlah baris asli: 5
# Deteksi duplikasi:
# 0 False
# 1 False
# 2 True
# 3 False
# 4 True
# dtype: bool
#
# DataFrame Setelah Duplikasi Dihapus:
# nik nama divisi
# 0 A01 Rudi Sales
# 1 A02 Siti HRD
# 2 A03 Tono IT
Memperbaiki Tipe Data dengan .astype()
Sering kali angka terbaca sebagai string karena adanya simbol mata uang atau kesalahan pembacaan CSV. Sebelum kolom tersebut dapat dikalkulasi, tipe datanya harus dikonversi dengan method .astype():
data_harga = {
"produk": ["Keyboard", "Mouse", "Headset"],
"kode_kategori": [1, 2, 1],
"stok_str": ["15", "40", "25"]
}
df_produk = pd.DataFrame(data_harga)
print("Tipe data awal:")
print(df_produk.dtypes)
# Mengonversi stok dari teks string menjadi integer
df_produk["stok"] = df_produk["stok_str"].astype(int)
# Mengonversi kode_kategori menjadi string/category agar tidak diperlakukan sebagai angka matematika
df_produk["kode_kategori"] = df_produk["kode_kategori"].astype(str)
print("\nTipe data setelah perbaikan:")
print(df_produk.dtypes)
# Output:
# Tipe data awal:
# produk object
# kode_kategori int64
# stok_str object
# dtype: object
#
# Tipe data setelah perbaikan:
# produk object
# kode_kategori object
# stok_str object
# stok int64
# dtype: object
Filtering Kondisional Lanjutan
Dalam analisis eksploratif, kita sering kali hanya tertarik menelaah subset populasi tertentu. Berikut adalah teknik penyaringan kondisional yang paling sering digunakan di industri:
1. Kondisi Logika Ganda (& dan |)
Ingatlah aturan emas: gunakan operator bitwise & (AND) dan | (OR), serta wajib mengapit setiap ekspresi kondisi dalam tanda kurung ().
data_transaksi = pd.DataFrame({
"transaksi_id": ["TRX01", "TRX02", "TRX03", "TRX04", "TRX05", "TRX06"],
"kategori": ["Elektronik", "Fashion", "Elektronik", "Makanan", "Fashion", "Elektronik"],
"nominal": [1200000, 350000, 4500000, 150000, 850000, 950000],
"metode": ["QRIS", "Kartu Kredit", "Transfer", "QRIS", "QRIS", "Transfer"]
})
# Filter: Kategori 'Elektronik' DAN nominal belanja di atas 1 juta
filter_elektronik_mahal = (data_transaksi["kategori"] == "Elektronik") & (data_transaksi["nominal"] > 1000000)
print("Transaksi Elektronik > 1 Juta:")
print(data_transaksi[filter_elektronik_mahal])
# Filter: Pembayaran via 'QRIS' ATAU nominal belanja di bawah 500 ribu
filter_qris_atau_murah = (data_transaksi["metode"] == "QRIS") | (data_transaksi["nominal"] < 500000)
print("\nTransaksi QRIS atau Belanja Murah:")
print(data_transaksi[filter_qris_atau_murah])
2. Menggunakan .isin() untuk Seleksi Multi-Kategori
Daripada menulis kondisi OR panjang seperti (kota == 'Jakarta') | (kota == 'Bandung') | (kota == 'Surabaya'), gunakan method .isin() yang jauh lebih ringkas:
target_kategori = ["Elektronik", "Fashion"]
filter_kategori = data_transaksi["kategori"].isin(target_kategori)
print("\nTransaksi Kategori Elektronik & Fashion:")
print(data_transaksi[filter_kategori])
3. Menggunakan .between() untuk Filter Rentang Angka
Method .between(nilai_bawah, nilai_atas) memeriksa apakah nilai numerik berada di dalam rentang inklusif tertentu:
# Transaksi dengan nominal antara 500.000 hingga 2.000.000
filter_rentang = data_transaksi["nominal"].between(500000, 2000000)
print("\nTransaksi dengan nominal rentang 500rb - 2jt:")
print(data_transaksi[filter_rentang])
# Output:
# Transaksi dengan nominal rentang 500rb - 2jt:
# transaksi_id kategori nominal metode
# 0 TRX01 Elektronik 1200000 QRIS
# 4 TRX05 Fashion 850000 QRIS
# 5 TRX06 Elektronik 950000 Transfer
Coba sendiri
Jalankan skenario pembersihan dataset log penjualan berikut di Google Colab:
- Buat DataFrame transaksi berisi 6 baris dengan kolom
ID,Pelanggan,Total_Bayar, danRating. Berikan nilainp.nanpada 2 baris kolomRatingdan 1 baris baris duplikat. - Deteksi dan hapus baris yang duplikat berdasarkan kolom
ID. - Isi nilai
Ratingyang hilang dengan nilai rata-rata (mean) rating yang tersedia. - Terapkan filtering untuk menyaring transaksi yang memiliki
Total_Bayardi atas nilai median sertaRatinglebih besar atau sama dengan 4.0.
# Coba jalankan di sel Google Colab:
import pandas as pd
import numpy as np
df_toko = pd.DataFrame({
"ID": [1, 2, 3, 3, 4, 5],
"Pelanggan": ["Ali", "Bella", "Caca", "Caca", "Doni", "Elisa"],
"Total_Bayar": [150000, 320000, 80000, 80000, 450000, 210000],
"Rating": [5.0, np.nan, 4.0, 4.0, np.nan, 4.5]
})
# 1. Hapus duplikat
df_bersih = df_toko.drop_duplicates(subset=["ID"]).copy()
# 2. Imputasi rating dengan mean
rata_rating = df_bersih["Rating"].mean()
df_bersih["Rating"] = df_bersih["Rating"].fillna(rata_rating)
# 3. Filter kondisional
median_bayar = df_bersih["Total_Bayar"].median()
hasil_filter = df_bersih[(df_bersih["Total_Bayar"] > median_bayar) & (df_bersih["Rating"] >= 4.0)]
print("Hasil Akhir Setelah Cleaning & Filtering:")
print(hasil_filter)
Kesalahan yang sering terjadi
Tabel berikut merangkum kesalahan fatal yang kerap ditemui saat membersihkan data menggunakan Pandas:
| Kesalahan Pemula | Gejala / Pesan Kesalahan | Penyebab Utama | Solusi yang Tepat |
|---|---|---|---|
Lupa menyimpan hasil .dropna() atau .fillna() |
Nilai NaN tetap ada saat dicek kembali | Method tidak mengubah DataFrame secara inplace tanpa assignment ulang. | Simpan kembali ke kolom/dataframe: df['kolom'] = df['kolom'].fillna(nilai) atau df = df.dropna(). |
| Mengisi missing values dengan rata-rata (mean) pada data skewed | Nilai imputasi tidak realistis (sangat bias) | Terdapat nilai ekstrem/outlier yang menarik rata-rata menjadi terlalu tinggi. | Gunakan nilai tengah (median) untuk data numerik yang memiliki outlier atau data pendapatan. |
Menggunakan operator kata kunci and alih-alih & |
ValueError: The truth value of a Series is ambiguous |
Operator Python and mengevaluasi kebenaran skalar tunggal, bukan elemen Series. |
Selalu gunakan operator bitwise & dan kurung setiap kondisi: (kondisi_1) & (kondisi_2). |
Menghapus duplikat tanpa parameter subset |
Baris yang seharusnya duplikat tidak terhapus | Hanya satu kolom pengenal (seperti NIK/ID) yang duplikat, sedangkan kolom timestamp berbeda mikrodetik. | Tentukan kolom pengenal unik utama pada parameter subset: df.drop_duplicates(subset=['id_pengguna']). |
Kaitannya dengan Praktik Industri / AI: Dalam arsitektur sistem rekomendasi e-commerce atau pipeline perbankan modern, data kotor dapat melumpuhkan model inferensi Real-Time. Pustaka pemodelan Scikit-Learn versi standar secara ketat menolak input matriks data yang mengandung satu saja nilai
NaNdan akan langsung melemparkan galatValueError: Input contains NaN. Oleh sebab itu, teknik imputasi dan validasi tipe data adalah komponen vital pertama yang selalu disematkan dalam pipeline rekayasa fitur (data preprocessing pipeline) sebelum model apa pun dapat beroperasi.
Latihan
Ujilah kemampuan pembersihan data Anda dengan menyelesaikan 3 latihan analisis berikut:
- Soal 1: Sebuah platform daring mendata performa konten video:
Hitung berapa total missing values pada masing-masing kolom. Kemudian lakukan imputasi pada kolomdf_video = pd.DataFrame({ "video_id": ["V01", "V02", "V03", "V04", "V05"], "judul": ["Tutorial Python", "Vlog Santai", "Resep Masak", "Tips Karir", "Unboxing Gadget"], "views": [15000, np.nan, 8500, 24000, np.nan], "likes": [1200, 450, np.nan, 1900, 310] })viewsdengan nilai median dari data views yang ada. - Soal 2: Diberikan data keanggotaan gym yang memuat baris pendaftaran ganda:
Hapus seluruh baris duplikat berdasarkan kolomdf_member = pd.DataFrame({ "member_id": ["M01", "M02", "M03", "M01", "M04", "M02"], "nama": ["Andi", "Bima", "Chandra", "Andi", "Dodi", "Bima"], "paket": ["Gold", "Silver", "Platinum", "Gold", "Silver", "Silver"] })member_idsehingga setiap anggota hanya tercatat satu kali, dan atur ulang indeks barisnya (reset index). - Soal 3: Diberikan DataFrame pelamar kerja:
Terapkan filtering kondisional untuk menyaring pelamar yang memenuhi kualifikasi: memiliki pengalaman kerja minimal 3 tahun DAN skor tes DAN berpendidikandf_pelamar = pd.DataFrame({ "nama": ["Aldi", "Bella", "Coki", "Diana", "Erik", "Fira"], "pengalaman_tahun": [1, 5, 3, 7, 2, 4], "skor_tes": [72, 88, 85, 94, 68, 91], "pendidikan": ["S1", "S1", "D3", "S2", "S1", "S1"] })"S1". Tampilkan hasil tabel pelamar yang lolos kualifikasi.
Kunci jawaban dan pembahasan
Solusi Soal 1
import pandas as pd
import numpy as np
df_video = pd.DataFrame({
"video_id": ["V01", "V02", "V03", "V04", "V05"],
"judul": ["Tutorial Python", "Vlog Santai", "Resep Masak", "Tips Karir", "Unboxing Gadget"],
"views": [15000, np.nan, 8500, 24000, np.nan],
"likes": [1200, 450, np.nan, 1900, 310]
})
# 1. Total missing values per kolom
print("Total Missing Values:")
print(df_video.isna().sum())
# 2. Imputasi nilai views dengan median
median_views = df_video["views"].median()
df_video["views"] = df_video["views"].fillna(median_views)
print(f"\nNilai median views: {median_views}")
print("\nDataFrame setelah imputasi kolom views:")
print(df_video)
# Output:
# Total Missing Values:
# video_id 0
# judul 0
# views 2
# likes 1
# dtype: int64
#
# Nilai median views: 15000.0
#
# DataFrame setelah imputasi kolom views:
# video_id judul views likes
# 0 V01 Tutorial Python 15000.0 1200.0
# 1 V02 Vlog Santai 15000.0 450.0
# 2 V03 Resep Masak 8500.0 NaN
# 3 V04 Tips Karir 24000.0 1900.0
# 4 V05 Unboxing Gadget 15000.0 310.0
Pembahasan: df_video.isna().sum() mendeteksi 2 nilai kosong pada kolom views dan 1 nilai kosong pada kolom likes. Dari nilai views yang terisi [15000, 8500, 24000], median yang diperoleh adalah 15000.0, yang kemudian menggantikan nilai NaN pada baris V02 dan V05.
Solusi Soal 2
import pandas as pd
df_member = pd.DataFrame({
"member_id": ["M01", "M02", "M03", "M01", "M04", "M02"],
"nama": ["Andi", "Bima", "Chandra", "Andi", "Dodi", "Bima"],
"paket": ["Gold", "Silver", "Platinum", "Gold", "Silver", "Silver"]
})
# Menghapus duplikasi member_id dan mereset index
df_member_unik = df_member.drop_duplicates(subset=["member_id"]).reset_index(drop=True)
print("Data Member Unik:")
print(df_member_unik)
# Output:
# Data Member Unik:
# member_id nama paket
# 0 M01 Andi Gold
# 1 M02 Bima Silver
# 2 M03 Chandra Platinum
# 3 M04 Dodi Silver
Pembahasan: Method .drop_duplicates(subset=['member_id']) menyaring baris sehingga menyisakan catatan kemunculan pertama. Pemanggilan .reset_index(drop=True) penting untuk memperbarui urutan nomor baris agar kembali runtut dari 0 sampai 3 tanpa meninggalkan lubang indeks lama.
Solusi Soal 3
import pandas as pd
df_pelamar = pd.DataFrame({
"nama": ["Aldi", "Bella", "Coki", "Diana", "Erik", "Fira"],
"pengalaman_tahun": [1, 5, 3, 7, 2, 4],
"skor_tes": [72, 88, 85, 94, 68, 91],
"pendidikan": ["S1", "S1", "D3", "S2", "S1", "S1"]
})
# Menyusun 3 kriteria filter sekaligus
kriteria_lolos = (
(df_pelamar["pengalaman_tahun"] >= 3) &
(df_pelamar["skor_tes"] >= 85) &
(df_pelamar["pendidikan"] == "S1")
)
pelamar_lolos = df_pelamar[kriteria_lolos]
print("Pelamar yang lolos kualifikasi:")
print(pelamar_lolos)
# Output:
# Pelamar yang lolos kualifikasi:
# nama pengalaman_tahun skor_tes pendidikan
# 1 Bella 5 88 S1
# 5 Fira 4 91 S1
Pembahasan: Kondisi majemuk digabungkan dengan operator &. Meskipun Coki memiliki pengalaman 3 tahun dan skor 85, ia berpendidikan D3 sehingga gugur. Diana memiliki pengalaman 7 tahun dan skor 94, namun berpendidikan S2 sehingga tidak memenuhi syarat persis S1. Hanya Bella dan Fira yang memenuhi ketiga kriteria secara bersamaan.



Diskusi (0)
Kami menyimpan nama (opsional), komentar, dan hash teknis (bukan alamat IP asli) untuk mencegah spam. Cookie fungsional dipakai agar Anda dapat mengedit atau menghapus komentar sendiri dari browser ini. Untuk permintaan penghapusan, gunakan halaman kontak.
Belum ada komentar. Mulai diskusinya.