Penskalaan pada dua chapter sebelumnya mengubah ukuran, bukan bentuk: sebaran yang miring ke kanan tetap miring setelah di-standardisasi. Padahal banyak metode — regresi linear dengan residual tertonjol kiri, uji-t, PCA — bekerja paling baik ketika sebaran mendekati simetris. Untungnya bentuk sebaran sendiri bisa kita ubah lewat transformasi distribusi. Pada chapter ini kita ukur kemiringan (skewness) secara resmi, luruskan sebaran dengan log1p, lalu serahkan pemilihan bentuk terbaik kepada Box-Cox dari SciPy.
Mengukur Kemiringan #
Kemiringan mengukur ketidakseimbangan sebaran: nol berarti simetris, positif berarti ekor menyeret ke kanan, negatif berarti ke kiri. Pendapatan adalah contoh klasik sebaran miring positif — mayoritas pelanggan kafe berpenghasilan beberapa juta rupiah, sementara segelintir pelanggan menembus puluhan juta. Kita pakai data pendapatan bulanan 16 pelanggan program loyalitas:
import numpy as np
import pandas as pd
from scipy import stats
pelanggan = pd.DataFrame({
"pendapatan_ribu": [1200, 1500, 1800, 2100, 2400, 2800, 3300, 3900,
4700, 5800, 7200, 9500, 13000, 19000, 32000, 68000],
})
pelanggan["log_pendapatan"] = np.log1p(pelanggan["pendapatan_ribu"])
skew_asli = stats.skew(pelanggan["pendapatan_ribu"])
skew_log = stats.skew(pelanggan["log_pendapatan"])
print("kemiringan asli :", round(skew_asli, 3))
print("kemiringan log1p:", round(skew_log, 3))
print()
print("statistik ringkas:")
print(pelanggan.describe().round(2))Output yang Dihasilkan:
kemiringan asli : 2.532
kemiringan log1p: 0.712
statistik ringkas:
pendapatan_ribu log_pendapatan
count 16.00 16.00
mean 11137.50 8.60
std 17208.83 1.15
min 1200.00 7.09
25% 2325.00 7.75
50% 4300.00 8.36
75% 10375.00 9.24
max 68000.00 11.13Mari kita baca hasilnya dengan cermat:
- Baris 10 (
np.log1p(...)): kolom pendapatan ditransformasi ke ranah log. Angka 68.000 yang semula 56 kali lipat dari 1.200 kini hanya berjarak 11,13 - 7,09 ≈ 4 satuan log. - Baris 12-13 (
stats.skew(...)): kemiringan dihitung lewat SciPy. Data asli mencatat 2,532 — miring kuat ke kanan; setelah log, angkanya turun ke 0,712, jauh lebih dekat ke simetris. - Tabel
describe(): perhatikan jarak antar kuartil. Pada kolom asli, kuartil ketiga (10.375) berjarak 6.075 dari median sementara kuartil pertama (2.325) hanya 1.975 — ekor kanan menyeret jauh. Pada kolom log, jarak kuartil ke median menjadi hampir seimbang (0,88 ke atas, 0,61 ke bawah), tanda sebaran yang jauh lebih rapi.
Perlu diingat bahwa log bukan obat segala kemiringan. Log sangat manjur untuk kemiringan positif dengan nilai tak negatif; untuk kemiringan negatif, cerita datar di batas atas (misalnya nilai ujian yang menumpuk di 100), atau data yang memuat nol dan nilai minus, bentuk transformasinya harus berbeda.
Box-Cox: Membiarkan Data Memilih Pangkat #
Log sebenarnya satu anggota dari keluarga transformasi pangkat: Box-Cox memakai parameter λ untuk memilih bentuknya secara otomatis — λ = 0 menyerupai log, λ = 1 nyaris tanpa perubahan, λ = 0,5 menjadi akar kuadrat, dan seterusnya. Fungsi stats.boxcox dari SciPy mengembalikan sekaligus data tertransformasi dan λ optimal yang ditemukannya:
import numpy as np
from scipy import stats
pendapatan = np.array([1200, 1500, 1800, 2100, 2400, 2800, 3300, 3900,
4700, 5800, 7200, 9500, 13000, 19000, 32000, 68000],
dtype=float)
terboxcox, lambda_optimal = stats.boxcox(pendapatan)
print("lambda optimal :", round(lambda_optimal, 4))
print("kemiringan asli :", round(stats.skew(pendapatan), 3))
print("kemiringan boxcox :", round(stats.skew(terboxcox), 3))
print("kemiringan log1p :", round(stats.skew(np.log1p(pendapatan)), 3))
print()
print("lima nilai pertama hasil boxcox:", terboxcox[:5].round(2))Output yang Dihasilkan:
lambda optimal : -0.3313
kemiringan asli : 2.532
kemiringan boxcox : 0.128
kemiringan log1p : 0.712
lima nilai pertama hasil boxcox: [2.73 2.75 2.77 2.78 2.79]Mari kita bedah maknanya:
- Baris 8 (
terboxcox, lambda_optimal = stats.boxcox(...)): satu pemanggilan mengembalikan dua hal — kolom tertransformasi dan nilai λ terbaik yang dipilih dengan metode maximum likelihood. - λ optimal -0,3313: data kita ternyata paling cocok dipangkatkan lebih lanjut dari log — pangkat negatif menekan ekor kanan lebih dalam. Nilai λ yang jauh dari 1 menegaskan bahwa data asli memang butuh transformasi kuat.
- Kemiringan 0,128: hasil akhirnya hampir simetris sempurna — lebih lurus daripada log1p (0,712) pada data yang sama. Perbandingan berdampingan inilah guna mencetak ketiga baris kemiringan sekaligus.
- Baris 14 (
terboxcox[:5]): cuplikan nilainya — bentuk baru yang tidak lagi berarti “rupiah”, tetapi siap dipakai model. Ingat pula praktik bab transformasi variabel: simpan λ ini agar data baru ditransformasi dengan pangkat yang sama.
Melihat Sebelum dan Sesudah #
Angka kemiringan meyakinkan, tetapi mata kita lebih cepat menangkap bentuk. Histogram berikut menampilkan sebaran pendapatan asli dan hasil log-nya secara berdampingan:
import matplotlib.pyplot as plt
import numpy as np
pendapatan = np.array([1200, 1500, 1800, 2100, 2400, 2800, 3300, 3900,
4700, 5800, 7200, 9500, 13000, 19000, 32000, 68000],
dtype=float)
fig, ax = plt.subplots(1, 2, figsize=(10, 4))
ax[0].hist(pendapatan, bins=8, color="#8c6bb1", edgecolor="black")
ax[0].set_title("Pendapatan asli (miring ke kanan)")
ax[0].set_xlabel("ribu rupiah")
ax[0].set_ylabel("jumlah pelanggan")
ax[1].hist(np.log1p(pendapatan), bins=8, color="#74c476", edgecolor="black")
ax[1].set_title("Setelah log1p (lebih seimbang)")
ax[1].set_xlabel("log1p(pendapatan)")
plt.tight_layout()
plt.show()Panel kiri menampilkan ciri khas sebaran miring kanan: satu batang tinggi menumpuk di ujung kiri (pendapatan kecil), lalu barisan batang makin cepat merosot hingga hampir rata dengan lantai di kanan — satu-dua pelanggan berpendapatan besar terpisah jauh dari gerombolannya. Panel kanan berubah wujud: batang-batang naik-turun mengelilingi pusat di sekitar 8-9, tanpa ekor panjang. Sebaran yang tadinya seperti papan luncur kini menyerupai bukit — persis perubahan yang ditunjukkan oleh kemiringan 2,532 menjadi 0,712.
Rangkuman #
- Kemiringan mengukur ketimpangan sebaran;
stats.skewmemberi angkanya — positif berarti ekor ke kanan, negatif ke kiri, nol berarti simetris. np.log1padalah obat pertama untuk kemiringan positif: aman untuk nol dan mudah dibalik dengannp.expm1. Pada data kita, kemiringan turun dari 2,532 ke 0,712.- Box-Cox memilih pangkat λ secara otomatis; pada data yang sama hasilnya lebih lurus lagi (0,128) dengan λ -0,3313 — tetapi hanya menerima nilai positif.
- Untuk data yang memuat nol atau minus, pakai varian Yeo-Johnson lewat
PowerTransformerscikit-learn. - Selalu simpan parameter transformasi (λ, atau fakta bahwa kolom adalah log1p) agar data baru diproses dengan bentuk yang sama.
Sejauh ini kita memperlakukan angka dan kategori. Satu tipe data lagi yang kaya informasi tetapi paling sering terabaikan: tanggal. Kolom tanggal yang dibiarkan sebagai teks menyembunyikan pola akhir pekan, musim, dan hari libur — kita bongkar semuanya pada chapter pembuatan fitur tanggal.