Kursus Python / Membersihkan dan merangkum data tabel dengan pandas

pandas mengolah data berbentuk baris dan kolom melalui DataFrame. Untuk laporan yang dapat ditafsirkan, pisahkan pembacaan, validasi, agregasi, dan penyimpanan, lalu laporkan jumlah data yang diterima maupun ditolak. Total yang tampak benar tidak membuktikan seluruh masukan telah diterima.

Memilih tipe kolom dan menyiapkan pandas

DataFrame adalah struktur seperti tabel, sedangkan Series mewakili satu kolom. pandas dapat menebak tipe saat membaca CSV, tetapi tebakan itu belum tentu cocok dengan kontrak aplikasi. ID seperti 0012 harus dibaca sebagai string agar nol di depan tidak hilang. Konversikan hanya kolom jumlah setelah aturan validasinya ditentukan.

pandas merupakan paket pihak ketiga, bukan pustaka standar. Aktifkan lingkungan virtual proyek dan pasang rilis yang kompatibel dengan Python serta sistem operasi Anda:

python -m pip install pandas

Jalankan pemasangan dengan interpreter yang juga akan menjalankan contoh. Jika impor gagal setelah pemasangan, periksa lingkungan virtual dan lokasi interpreter sebelum memasang ulang pada instalasi lain. Contoh ini diperiksa pada Python 3.12 dengan pandas 3.1.1; tampilan tipe internal dapat berbeda pada rilis lain.

Menjalankan validasi dan ringkasan lengkap

Buat folder latihan terpisah dan simpan seluruh kode berikut sebagai pandas_demo.py dengan UTF-8. Jalankan python pandas_demo.py pada terminal lingkungan proyek. Gunakan python3 sebagai nama perintah jika itulah interpreter yang dipakai di lingkungan Anda.

from io import StringIO
import pandas as pd

raw = "id,category,amount\ne1,food,3500\ne2,food,8000\ne3,transport,2500\ne4,food,bad\n"
df = pd.read_csv(StringIO(raw), dtype="string", keep_default_na=False)
amounts = pd.to_numeric(df["amount"], errors="coerce")
valid = amounts.notna() & (amounts >= 0) & (amounts % 1 == 0)
clean = df.loc[valid].copy()
clean["amount"] = amounts.loc[valid].astype("int64")
report = clean.groupby("category", as_index=False, sort=True)["amount"].sum()
print("accepted:", len(clean), "rejected:", int((~valid).sum()))
for row in report.itertuples(index=False):
    print(row.category, row.amount)

Hasil yang diharapkan:

accepted: 3 rejected: 1
food 11500
transport 2500

Empat record dibaca sebagai string, termasuk jumlah. to_numeric mengonversi kolom jumlah; errors="coerce" menandai kegagalan konversi sebagai nilai hilang. Mask valid menerima nilai yang tidak hilang, nonnegatif, dan tidak memiliki bagian pecahan. Teks bad ditolak, sehingga tersisa tiga record.

groupby menggabungkan jumlah menurut kategori. Dua pengeluaran food menghasilkan 11500 dan transport menghasilkan 2500. Pengaturan as_index=False mempertahankan kategori sebagai kolom biasa, sedangkan sort=True membuat urutan kategori konsisten. Jumlah keseluruhan adalah 14000.

Memahami nilai hilang, mask, dan batas validasi

Nilai hilang bukan nilai nol. Mengisi semua jumlah hilang dengan 0 akan menggabungkan “tidak ada jumlah” dengan “jumlahnya memang nol” dan dapat menyembunyikan kesalahan data. Gunakan mask untuk memisahkan record diterima dan ditolak, simpan alasan yang dibutuhkan aplikasi, serta cocokkan jumlah keduanya dengan seluruh record sumber.

df.loc[valid].copy() membuat tabel pilihan yang dipakai untuk penugasan kolom jumlah tanpa mengubah kolom string pada df. Setelah konversi, nilai pada clean menjadi integer, sementara tabel asli tetap tersedia untuk pemeriksaan masukan. Ini bukan klaim bahwa seluruh objek Python bertingkat selalu disalin secara rekursif.

Mask ini hanya memeriksa angka. ID kosong, kategori kosong, atau ID ganda belum ditolak, sehingga jangan menyebut clean sebagai data yang telah lolos seluruh aturan bisnis. Jika data memerlukan identitas unik dan laporan alasan penolakan, tambahkan pemeriksaan tersebut atau gunakan proyek CSV dengan validasi per record.

Contoh mengasumsikan jumlah kecil yang aman dalam int64. Konversi angka sangat besar dapat kehilangan presisi atau gagal saat diubah ke integer; agregasi tipe berukuran tetap juga memiliki batas. Teks 3.0 dapat diterima sebagai nilai integer 3, sedangkan 3.5 ditolak oleh mask. Jika kontraknya harus digit ASCII saja atau uang desimal tepat, tentukan kebijakan format, rentang, dan presisi tersendiri sebelum agregasi.

Untuk memakai berkas nyata, ganti pembentukan DataFrame dengan pd.read_csv("expenses-input.csv", dtype={"id":"string", "category":"string", "amount":"string"}). Perhatikan bahwa pemanggilan itu menggunakan pengenalan nilai hilang bawaan, sedangkan contoh StringIO menetapkan keep_default_na=False. Tentukan secara sadar apakah teks seperti NA adalah isi kategori yang sah atau penanda hilang. Rincian tersedia dalam read_csv dan to_numeric.

Menyimpan laporan dan merekonsiliasi hasil

Setelah report terbentuk, tambahkan baris berikut pada akhir file:

report.to_csv("category-report.csv", index=False, encoding="utf-8")

Baris ini membuat category-report.csv pada folder kerja. index=False mencegah indeks DataFrame ikut disimpan sebagai kolom tambahan. Pemanggilan bawaan menimpa file yang sudah ada, sehingga gunakan lokasi keluaran baru untuk latihan dan simpan laporan lama jika masih diperlukan. Perilakunya dijelaskan dalam DataFrame.to_csv.

  1. Jalankan data asli dan cocokkan tiga record diterima ditambah satu ditolak dengan empat record masukan. Buka CSV keluaran dan periksa kolom category serta amount.
  2. Ganti hanya teks bad dalam data menjadi 0. Jalankan pada folder keluaran baru: empat record diterima, tidak ada penolakan, dan total tetap 14000.
  3. Ganti nilai itu menjadi -1. Tiga record diterima dan satu ditolak; total tetap 14000.
  4. Kembalikan data awal dan ulangi rekonsiliasi. Jangan hanya memeriksa grafik atau tabel ringkasan; periksa jumlah record dan kebijakan penolakannya juga.

Untuk memeriksa hasil lebih lanjut, bandingkan total kolom amount pada record diterima dengan total pada laporan. Kesamaan 14000 berlaku untuk data contoh ini, sedangkan perubahan data yang sah harus diikuti nilai harapan yang sesuai. Pilih pandas ketika operasi tabel memang membantu; modul CSV standar tetap memadai untuk banyak pemrosesan per record yang sederhana.