Tipe Data II:
Penanganan Tipe Data Khusus
Dataset nyata jarang "bersih" — penuh tanggal dalam format aneh, teks kotor, nilai yang hilang (missing values), dan kategori yang perlu dikodekan sebelum bisa dianalisis. Sesi ini membekali kita dengan teknik praktis penanganannya.
1. Data Tanggal dan Waktu (Datetime)
Data tanggal/waktu adalah salah satu tipe data yang paling kaya informasi sekaligus paling sering salah ditangani. Disimpan sebagai string, kita kehilangan kemampuan menghitung selisih hari, mengekstrak bulan, atau membuat time series.
Sebuah tanggal seperti "2024-03-15 09:30:00" bukan sekadar teks — di dalamnya tersimpan: tahun, kuartal, bulan, minggu ke berapa, hari dalam seminggu, jam, apakah akhir pekan, dan masih banyak lagi.
Menyimpannya sebagai string seperti menyimpan berlian di plastik kresek biasa — isinya ada, tapi Anda tidak bisa mengaksesnya dengan mudah. Konversi ke datetime64 = menyimpan berlian di kotak khusus berisi semua slot yang tepat.
Atribut yang bisa diekstrak dari satu tanggal:
Format Tanggal yang Sering Ditemui di Dataset Indonesia
| Format String | Contoh | Kode Format (strftime) |
|---|---|---|
| ISO 8601 (standar) | 2024-08-17 | %Y-%m-%d |
| Format Indonesia umum | 17/08/2024 | %d/%m/%Y |
| Format panjang | 17 Agustus 2024 | %d %B %Y (locale id) |
| Dengan waktu | 2024-08-17 08:30:00 | %Y-%m-%d %H:%M:%S |
| Excel serial number | 45520 | pd.to_datetime(origin='1899-12-30') |
import pandas as pd
import numpy as np
# Tanggal dalam berbagai format
df = pd.DataFrame({
'nama' : ['Andi', 'Budi', 'Citra', 'Dewi'],
'tgl_lahir' : ['17/08/2002', '05/03/2001', '22/11/2003', '01/07/2002'],
'tgl_masuk' : ['2021-09-01', '2021-09-01', '2022-02-14', '2021-09-01'],
'jam_login' : ['2024-10-01 08:30', '2024-10-01 13:15',
'2024-10-02 09:00', '2024-10-01 20:45']
})
# Konversi dengan format eksplisit
df['tgl_lahir'] = pd.to_datetime(df['tgl_lahir'], format='%d/%m/%Y')
df['tgl_masuk'] = pd.to_datetime(df['tgl_masuk'])
df['jam_login'] = pd.to_datetime(df['jam_login'])
# Ekstraksi komponen tanggal
today = pd.Timestamp('2024-10-15')
df['umur'] = ((today - df['tgl_lahir']).dt.days / 365.25).astype(int)
df['bulan_masuk'] = df['tgl_masuk'].dt.month_name()
df['hari_login'] = df['jam_login'].dt.day_name()
df['jam_digit'] = df['jam_login'].dt.hour
df['sesi_login'] = df['jam_digit'].apply(
lambda h: 'Pagi' if h < 12 else ('Siang' if h < 17 else 'Malam')
)
print(df[['nama','umur','bulan_masuk','hari_login','sesi_login']])
nama umur bulan_masuk hari_login sesi_login
0 Andi 22 September Tuesday Pagi
1 Budi 23 September Tuesday Siang
2 Citra 20 February Wednesday Pagi
3 Dewi 22 September Tuesday Malam
2. Missing Values: Memahami Data yang Hilang
Hampir setiap dataset nyata memiliki missing values (NaN, None, null). Cara menanganinya sangat mempengaruhi kualitas analisis.
Bayangkan daftar nilai ujian kelas. Beberapa mahasiswa kolom nilainya kosong. Ada berbagai alasan: tidak hadir ujian (missing purposeful), lembar jawaban hilang (missing random), atau mahasiswa sakit parah sehingga hampir semua kolomnya kosong (missing karena kondisi khusus).
Alasan yang berbeda memerlukan penanganan yang berbeda. Mengisi nilai kosong karena tidak hadir dengan rata-rata kelas adalah salah — seharusnya nilainya memang 0 atau perlu diperlakukan khusus.
5 NaN dari 25 sel = 20% missing rate. Distribusi missing tidak merata — kolom "gaji" dan "pendidikan" paling banyak missing.
3. Tiga Mekanisme Missing Data (Rubin, 1976)
MNAR tidak bisa "diperbaiki" dengan imputasi biasa. Mengisi rata-rata pada kasus MNAR justru membuat estimasi bias. Solusinya: kumpulkan data yang hilang, atau gunakan model yang mengakui bahwa data hilang bukan secara acak.
4. Strategi Penanganan Missing Values
5. Penanganan Data Teks (String)
Data teks mentah sering penuh masalah: spasi berlebih, huruf besar-kecil tidak konsisten, karakter spesial, atau format yang tidak seragam. Pembersihan teks adalah langkah wajib sebelum analisis lebih lanjut.
Bayangkan guru menerima daftar nama dari berbagai sumber: " andi ", "BUDI", "citra_dewi", "Eko S.". Secara teknis ini 4 nama berbeda — padahal mungkin ada duplikat yang tidak terdeteksi karena format tidak konsisten.
Pembersihan teks = menyamakan format sehingga "andi", " Andi ", dan "ANDI" dikenali sebagai orang yang sama.
6. Encoding Data Kategorikal untuk Analisis & ML
Model machine learning hanya bisa bekerja dengan angka. Data kategorikal perlu dikonversi (di-encode) sebelum dimasukkan ke model. Ada dua teknik utama.
• Nominal (tanpa urutan) → One-Hot Encoding, kecuali kategori sangat banyak (gunakan Target Encoding)
• Ordinal (ada urutan) → Ordinal/Label Encoding dengan mapping manual yang benar
• Binary (2 kategori) → Cukup 1 kolom binary (0/1): L=0, P=1
• Saat OHE: gunakan drop_first=True untuk menghindari dummy variable trap (multikolinearitas)
7. Menangani Kolom dengan Tipe Data Campuran
Di dunia nyata, kolom yang seharusnya numerik sering mengandung teks, atau kolom teks mengandung angka tersembunyi. Ini menimbulkan dtype object pada kolom yang seharusnya numerik.
# Data nyata sering kotor seperti ini
df_kotor = pd.DataFrame({
'nama' : ['Andi', 'Budi', 'Citra', 'Dewi', 'Eko'],
'gaji' : ['5.500.000', '7.200.000', 'N/A', '9.000.000', '-'],
'kota' : [' jakarta', 'BANDUNG', 'Surabaya', ' surabaya ', 'Medan'],
'grade' : ['A', 'b', 'B', 'a', 'C']
})
print("=== DATA SEBELUM DIBERSIHKAN ===")
print(df_kotor)
# ── Bersihkan kolom gaji ────────────────────────────
df_kotor['gaji_bersih'] = (
df_kotor['gaji']
.str.replace('.', '', regex=False) # hapus titik ribuan
.str.replace('[^0-9]', '', regex=True) # hapus non-angka
.replace('', np.nan) # string kosong → NaN
.astype('float') # konversi ke numerik
)
# ── Bersihkan kolom kota ────────────────────────────
df_kotor['kota_bersih'] = (
df_kotor['kota']
.str.strip() # hapus spasi tepi
.str.title() # Huruf Kapital Tiap Kata
)
# ── Standarisasi grade ke huruf kapital ─────────────
df_kotor['grade_bersih'] = df_kotor['grade'].str.upper()
print("\n=== DATA SETELAH DIBERSIHKAN ===")
hasil = df_kotor[['nama','gaji_bersih','kota_bersih','grade_bersih']]
print(hasil)
print("\nDtype setelah bersih:")
print(hasil.dtypes)
=== DATA SEBELUM DIBERSIHKAN ===
nama gaji kota grade
0 Andi 5.500.000 jakarta A
1 Budi 7.200.000 BANDUNG b
2 Citra N/A Surabaya B
3 Dewi 9.000.000 surabaya a
4 Eko - Medan C
=== DATA SETELAH DIBERSIHKAN ===
nama gaji_bersih kota_bersih grade_bersih
0 Andi 5500000.0 Jakarta A
1 Budi 7200000.0 Bandung B
2 Citra NaN Surabaya B ← N/A → NaN dengan benar
3 Dewi 9000000.0 Surabaya A ← duplikat kota kini konsisten
4 Eko NaN Medan C ← '-' → NaN dengan benar
Dtype setelah bersih:
gaji_bersih float64 ✓
kota_bersih object
grade_bersih object
8. Praktik: Pipeline Lengkap Pembersihan Tipe Data
from sklearn.impute import SimpleImputer
# Dataset dengan missing values
np.random.seed(42)
n = 20
df_mv = pd.DataFrame({
'usia' : np.where(np.random.random(n) < 0.15, np.nan,
np.random.randint(20, 50, n).astype(float)),
'gaji' : np.where(np.random.random(n) < 0.20, np.nan,
np.random.normal(6e6, 1.5e6, n)),
'kota' : np.where(np.random.random(n) < 0.10, np.nan,
np.random.choice(['Jakarta','Bandung','Surabaya'], n))
})
# Laporan missing
missing_report = pd.DataFrame({
'Missing Count' : df_mv.isnull().sum(),
'Missing Pct (%)': (df_mv.isnull().sum() / len(df_mv) * 100).round(1)
})
print("=== LAPORAN MISSING VALUES ===")
print(missing_report)
# Strategi: numerik → median, kategorikal → modus
imp_num = SimpleImputer(strategy='median')
imp_cat = SimpleImputer(strategy='most_frequent')
df_mv[['usia','gaji']] = imp_num.fit_transform(df_mv[['usia','gaji']])
df_mv[['kota']] = imp_cat.fit_transform(df_mv[['kota']])
print("\n=== SETELAH IMPUTASI ===")
print(f"Total NaN tersisa: {df_mv.isnull().sum().sum()}")
# One-Hot Encoding dengan pandas
df_enc = pd.DataFrame({
'kota' : ['Jakarta', 'Bandung', 'Jakarta', 'Surabaya', 'Bandung'],
'grade' : ['A', 'B', 'A', 'C', 'B'],
'ipk' : [3.8, 3.4, 3.9, 2.9, 3.5]
})
# OHE untuk nominal (kota): drop_first hindari multikolinearitas
df_ohe = pd.get_dummies(df_enc, columns=['kota'],
drop_first=True, dtype=int)
# Ordinal Encoding untuk grade
grade_map = {'D':0, 'C':1, 'B':2, 'A':3}
df_ohe['grade_num'] = df_ohe['grade'].map(grade_map)
df_ohe = df_ohe.drop(columns=['grade'])
print("=== HASIL ENCODING ===")
print(df_ohe)
=== HASIL ENCODING ===
ipk kota_Jakarta kota_Surabaya grade_num
0 3.8 1 0 3 ← Jakarta + A
1 3.4 0 0 2 ← Bandung + B (referensi, drop_first)
2 3.9 1 0 3 ← Jakarta + A
3 2.9 0 1 1 ← Surabaya + C
4 3.5 0 0 2 ← Bandung + B
Uji Pemahaman Sesi 7
- Data tanggal/waktu disimpan sebagai datetime64 di Pandas — bukan string. Gunakan pd.to_datetime() dengan parameter format eksplisit
- Dari satu datetime bisa diekstrak: tahun, bulan, hari, hari dalam seminggu, kuartal, jam, dll. via atribut .dt
- Missing values perlu dipahami mekanismenya: MCAR (acak murni), MAR (berkaitan variabel lain), MNAR (berkaitan nilai itu sendiri — paling berbahaya)
- Strategi penanganan: hapus baris (missing kecil), isi mean/median (numerik), modus (kategorikal), forward fill (time series), KNN (kompleks)
- Pembersihan teks: .str.lower(), .strip(), .replace(), .title() — wajib sebelum grouping atau analisis
- One-Hot Encoding untuk nominal (tanpa urutan) — buat kolom biner per kategori; gunakan drop_first=True
- Ordinal Encoding untuk ordinal (ada urutan) — petakan ke integer dengan mapping manual yang tepat
- High cardinality nominal (banyak kategori unik) → hindari OHE, pertimbangkan Target/Frequency Encoding
- Tipe campuran: bersihkan dulu dengan str.replace() + regex, lalu konversi ke dtype yang tepat