Bagian 1 berakhir dengan daftar enam masalah pada indikator_kesehatan.csv: enam baris duplikat, 12 label provinsi tak seragam, lima sel '-' yang mengubah tipe kolom dokter, sembilan persen kosong, empat persen di luar rentang 0-100, dan dua nilai jumlah negatif. Data dalam keadaan seperti ini belum boleh dipakai menuntaskan pertanyaan analisis proyek, karena satu persen 118.9 saja sudah merusak rata-rata dan peringkat. Pada bagian ini kita membersihkan keenam masalah itu dalam lima langkah yang alasannya jelas, memvalidasi hasilnya terhadap rentang yang sah, lalu mengemas seluruh proses menjadi fungsi bersihkan() yang dipakai Bagian 3 dan Bagian 4. Pendekatannya adalah penerapan langsung materi section Data Cleaning pada dataset proyek.
Urutan Langkah Pembersihan #
Urutan langkah dirancang agar tiap langkah menyederhanakan langkah berikutnya.
drop_duplicates()menghapus enam salinan persis sehingga kembali 102 baris — 34 provinsi kali 3 tahun — sesuai materi Menangani Nilai Duplikat.- Seragamkan label provinsi dengan
str.strip().str.lower().map(NAMA_BAKU). Kita sengaja tidak memakaistr.title(): nama sepertiDI Yogyakartajustru berubah menjadiDi Yogyakarta. Peta ke nama baku lebih aman dan menjadi pelengkap chapter Standardisasi Label Kategori. pd.to_numeric(errors="coerce")mengembalikan tipejumlah_dokterke numerik; lima sel'-'menjadi NaN, pola dari chapter Memperbaiki Tipe Data.mask()nilai di luar rentang: jumlah negatif dan persen di bawah 0 atau di atas 100 tidak mungkin benar, sehingga ditandai NaN alih-alih dipotong — keputusan gaya Validasi Rentang Nilai.- Isi NaN dengan median provinsi itu sendiri lewat
groupby(...).transform("median"). Setiap provinsi punya tiga tahun observasi, sehingga tahun-tahun lainnya menjadi pembanding alami; pola ini dari chapter transform() pada GroupBy dan Mengisi Nilai Kosong.
Langkah terakhir adalah sort_values(["kode_provinsi", "tahun"]) supaya hasil akhir terurut rapi dan siap dibandingkan.
Implementasi Langkah demi Langkah #
Skrip berikut memuat ulang dataset mentah dari Bagian 1, menjalankan kelima langkah sambil mencetak efeknya, memvalidasi rentang, lalu membandingkan hasilnya dengan fungsi bersihkan() pada buat_dataset.py.
"""Membersihkan indikator_kesehatan.csv langkah demi langkah sampai valid."""
from pathlib import Path
import tempfile
import pandas as pd
from pandas.testing import assert_frame_equal
from buat_dataset import NAMA_BAKU, bersihkan, buat_dataset
if __name__ == "__main__":
with tempfile.TemporaryDirectory() as folder:
berkas = Path(folder) / "indikator_kesehatan.csv"
buat_dataset().to_csv(berkas, index=False)
mentah = pd.read_csv(berkas, dtype={"kode_provinsi": "str"})
print(f"data mentah : {len(mentah)} baris, duplikat {mentah.duplicated().sum()}")
df = mentah.drop_duplicates().reset_index(drop=True)
print(f"1) drop_duplicates -> {len(df)} baris")
df["nama_provinsi"] = df["nama_provinsi"].str.strip().str.lower().map(NAMA_BAKU)
print(
f"2) map NAMA_BAKU -> {df['nama_provinsi'].nunique()} nama baku, "
f"NaN {df['nama_provinsi'].isna().sum()}"
)
df["jumlah_dokter"] = pd.to_numeric(df["jumlah_dokter"], errors="coerce")
print(
f"3) to_numeric coerce -> dtype {df['jumlah_dokter'].dtype}, "
f"NaN {df['jumlah_dokter'].isna().sum()}"
)
df["jumlah_puskesmas"] = df["jumlah_puskesmas"].mask(df["jumlah_puskesmas"] < 0)
df["jumlah_dokter"] = df["jumlah_dokter"].mask(df["jumlah_dokter"] < 0)
df["persen_akses_air_bersih"] = df["persen_akses_air_bersih"].mask(
(df["persen_akses_air_bersih"] < 0) | (df["persen_akses_air_bersih"] > 100)
)
print(
"4) mask luar rentang -> NaN persen "
f"{df['persen_akses_air_bersih'].isna().sum()}, "
f"puskesmas {df['jumlah_puskesmas'].isna().sum()}, "
f"dokter {df['jumlah_dokter'].isna().sum()}"
)
for kolom in ["jumlah_puskesmas", "jumlah_dokter", "persen_akses_air_bersih"]:
median = df.groupby("nama_provinsi")[kolom].transform("median")
df[kolom] = df[kolom].fillna(median)
df["jumlah_puskesmas"] = df["jumlah_puskesmas"].round().astype("int64")
df["jumlah_dokter"] = df["jumlah_dokter"].round().astype("int64")
df["persen_akses_air_bersih"] = df["persen_akses_air_bersih"].round(1)
print(f"5) isi median provinsi -> total NaN {df.isna().sum().sum()}")
df = df.sort_values(["kode_provinsi", "tahun"]).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 (df[["jumlah_puskesmas", "jumlah_rumah_sakit", "jumlah_dokter"]] >= 0).all().all()
assert df["persen_akses_air_bersih"].between(0, 100).all()
assert (df["nama_provinsi"].value_counts() == 3).all()
print("validasi rentang & kelengkapan: lolos")
assert_frame_equal(df, bersihkan(mentah))
print("validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identik")Output yang Dihasilkan:
data mentah : 108 baris, duplikat 6
1) drop_duplicates -> 102 baris
2) map NAMA_BAKU -> 34 nama baku, NaN 0
3) to_numeric coerce -> dtype float64, NaN 5
4) mask luar rentang -> NaN persen 13, puskesmas 1, dokter 6
5) isi median provinsi -> total NaN 0
hasil akhir: 102 baris x 7 kolom, duplikat 0, NaN total 0
validasi rentang & kelengkapan: lolos
validasi: hasil langkah manual == bersihkan() pada buat_dataset.py -> identikMari kita telaah:
drop_duplicates(): jejak barisnya jelas — 108 mentah menjadi 102, tepat enam salinan dari daftar Bagian 1.map(NAMA_BAKU): hasilnya 34 nama baku dengan NaN 0, bukti semua varian label adalah penulisan ulang dari nama yang sah, bukan provinsi baru.mask()dan penghitungan NaN: persen kini punya 13 NaN — sembilan kosong bawaan ditambah empat di luar rentang — sementara dokter punya 6 (lima'-'plus satu nilai negatif) dan puskesmas 1.transform("median"): median dihitung per provinsi atas nilai yang tersisa, lalufillna()mengisi persis sel yang kosong; jumlah bulat kemudian dikembalikan keint64.assertrentang dan kelengkapan: seluruh jumlah tak negatif, persen berada dalambetween(0, 100), dan tiap provinsi tepat tiga baris.
Hasilnya terlihat pada nilai konkret: persen Sulawesi Barat 2022 yang tertulis 118.9 kini 61.9, dan Lampung 2022 yang tertulis -5.2 kini 67.6 — keduanya median provinsinya sendiri.
Fungsi bersihkan() dan Uji Dua Jalur #
Modul buat_dataset.py di folder ini kini membawa fungsi bersihkan() yang mengulang kelima langkah tanpa cetakan. Baris terakhir skrip menjalankan assert_frame_equal(df, bersihkan(mentah)): hasil langkah manual dibandingkan sel-per-sel dengan fungsi kemasan, dan keduanya identik. Pola uji dua jalur ini Anda kenal dari chapter assert_frame_equal() pada section Pengujian dan Debugging, dan dipakai juga oleh proyek Analisis Data Cuaca — bukti bahwa polanya layak dibawa ke proyek mana pun.
Dataset 102 baris kini bersih, bertipe benar, dan lolos uji rentang. Bagian 3 akhirnya mulai bereksplorasi: statistik per kolom, hubungan antarkolom, dan peringkat provinsi.
Sumber bacaan resmi: pandas.DataFrame.drop_duplicates , pandas.to_numeric , dan pandas.testing.assert_frame_equal .