MA1420 · DATA SAINS · SESI 07

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.

💡 ILUSTRASI — TANGGAL SEBAGAI "BONGKAHAN INFORMASI"

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.

ANATOMI SEBUAH TIMESTAMP
"2024-08-17 08:30:45"
2024 → Tahun 08 → Agustus 17 → Hari 08 → Jam 30 → Menit 45 → Detik

Atribut yang bisa diekstrak dari satu tanggal:

.year2024
.month8 (Agustus)
.day17
.dayofweek5 (Sabtu)
.dayofyear230
.quarter3 (Q3)
.week33 (minggu ke-33)
.hour8

Format Tanggal yang Sering Ditemui di Dataset Indonesia

Format StringContohKode Format (strftime)
ISO 8601 (standar)2024-08-17%Y-%m-%d
Format Indonesia umum17/08/2024%d/%m/%Y
Format panjang17 Agustus 2024%d %B %Y (locale id)
Dengan waktu2024-08-17 08:30:00%Y-%m-%d %H:%M:%S
Excel serial number45520pd.to_datetime(origin='1899-12-30')
PYTHON · PENANGANAN DATETIME
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']])
📤 OUTPUT
    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.

💡 ILUSTRASI — ABSENSI KELAS

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.

VISUALISASI MISSING VALUES DALAM DATASET
nama
umur
kota
gaji
pendidikan
Andi
25
Jakarta
5.5jt
S1
Budi
28
NaN ⚠
7.2jt
S1
Citra
NaN ⚠
Bandung
NaN ⚠
D3
Dewi
31
Surabaya
9.0jt
NaN ⚠
Eko
24
Medan
NaN ⚠
NaN ⚠

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)

MCAR
Missing Completely At Random
Data hilang secara acak murni — tidak berkaitan dengan nilai data manapun, baik yang hilang maupun yang ada.
📌 Contoh: Surveyor lupa mengisi kolom secara acak. Imputasi apa pun relatif aman.
MAR
Missing At Random
Data hilang berkaitan dengan variabel lain yang terobservasi, bukan dengan nilai yang hilang itu sendiri.
📌 Contoh: Laki-laki cenderung tidak mengisi kolom "penghasilan" → berkaitan dengan jenis kelamin (terobservasi).
MNAR
Missing Not At Random
Data hilang berkaitan langsung dengan nilai yang hilang itu sendiri. Paling berbahaya — bias sistematis.
📌 Contoh: Orang bergaji tinggi sengaja tidak mengisi kolom gaji → nilainya (tinggi) penyebab ia missing.
⚠️ MNAR PALING BERBAHAYA

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

METODE
KAPAN DIGUNAKAN
PERHATIKAN
Hapus baris
Missing rate kecil (<5%), mekanisme MCAR
Bisa mengurangi sampel signifikan jika missing banyak
Isi Mean
Data numerik, distribusi simetris, MCAR/MAR
Mengecilkan variansi; tidak cocok untuk data skewed
Isi Median
Data numerik dengan outlier atau distribusi miring
Lebih robust dari mean; tetap mengecilkan variansi
Isi Modus
Data kategorikal (nominal/ordinal)
Memperbesar dominasi kategori yang paling sering muncul
Forward Fill
Data time series — nilai sebelumnya dianggap masih berlaku
Hanya masuk akal jika data berurutan waktu
KNN / Model
Missing rate tinggi, hubungan antar variabel kompleks
Kompleks, mahal komputasi; perlu evaluasi kualitas imputasi

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.

💡 ILUSTRASI — DAFTAR NAMA SISWA DI EXCEL

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.

.str.lower() / .upper()
Standarisasi kapitalisasi — wajib sebelum pencocokan atau grouping.
"JAKARTA" → "jakarta"
.str.strip()
Hapus spasi di awal dan akhir string. Sering jadi penyebab duplikat tersembunyi.
" Andi " → "Andi"
.str.replace()
Ganti pola teks atau karakter tertentu, mendukung regex untuk pola kompleks.
"Rp 5.000" → "5000"
.str.contains()
Filter baris berdasarkan apakah kolom mengandung substring tertentu.
filter baris "Jakarta Selatan"
.str.split()
Pecah string berdasarkan pemisah. Berguna untuk memisahkan nama depan-belakang, atau kode-deskripsi.
"DKI Jakarta" → ["DKI","Jakarta"]
.str.extract()
Ekstrak bagian teks menggunakan regex. Cocok untuk memisahkan angka dari teks campuran.
"NIM: 2021001" → "2021001"

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.

TEKNIK 01 — NOMINAL
One-Hot Encoding (OHE)
Untuk data nominal (tanpa urutan). Buat kolom biner (0/1) baru untuk setiap kategori.
Kota: Jakarta, Bandung, Surabaya → kota_Jakarta: 1 0 0 → kota_Bandung: 0 1 0 → kota_Surabaya: 0 0 1
✓ Tidak ada urutan implisit; model tidak "mengira" Bandung > Jakarta
✗ Dimensi meledak jika banyak kategori unik (high cardinality)
TEKNIK 02 — ORDINAL
Label Encoding / Ordinal Encoding
Untuk data ordinal (ada urutan). Petakan setiap kategori ke integer yang mencerminkan urutannya.
Grade: D=0, C=1, B=2, A=3 Pendidikan: SD=1, SMP=2, SMA=3, S1=4 Ukuran: S=1, M=2, L=3, XL=4
✓ Hemat dimensi; urutan terjaga
✗ Jangan pakai untuk nominal! Model akan mengira "Jakarta=0 < Bandung=1"
💡 ATURAN PRAKTIS MEMILIH ENCODING

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.

PYTHON · DETEKSI & BERSIHKAN TIPE CAMPURAN
# 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)
📤 OUTPUT
=== 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

PYTHON · PIPELINE MISSING VALUES
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()}")
PYTHON · ONE-HOT ENCODING
# 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)
📤 OUTPUT
=== 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

🧩 PERTANYAAN 1 — MISSING MECHANISM
Dalam survei penghasilan, orang yang berpenghasilan sangat tinggi cenderung mengosongkan kolom "gaji bulanan". Ini termasuk mekanisme missing apa?
Benar! Ini adalah MNAR (Missing Not At Random) — orang tidak mengisi gaji karena gajinya tinggi. Nilai yang hilang itu sendiri (gaji tinggi) adalah penyebab missingnya. Ini paling berbahaya karena imputasi rata-rata akan meremehkan rata-rata penghasilan sesungguhnya.
🧩 PERTANYAAN 2 — ENCODING
Kolom "kota_asal" berisi 50 kota berbeda di Indonesia dan akan digunakan dalam model machine learning. Encoding apa yang PALING TEPAT?
Benar! Dengan 50 kategori unik (high cardinality), OHE akan membuat 49–50 kolom baru — membuat dimensi membengkak dan model mungkin overfit. Label Encoding salah karena kota adalah nominal (tidak ada urutan). Untuk high cardinality nominal, gunakan Target Encoding, Frequency Encoding, atau embedding.
🧩 PERTANYAAN 3 — DATETIME
Kolom "tanggal_transaksi" disimpan sebagai string "17/08/2024". Manakah perintah Pandas yang BENAR untuk mengkonversinya ke datetime?
Benar! Format "17/08/2024" adalah %d/%m/%Y (hari/bulan/tahun). Tanpa parameter format, pd.to_datetime() mungkin salah menginterpretasikan sebagai bulan/hari/tahun (format Amerika) → "17" akan dianggap bulan ke-17 dan error. Selalu tentukan format eksplisit untuk mencegah ambiguitas.
📋 Ringkasan Sesi 7