Tahap akhir sebuah alur analisis biasanya adalah model: prediksi total penjualan dari jumlah dan jenis produk, atau klasifikasi pelanggan dari perilaku belanja. scikit-learn adalah library machine learning standar Python, dan sejak awal library ini dirancang menerima DataFrame pandas sebagai input. Keuntungan besaranya terasa saat model selesai dilatih: nama kolom DataFrame tersimpan di dalam model, sehingga kita bisa melacak fitur mana yang dipakai. Pada Chapter ini kita menyusun pipeline prediksi total penjualan yang menggabungkan kolom kategori dan kolom numerik dalam satu alur.

Kontrak DataFrame dan scikit-learn #

Konvensi scikit-learn memisahkan dua peran: X berisi kolom fitur (prediktor) dan y berisi kolom target (nilai yang diprediksi). Keduanya diambil langsung dari DataFrame dengan seleksi kolom biasa — tidak perlu konversi ke array manual:

IstilahArtinya di pandasDi contoh kita
fitur (feature)kolom-kolom input, disimpan di Xkota, produk, jumlah
target (label)satu kolom output, disimpan di ytotal
estimatorobjek yang punya fit() dan predict()LinearRegression
transformerobjek yang punya fit() dan transform()OneHotEncoder

Manfaat memberi X berupa DataFrame (bukan ndarray) adalah atribut feature_names_in_: setelah fit(), model mengingat nama-nama kolomnya, dan peringatan “fitur tidak cocok” muncul bila urutan kolom berubah saat prediksi.

Menyiapkan Fitur dan Melatih Pipeline #

Kolom kota dan produk bertipe teks sehingga harus di-encode; kolom jumlah numerik boleh lewat begitu saja. ColumnTransformer menjalankan perlakuan berbeda per kolom, dan make_pipeline menyambungkannya ke model dalam satu objek:

python
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import OneHotEncoder

penjualan = pd.DataFrame({
    "kota": ["Jakarta", "Jakarta", "Jakarta", "Jakarta",
             "Bandung", "Bandung", "Bandung", "Bandung",
             "Surabaya", "Surabaya", "Surabaya", "Surabaya"],
    "produk": ["Beras", "Minyak", "Gula", "Kopi",
               "Beras", "Minyak", "Gula", "Kopi",
               "Beras", "Minyak", "Gula", "Kopi"],
    "jumlah": [40, 25, 30, 20, 32, 22, 27, 18, 36, 19, 25, 21],
    "harga": [68000, 35000, 18000, 26000, 67500, 34500, 17500, 25500, 67000, 34000, 17200, 25000],
})
penjualan["total"] = penjualan["jumlah"] * penjualan["harga"]

fitur = ["kota", "produk", "jumlah"]
X = penjualan[fitur]
y = penjualan["total"]

X_latih, X_uji, y_latih, y_uji = train_test_split(
    X, y, test_size=0.25, random_state=42
)
print("baris latih:", len(X_latih), "| baris uji:", len(X_uji))
print()

preprocessor = ColumnTransformer([
    ("kategori", OneHotEncoder(drop="first"), ["kota", "produk"]),
    ("angka", "passthrough", ["jumlah"]),
])
model = make_pipeline(preprocessor, LinearRegression())
model.fit(X_latih, y_latih)

print("feature_names_in_     :", model.feature_names_in_.tolist())
print("get_feature_names_out :", preprocessor.get_feature_names_out().tolist())
print()

prediksi = model.predict(X_uji)
print("prediksi:", np.round(prediksi, 0).astype(int).tolist())
print("aktual  :", y_uji.tolist())
print("R^2 uji :", round(model.score(X_uji, y_uji), 3))

Output yang Dihasilkan:

text
baris latih: 9 | baris uji: 3

feature_names_in_     : ['kota', 'produk', 'jumlah']
get_feature_names_out : ['kategori__kota_Jakarta', 'kategori__kota_Surabaya', 'kategori__produk_Gula', 'kategori__produk_Kopi', 'kategori__produk_Minyak', 'angka__jumlah']

prediksi: [77650, 295550, 2778100]
aktual  : [430000, 646000, 2720000]
R^2 uji : 0.922

Mari kita telaah baris-baris pentingnya:

  1. Baris 21-23 (fitur = [...], X = ..., y = ...): pemisahan fitur dan target dilakukan dengan seleksi kolom pandas murni; X tetap DataFrame sehingga nama kolom ikut terbawa.
  2. Baris 25-27 (train_test_split(...)): 12 baris diacak menjadi 9 baris latih dan 3 baris uji; argumen random_state=42 membuat hasil acakan tetap sama setiap kali skrip dijalankan.
  3. Baris 31-34 (ColumnTransformer([...])): transformator bernama kategori menerapkan OneHotEncoder(drop="first") pada kota dan produk, sementara angka meneruskan jumlah tanpa diubah ("passthrough").
  4. Baris 35-36 (make_pipeline(...), model.fit(...)): pipeline menyambungkan preprocessor dan LinearRegression; satu panggilan fit() meng-latih seluruh rantai pada data latih.
  5. Baris 38-39: feature_names_in_ membuktikan DataFrame yang melatih model, dan get_feature_names_out() menampilkan enam kolom hasil transformasi: dua dummy kota, tiga dummy produk, dan satu kolom jumlah.
  6. Baris 42-45 (model.predict(X_uji)): prediksi dilakukan langsung terhadap DataFrame uji; perbandingan prediksi dengan aktual lalu skor R^2 mengukur ketepatan model pada data yang belum pernah dilihat.

Cara membaca hasil: model memprediksi baris uji cukup dekat (misalnya 2.778.100 untuk nilai aktual 2.720.000) meski belum persis, terlihat dari R² 0,922 pada tiga baris uji. Ketidaktepatan itu wajar — hubungan total = jumlah × harga bersifat interaktif, sedangkan regresi linear murni aditif. Untuk data latih yang jauh lebih besar, pola pipeline yang sama persis inilah yang dipakai, tinggal mengganti estimatornya.

Model sudah mampu memprediksi dari tabel yang hidup di memori. Pertanyaan berikutnya adalah menyimpan dan mengambil tabel itu sendiri dari tempat yang lebih permanen — database — dan itulah peran SQLAlchemy pada Chapter berikutnya.

Referensi resmi untuk pendalaman: dokumentasi scikit-learn dan panduan dasar-dasar pandas .