Pembersihan data bukan membuang semua yang terlihat janggal, melainkan memperbaiki setiap masalah dengan keputusan yang bisa dipertanggungjawabkan. Pada bagian ini kita mengeksekusi rencana dari Bagian 4 satu per satu: membuang baris duplikat, menyeragamkan penulisan kelas, lalu menghadapi sel kehadiran yang kosong dengan keputusan yang paling jujur di antara pilihan yang ada. Di akhir bagian kita punya 30 baris data bersih yang lolos pemeriksaan ulang — dan sebuah fungsi pemuat yang akan dipakai seluruh bagian berikutnya.
Urutan Pembersihan yang Dipilih #
Urutan langkah menentukan hasil. Kita kerjakan dengan urutan berikut dan alasannya:
- Buang duplikat lebih dulu — salinan baris akan mendistorsi perhitungan apa pun yang dilakukan setelahnya, jadi ia tidak boleh ikut ke langkah mana pun.
- Seragamkan penulisan kelas ke huruf besar — agregasi per kelas akan memisahkan
adariAbila ini belum dibereskan. - Ubah
-menjadiNaNdan jadikan kolom kehadiran numerik — nilai kosong harus berbentukNaNagar pandas bisa membedakannya dari data asli. - Putuskan nasib baris tanpa kehadiran — isi ulang bila ada dasar hitungnya; bila tidak ada, buang dengan catatan.
Langkah 1-2: Duplikat dan Penulisan Kelas #
Dua langkah pertama dikerjakan sekali jalan karena sama-sama menyangkut bentuk baris, bukan hitungan:
import pandas as pd
df = pd.read_csv("../data/nilai_siswa.csv")
# Langkah 1: buang baris duplikat persis.
bersih = df.drop_duplicates().reset_index(drop=True)
# Langkah 2: seragamkan penulisan kelas ke huruf besar.
bersih["kelas"] = bersih["kelas"].str.upper()
print("Baris data mentah :", len(df))
print("Baris setelah bersih:", len(bersih))
print("Nilai unik kelas :", sorted(bersih["kelas"].unique()))
print("Siswa unik :", bersih["siswa"].nunique())Output yang Dihasilkan:
Baris data mentah : 32
Baris setelah bersih: 31
Nilai unik kelas : ['A', 'B']
Siswa unik : 31Mari kita bedah hasilnya:
drop_duplicates()membuang salinan dan menyisakan baris asli — jumlah baris turun dari 32 ke 31, dan sekarang setiap siswa tepat satu baris.reset_index(drop=True)merapikan kembali penomoran baris setelah penghapusan.str.upper()menaikkan seluruh penulisan kelas ke huruf besar: nilaiamilik Gita menyatu denganA, sehingga nilai unik kelas tinggal dua. Dua masalah selesai tanpa kehilangan satu pun baris data.
Langkah 3: Kehadiran yang Kosong #
Kini gilitan masalah yang paling menuntut pertimbangan. Kita ubah - menjadi NaN dan konversikan kolomnya ke numerik dulu, agar masalahnya terlihat bentuk persisnya:
import pandas as pd
df = pd.read_csv("../data/nilai_siswa.csv").drop_duplicates().reset_index(drop=True)
df["kelas"] = df["kelas"].str.upper()
# Langkah 3: ubah '-' menjadi NaN lalu jadikan kolom kehadiran numerik.
df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
print("Tipe kolom kehadiran setelah konversi :", df["kehadiran_persen"].dtype)
print("Baris tanpa kehadiran :", df["kehadiran_persen"].isna().sum())
print("\nBaris yang kehilangan kehadiran:")
print(df[df["kehadiran_persen"].isna()].to_string(index=False))
# Kehadiran tidak punya rumus untuk dihitung ulang, jadi barisnya dibuang.
bersih = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
bersih["kehadiran_persen"] = bersih["kehadiran_persen"].astype(int)
print("\nBaris setelah baris kosong dibuang :", len(bersih))
print("Rentang kehadiran :",
bersih["kehadiran_persen"].min(), "-", bersih["kehadiran_persen"].max())Output yang Dihasilkan:
Tipe kolom kehadiran setelah konversi : float64
Baris tanpa kehadiran : 1
Baris yang kehilangan kehadiran:
siswa kelas jam_belajar_mingguan kehadiran_persen nilai_ujian
Uma B 9 NaN 65
Baris setelah baris kosong dibuang : 30
Rentang kehadiran : 58 - 96Mari kita bedah keputusan di balik langkah ini:
replace("-", pd.NA)lalupd.to_numericmengubah tanda-menjadi penanda kosong resmi, lalu mengonversi kolom menjadi angka. Tipe kolom menjadifloat64— bukan karena datanya pecahan, melainkan karenaNaNhanya bisa tinggal di kolom numerik bertipe pecahan.- Baris Uma adalah satu-satunya yang kosong — nama, kelas, jam belajar, dan nilainya lengkap; hanya kehadiran yang hilang. Inilah titik keputusan: mengisi atau membuang.
- Membuang dipilih karena tidak ada dasar hitung — pada proyek penjualan kita berani mengisi sel kosong karena ada rumus (gelas = pendapatan dibagi harga satuan). Untuk kehadiran tidak ada rumus serupa: kehadiran tiap siswa tidak bisa dihitung dari kolom lain. Mengisinya dengan rata-rata kelas berarti menambahkan angka buatan yang ikut dihitung dalam korelasi P2 — rekayasa kecil yang bisa menggeser hasil. Dengan 1 baris dari 31 (sekitar 3%), membuang adalah kerugian informasi terkecil.
.astype(int)mengembalikan kolom ke bilangan bulat setelah tidak ada lagiNaN— sisa 30 baris kini sepenuhnya numerik dengan rentang 58 sampai 96 persen.
Fungsi muat_bersih dan Verifikasi Ulang #
Pekerjaan pembersihan tidak selesai sebelum diperiksa ulang. Kita rangkum seluruh proses menjadi satu fungsi muat_bersih — bentuk yang sama akan kita salin ke tiap blok pada bagian-bagian berikutnya — lalu jalankan seluruh pemeriksaan Bagian 4 sekali lagi terhadap hasilnya:
import pandas as pd
def muat_bersih(path):
"""Muat data mentah dan terapkan seluruh langkah pembersihan."""
df = pd.read_csv(path).drop_duplicates().reset_index(drop=True)
df["kelas"] = df["kelas"].str.upper()
df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
df = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
df["kehadiran_persen"] = df["kehadiran_persen"].astype(int)
return df
bersih = muat_bersih("../data/nilai_siswa.csv")
print("Ukuran data bersih :", bersih.shape)
print("Duplikat tersisa :", bersih.duplicated().sum())
print("Kehadiran kosong :", bersih["kehadiran_persen"].isna().sum())
print("Nilai unik kelas :", sorted(bersih["kelas"].unique()))
print("Siswa per kelas :",
bersih["kelas"].value_counts().sort_index().to_dict())
print("Rentang nilai ujian :", bersih["nilai_ujian"].min(), "-",
bersih["nilai_ujian"].max())
print("Rata-rata nilai :", round(bersih["nilai_ujian"].mean(), 2))Output yang Dihasilkan:
Ukuran data bersih : (30, 5)
Duplikat tersisa : 0
Kehadiran kosong : 0
Nilai unik kelas : ['A', 'B']
Siswa per kelas : {'A': 15, 'B': 15}
Rentang nilai ujian : 44 - 91
Rata-rata nilai : 68.73Kita baca hasil verifikasi tersebut:
- Seluruh pemeriksaan nol — tidak ada duplikat, tidak ada kehadiran kosong, dan nilai unik kelas tinggal dua. Ukuran
(30, 5)persis sesuai rencana: 30 siswa, 5 kolom. - Kedua kelas seimbang 15 berbanding 15 — komposisi ini pantas dicatat karena membuat perbandingan antar kelas pada P3 lebih sederhana; kelompok yang seimbang membuat uji-t lebih mudah ditafsirkan.
- Rata-rata nilai 68,73 menjadi angka pembanding untuk bagian-bagian berikutnya. Berkat fungsi
muat_bersih, angka yang sama akan muncul setiap kali data bersih dimuat ulang — inilah arti pembersihan yang bisa direproduksi.
Data bersih sudah berdiri di atas fondasi yang teruji. Sekarang saatnya mengajukan pertanyaan pertama kali kepada datanya: apa cerita yang tersimpan di dalam 30 baris ini? Kita mulai menjelajah pada Bagian 6: Eksplorasi Data .