Pada chapter pertama kita melihat gejala aneh: kolom yang tampak berisi angka ternyata bertipe object, sehingga penjumlahannya merekatkan teks alih-alih menjumlahkan. Tipe yang salah adalah masalah paling diam-diam di data cleaning — operasi bisa gagal dengan galat yang jelas, tetapi bisa juga “berhasil” dengan hasil yang salah sepenuhnya. Pada chapter ini kita memperbaiki tiga keluarga kasus: angka berkala yang tersimpan sebagai teks, konversi yang menabrak nilai rusak, dan kolom label yang lebih tepat diperlakukan sebagai kategori.
Tipe Data yang Dipakai pandas #
Sebelum memperbaiki, kita perlu tipe apa yang tersedia. pandas menyimpan setiap kolom dengan satu tipe; keenam tipe berikut mencakup hampir semua kebutuhan data tabular.
| Tipe | Menyimpan | Contoh kolom toko |
|---|---|---|
int64 | Bilangan bulat | jumlah cangkir terjual |
float64 | Desimal — satu-satunya tipe angka yang bisa memuat NaN | persen diskon |
bool | True/False | penanda pelanggan member |
object | Teks atau campuran | menu, pelanggan |
category | Label berulang dari daftar terbatas | menu, porsi |
datetime64[ns] | Tanggal dan waktu | tanggal transaksi |
Kolom angka bertipe object hampir selalu tanda masalah — kecuali memang sengaja, seperti nomor telepon yang tidak akan dihitung. Sebaliknya, dua tipe terakhir sering tidak terpakai padahal cocok: category menghemat memori untuk kolom berulang, dan datetime64 membuka seluruh pintu analisis waktu yang kita bahas pada chapter Menangani Format Tanggal
.
Ketika Angka Tersimpan Sebagai Teks #
Daftar menu di bawah diekspor dari aplikasi kasir lama dengan dua kebiasaan penulisan Indonesia: harga dengan titik ribuan (24.000) dan persen diskon dengan koma desimal (12,5). Keduanya membuat kolom terbaca sebagai teks, dan penjumlahan teks menghasilkan bencana yang senyap.
import pandas as pd
menu = pd.DataFrame({
"menu": ["kopi susu", "matcha latte", "teh tawar", "americano"],
"harga": ["24.000", "28.000", "20.000", "22.000"],
"diskon": ["12,5", "0", "5,5", "10"],
})
print(menu)
print()
print(menu.dtypes)
print()
print("Hasil sum() kolom harga:", menu["harga"].sum())Output yang Dihasilkan:
menu harga diskon
0 kopi susu 24.000 12,5
1 matcha latte 28.000 0
2 teh tawar 20.000 5,5
3 americano 22.000 10
menu object
harga object
diskon object
dtype: object
Hasil sum() kolom harga: 24.00028.00020.00022.000Mari kita telaah hasilnya:
- Baris 9-11 (
print(menu),dtypes): tabelnya tampak sehat, tetapi ketiga kolom bertipeobject— harga dan diskon tersimpan sebagai teks, bukan angka. - Baris 13 (
menu["harga"].sum()): penjumlahan tidak gagal dan tidak memperingatkan; teks-teks justru direkatkan menjadi24.00028.00020.00022.000. Inilah bahaya tipe salah yang paling sukses menyamar: operasi berjalan, hasilnya tak bermakna.
Konversi Langsung dengan astype dan Jebakannya #
Metode astype mengubah tipe kolom sekaligus — cara tercepat bila isinya bersih. Tetapi pada data kita, konversi langsung menabrak dua jebakan berbeda: satu yang berbunyi keras, satu yang tidak.
import pandas as pd
menu = pd.DataFrame({
"menu": ["kopi susu", "matcha latte", "teh tawar", "americano"],
"harga": ["24.000", "28.000", "20.000", "22.000"],
"diskon": ["12,5", "0", "5,5", "10"],
})
try:
menu["diskon"].astype(float)
except ValueError as galat:
print("astype(float) pada kolom diskon gagal:", galat)
print()
hasil = menu["harga"].astype(float)
print("astype(float) pada kolom harga tidak mengeluarkan galat:")
print(hasil)Output yang Dihasilkan:
astype(float) pada kolom diskon gagal: could not convert string to float: '12,5'
astype(float) pada kolom harga tidak mengeluarkan galat:
0 24.0
1 28.0
2 20.0
3 22.0
Name: harga, dtype: float64Dua kelakuan yang berlawanan itu layak dibedah:
- Baris 8-11 (
astype(float)padadiskon): teks12,5ditolak keras denganValueError— Python tidak mengenal koma sebagai penanda desimal. Galat yang berbunyi keras itu sebenarnya membantu: kita langsung tahu ada masalah. - Baris 13-15 (
astype(float)padaharga): konversi berhasil tanpa keluhan — dan di situlah jebakannya. Titik pada24.000ditafsirkan sebagai penanda desimal, sehingga harga 24.000 rupiah berubah menjadi 24,0 rupiah. Nilainya melenceng seribu kali lipat tanpa satu pun peringatan.
Bersihkan Dulu, Konversi Kemudian #
Karena astype menuntut isi yang sudah bersih, urutan kerjanya jelas: rapikan formatnya dengan metode teks lebih dulu, baru konversi. Titik ribuan dibuang dengan str.replace; koma desimal diganti titik. Setelah itu pd.to_numeric mengubah kolom menjadi angka murni.
import pandas as pd
menu = pd.DataFrame({
"menu": ["kopi susu", "matcha latte", "teh tawar", "americano"],
"harga": ["24.000", "28.000", "20.000", "22.000"],
"diskon": ["12,5", "0", "5,5", "10"],
})
menu["harga"] = pd.to_numeric(menu["harga"].str.replace(".", "", regex=False))
menu["diskon"] = pd.to_numeric(menu["diskon"].str.replace(",", ".", regex=False))
print(menu)
print()
print(menu.dtypes)
print()
print("Total harga:", menu["harga"].sum())Output yang Dihasilkan:
menu harga diskon
0 kopi susu 24000 12.5
1 matcha latte 28000 0.0
2 teh tawar 20000 5.5
3 americano 22000 10.0
menu object
harga int64
diskon float64
dtype: object
Total harga: 94000Mari kita telaah dua baris yang melakukan seluruh pekerjaan:
- Baris 9 (
harga):str.replace(".", "", regex=False)membuang semua titik sehingga24.000menjadi"24000";pd.to_numericlalu menghasilkan bilangan bulat 24000 bertipeint64. Pemeriksaan silangnya mudah: total harga 94.000 harus sama dengan 24+28+20+22 ribu — dan memang demikian. - Baris 10 (
diskon): koma diganti titik ("12,5"menjadi"12.5"), laluto_numericmenghasilkan desimalfloat64. Tipe ini memang tepat karena diskon bisa berupa pecahan. - Urutan berpikirnya: kenali format -> rapikan dengan operasi teks -> konversi -> verifikasi hasil. Bila berkas aslinya masih ada, kita bahkan bisa melewati langkah manual ini:
pd.read_csv(..., thousands=".", decimal=",")menanganinya sejak pembacaan, seperti dijelaskan pada dokumentasipandas.read_csv.
Menjinakkan Nilai Gagal dengan errors=“coerce” #
Data nyata tidak sepenurut daftar menu kita. Kolom stok berikut memuat catatan manusia: kata habis dan tanda -. Konversi bawaan menolaknya keras; errors="coerce" mengubah nilai yang gagal menjadi NaN sehingga proses berlanjut — dengan catatan kita wajib mengaudit nilai apa saja yang gagal.
import pandas as pd
stok = pd.DataFrame({
"menu": ["kopi susu", "matcha latte", "teh tawar", "americano", "es kopi"],
"stok": ["40", "habis", "35", "-", "38"],
})
try:
pd.to_numeric(stok["stok"])
except ValueError as galat:
print("Tanpa errors='coerce':", galat)
print()
stok["stok"] = pd.to_numeric(stok["stok"], errors="coerce")
print(stok)
print()
print("Nilai yang gagal dikonversi:", stok["stok"].isna().sum())
print("Barisnya:", stok.loc[stok["stok"].isna(), "menu"].tolist())Output yang Dihasilkan:
Tanpa errors='coerce': Unable to parse string "habis" at position 1
menu stok
0 kopi susu 40.0
1 matcha latte NaN
2 teh tawar 35.0
3 americano NaN
4 es kopi 38.0
Nilai yang gagal dikonversi: 2
Barisnya: ['matcha latte', 'americano']Mari kita telaah alurnya:
- Baris 8-11 (
try/except): konversi bawaan berhenti di nilaihabisdenganValueError. Pada data ribuan baris, satu nilai rusak menghentikan seluruh proses — kadang memang diinginkan, kadang tidak. - Baris 13 (
errors="coerce"): nilai yang gagal dikonversi menjadiNaN—habisdan-berubah menjadi sel kosong, dan kolom akhirnya bertipe angka (float64, karena adaNaN). - Baris 16-17 (audit):
errors="coerce"tidak melaporkan apa pun secara otomatis, jadi kita hitung sendiri: dua nilai gagal, pada baris matcha latte dan americano. Tanpa audit ini,coercehanya memindahkan masalah dari “galat yang terlihat” ke “kekosongan yang tersembunyi” — yang kebetulan sudah kita pelajari cara menanganinya pada chapter Menangani Missing Values .
Kolom Kategori dan Urutannya #
Kolom label yang berulang — nama menu, porsi, kota — menempati pilihan tipe terakhir: category. Alih-alih menyimpan teks yang sama berulang kali, pandas menyimpan daftar label unik plus kode angka di tiap baris; hasilnya lebih hemat memori dan agregasi menjadi lebih cepat. Bonusnya, kategori bisa diberi urutan: kecil < sedang < besar bukan sekadar teks, melainkan tingkatan yang bisa diurutkan dan dibandingkan.
import pandas as pd
penjualan = pd.DataFrame({
"menu": ["kopi susu", "teh tawar", "kopi susu", "americano",
"kopi susu", "teh tawar", "americano", "matcha latte"],
"porsi": ["sedang", "besar", "kecil", "sedang",
"besar", "sedang", "kecil", "sedang"],
})
penjualan["menu"] = penjualan["menu"].astype("category")
print(penjualan["menu"].dtype)
print("Kategori:", penjualan["menu"].cat.categories.tolist())
print()
urutan = ["kecil", "sedang", "besar"]
penjualan["porsi"] = pd.Categorical(penjualan["porsi"], categories=urutan, ordered=True)
print("Porsi terkecil:", penjualan["porsi"].min())
print("Porsi terbesar:", penjualan["porsi"].max())
print()
print(penjualan.sort_values("porsi"))Output yang Dihasilkan:
category
Kategori: ['americano', 'kopi susu', 'matcha latte', 'teh tawar']
Porsi terkecil: kecil
Porsi terbesar: besar
menu porsi
2 kopi susu kecil
6 americano kecil
0 kopi susu sedang
3 americano sedang
5 teh tawar sedang
7 matcha latte sedang
1 teh tawar besar
4 kopi susu besarMari kita telaah hasilnya:
- Baris 12 (
astype("category")): kolommenuberubah tipe;cat.categoriesmemperlihatkan empat label uniknya — daftar ini yang bisa kita audit: satu nilai asing akan langsung terlihat sebagai kategori tambahan. - Baris 16-17 (
pd.Categorical(..., ordered=True)): kolomporsididefinisikan dengan urutan eksplisitkecil < sedang < besar. Setelah itumin()danmax()bermakna tingkatan, bukan urutan alfabet. - Baris 21 (
sort_values("porsi")): pengurutan mengikuti urutan kategori — seluruh bariskecilmendahuluisedang, lalubesar. Pada kolom teks biasa, pengurutan mengikuti alfabet danbesarakan mendahuluikecil.
Rangkuman #
- Tipe kolom menentukan operasi yang sah; kolom angka bertipe
objectadalah masalah yang menyamar, karena penjumlahannya merekatkan teks tanpa galat. astypemenuntut isi bersih: koma desimal ditolak keras (ValueError), sedangkan titik ribuan terlewat sebagai desimal dan mengubah nilai secara diam-diam.- Pola kerjanya: rapikan format dengan
str.replace-> konversi denganpd.to_numeric-> verifikasi hasil;pd.read_csv(thousands=".", decimal=",")menangkapnya lebih awal. errors="coerce"mengubah nilai gagal menjadiNaN; selalu audit nilai mana saja yang gagal denganisna().categorymenghemat memori untuk label berulang, danordered=Truememberi makna urutan pada tingkatan seperti porsi.
Tipe sudah benar, tetapi perhatikan kolom menu dan pelanggan pada data kita: spasi tersembunyi, kapital yang tak seragam, dan nilai gabungan masih membuat analisis bocor. Operasi teks pandas menjadi peralatan utama pada chapter berikutnya, Membersihkan Teks
.