Korelasi -0,996 pada chapter sebelumnya memberi tahu kita arah dan kekuatan hubungan harga promosi dengan penjualan, tetapi keputusan bisnis hampir selalu bertanya “berapa”: berapa gelas tambahan yang terjual bila harga turun 1.000 rupiah, dan berapa perkiraan penjualan bila pekan depan kopi susu dijual di harga 25 ribu. Regresi linear menjawab pertanyaan itu dengan satu garis lurus yang paling dekat dengan seluruh titik data. Pada chapter ini kita menurunkan garis tersebut dari data promo 14 hari yang sama, memakainya untuk memprediksi, mengukur kesalahannya lewat residual dan R², lalu membaca koefisiennya sebagai efek marginal.

Garis Terbaik dengan Metode Kuadrat Terkecil #

Regresi linear sederhana memasangkan satu variabel bebas x dengan satu variabel target y melalui persamaan garis y = a + b*x. Intersep a menandai posisi garis saat x = 0, sedangkan kemiringan b menyatakan perubahan y untuk tiap kenaikan x satu satuan. Dari sekian banyak garis yang bisa digambar pada scatter plot, metode kuadrat terkecil (least squares) memilih garis yang meminimalkan jumlah kuadrat selisih antara nilai teramati dan nilai di garis:

text
y = a + b * x
kuadrat terkecil: pilih a dan b yang meminimalkan Σ (y - ŷ)²

Pengkuadratan menghilangkan tanda positif-negatif sekaligus menghukum prediksi yang meleset jauh lebih berat daripada yang meleset tipis. Kita terapkan pada data promo kopi susu Toko Kopi Kita yang pernah kita pakai di analisis bivariat:

python
import numpy as np
import pandas as pd

promo = pd.DataFrame({
    "harga_promo": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 22, 24, 26],
    "gelas_terjual": [42, 40, 37, 35, 33, 30, 28, 26, 23, 21, 19, 31, 27, 24],
})

b, a = np.polyfit(promo["harga_promo"], promo["gelas_terjual"], 1)
print("Intersep a   : %.2f gelas" % a)
print("Kemiringan b : %.2f gelas per ribu rupiah" % b)
print("Korelasi r   : %.3f" % promo["harga_promo"].corr(promo["gelas_terjual"]))

Output yang Dihasilkan:

text
Intersep a   : 83.17 gelas
Kemiringan b : -2.30 gelas per ribu rupiah
Korelasi r   : -0.996

Mari kita telaah baris-baris kode di atas:

  1. np.polyfit(x, y, 1) mencari koefisien polinomial derajat 1 — garis lurus — yang meminimalkan kuadrat residual. Fungsi ini mengembalikan koefisien dari derajat tertinggi lebih dulu, sehingga urutan pembongkarannya adalah b, a: kemiringan baru, intersep kemudian.
  2. %.2f dan %.3f memformat angka menjadi dua dan tiga angka desimal agar laporan terbaca rapi.
  3. Korelasi r dicetak ulang untuk menegaskan hubungan kedua alat: korelasi dari Analisis Korelasi menjawab “seberapa erat dua variabel bergerak bersama”, sedangkan garis regresi menjawab “berapa perubahan y tiap satu satuan x”. Angka yang sama, dua bentuk jawaban.

Pembacaan garisnya: setiap kenaikan harga 1.000 rupiah berkaitan dengan penurunan penjualan rata-rata 2,30 gelas. Intersep 83,17 gelas adalah taksiran penjualan saat harga nol — nilai di luar jangkauan data yang tidak boleh ditafsirkan sebagai ramalan, sebagaimana akan kita lihat pada bagian ekstrapolasi.

Prediksi dan Residual #

Setelah garis diperoleh, persamaannya menjadi mesin prediksi: masukkan harga, keluar taksiran gelas. Kualitas garis kita ukur lewat residual — selisih nilai teramati terhadap taksiran garis, residual = y - ŷ:

python
import numpy as np
import pandas as pd

promo = pd.DataFrame({
    "harga_promo": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 22, 24, 26],
    "gelas_terjual": [42, 40, 37, 35, 33, 30, 28, 26, 23, 21, 19, 31, 27, 24],
})

b, a = np.polyfit(promo["harga_promo"], promo["gelas_terjual"], 1)

def prediksi_gelas(harga):
    return a + b * harga

for harga in [22, 25, 30]:
    print("Harga %2d ribu -> taksiran %.1f gelas" % (harga, prediksi_gelas(harga)))

promo["prediksi"] = a + b * promo["harga_promo"]
promo["residual"] = promo["gelas_terjual"] - promo["prediksi"]
print()
print(promo.round(2))

Output yang Dihasilkan:

text
Harga 22 ribu -> taksiran 32.5 gelas
Harga 25 ribu -> taksiran 25.6 gelas
Harga 30 ribu -> taksiran 14.1 gelas

    harga_promo  gelas_terjual  prediksi  residual
0            18             42     41.72      0.28
1            19             40     39.42      0.58
2            20             37     37.12     -0.12
3            21             35     34.81      0.19
4            22             33     32.51      0.49
5            23             30     30.21     -0.21
6            24             28     27.91      0.09
7            25             26     25.60      0.40
8            26             23     23.30     -0.30
9            27             21     21.00      0.00
10           28             19     18.69      0.31
11           22             31     32.51     -1.51
12           24             27     27.91     -0.91
13           26             24     23.30      0.70

Tiga hal perlu dibaca dari keluaran tersebut:

  1. prediksi_gelas(harga) hanya mengalikan harga dengan kemiringan lalu menambah intersep — persis persamaan garis. Harga 25 ribu ditaksir terjual 25,6 gelas.
  2. Residual yang kecil dan acak menandakan garis cocok: seluruhnya bergerak di kisaran -1,51 sampai 0,70 gelas. Baris berindeks 11 (harga 22, terjual 31) memiliki residual terbesar; hari itu penjualan 1,51 gelas di bawah taksiran garis — kandidat anomali yang layak ditelusuri, misalnya hujan atau pesanan borongan.
  3. Harga 30 berada di luar rentang data 18–28 ribu. Taksiran 14,1 gelas itu hasil ekstrapolasi, bukan interpolasi.

R²: Seberapa Banyak Variasi yang Dijelaskan #

Residual memberi intuisi visual, tetapi laporan membutuhkan satu angka ringkas. R² membandingkan variasi yang disisakan garis terhadap variasi total data:

text
R² = 1 - SS_res / SS_tot
SS_res = Σ (y - ŷ)²   variasi yang tidak dijelaskan garis
SS_tot = Σ (y - mean(y))²   variasi total pada data

R² bernilai 0 sampai 1: makin dekat ke 1, makin besar bagian variasi y yang dijelaskan garis. Kita hitung kedua jumlah itu langsung dengan kode:

python
import numpy as np
import pandas as pd

promo = pd.DataFrame({
    "harga_promo": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 22, 24, 26],
    "gelas_terjual": [42, 40, 37, 35, 33, 30, 28, 26, 23, 21, 19, 31, 27, 24],
})

b, a = np.polyfit(promo["harga_promo"], promo["gelas_terjual"], 1)
prediksi = a + b * promo["harga_promo"]
residual = promo["gelas_terjual"] - prediksi

ss_res = (residual ** 2).sum()
ss_tot = ((promo["gelas_terjual"] - promo["gelas_terjual"].mean()) ** 2).sum()
r2 = 1 - ss_res / ss_tot

print("SS_res (variasi sisa garis) : %.2f" % ss_res)
print("SS_tot (variasi total data) : %.2f" % ss_tot)
print("R^2 manual                  : %.4f" % r2)
print("r kuadrat dari korelasi     : %.4f" % promo["harga_promo"].corr(promo["gelas_terjual"]) ** 2)

Output yang Dihasilkan:

text
SS_res (variasi sisa garis) : 4.69
SS_tot (variasi total data) : 642.86
R^2 manual                  : 0.9927
r kuadrat dari korelasi     : 0.9927

Dua baris terakhir sengaja memuat jawaban yang sama lewat dua jalan. Variasi total penjualan selama 14 hari tercatat 642,86; garis menyisakan hanya 4,69 — garis harga menjelaskan 99,27% variasi penjualan. Pada regresi linear sederhana dengan satu variabel bebas, R² selalu sama dengan r kuadrat, sehingga korelasi -0,996 dan R² 0,9927 adalah dua potong informasi dari satu kenyataan yang sama. Sisa 0,73% adalah porsi faktor lain — cuaca, jam buka, pesaing sebelah — yang tidak ditangkap harga.

Membaca Koefisien sebagai Efek Marginal #

Angka-angka model baru berguna bila kita bisa menerjemahkannya ke bahasa bisnis. Tiga komponen hasil regresi dibaca sebagai berikut:

KomponenNilai kitaCara membacanya
Intersep a83,17 gelastaksiran y saat x = 0; di luar rentang data, cukup sebagai posisi garis
Kemiringan b-2,30 gelas per ribu rupiahefek marginal: perubahan y rata-rata tiap x naik satu satuan, faktor lain tetap
R²0,9927bagian variasi y yang dijelaskan garis

Efek marginal adalah bacaan yang paling sering dipakai: “setiap kenaikan harga 1.000 rupiah berkaitan dengan penurunan penjualan rata-rata 2,3 gelas”. Perhatikan dua disiplin pada kalimat itu. Pertama, satuan wajib disebut — koefisien -2,30 hanya bermakna jika kita tahu x dalam ribu rupiah dan y dalam gelas. Kedua, kata “berkaitan dengan”, bukan “menyebabkan”: garis ini dibangun dari data observasional, dan pernyataan sebab-akibat menuntut eksperimen terkendali sebagaimana kita latih pada Analisis Konfirmasi .

Rute Pustaka: scikit-learn dan statsmodels #

np.polyfit transparan untuk satu variabel bebas, tetapi analisis nyata memakai banyak fitur sekaligus. Untuk itu kita berpindah ke pustaka pemodelan: kelas LinearRegression dari scikit-learn menerima X berupa data frame multi-kolom dan mengembalikan koefisien dalam bentuk yang siap dipakai pipeline:

python
import pandas as pd
from sklearn.linear_model import LinearRegression

promo = pd.DataFrame({
    "harga_promo": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 22, 24, 26],
    "gelas_terjual": [42, 40, 37, 35, 33, 30, 28, 26, 23, 21, 19, 31, 27, 24],
})

X = promo[["harga_promo"]]
y = promo["gelas_terjual"]

model = LinearRegression()
model.fit(X, y)

print("Intersep    : %.2f gelas" % model.intercept_)
print("Kemiringan  : %.2f gelas per ribu rupiah" % model.coef_[0])
print("R^2 (score) : %.4f" % model.score(X, y))

Output yang Dihasilkan:

text
Intersep    : 83.17 gelas
Kemiringan  : -2.30 gelas per ribu rupiah
R^2 (score) : 0.9927

Tiga angka itu identik dengan perhitungan np.polyfit kita — pustaka memakai kuadrat terkecil yang sama, hanya kemasannya berbeda. Bedanya pada bentuk masukan: X harus dua dimensi (data frame dengan satu atau lebih kolom fitur), model.coef_ berbentuk larik karena jumlahnya mengikuti jumlah fitur, dan model.score() langsung mengembalikan R². Pustaka statsmodels melangkah lebih jauh dengan tabel ringkasan yang memuat standard error dan p-value tiap koefisien — perlengkapan uji formal yang akan sering kita pakai ketika regresi dipakai sebagai model machine learning di Section Supervised Learning .

Rangkuman #

  • Regresi linear sederhana mencari garis y = a + b*x dengan kuadrat terkecil: np.polyfit(x, y, 1) mengembalikan b (kemiringan) lebih dulu, baru a (intersep).
  • Persamaan menjadi mesin prediksi; residual y - ŷ mengukur selisih tiap titik terhadap garis, dan ekstrapolasi ke luar rentang data dilarang.
  • R² = 1 - SS_res/SS_tot mengukur bagian variasi yang dijelaskan garis; pada regresi sederhana nilainya sama dengan r kuadrat.
  • Kemiringan dibaca sebagai efek marginal dengan satuan yang eksplisit dan kata “berkaitan dengan”, bukan pernyataan sebab-akibat.
  • LinearRegression scikit-learn menghitung koefisien yang sama untuk masukan multi-fitur; statsmodels menambahkan tabel uji formal.

Sepanjang chapter ini target prediksi kita berupa angka: berapa gelas terjual. Pertanyaan bisnis yang sama sering menjawab hal yang bukan angka — pelanggan ini akan berhenti berlangganan atau tidak, transaksi ini penipuan atau bukan. Untuk memodelkan hasil biner semacam itu, garis lurus perlu diganti fungsi yang bekerja pada peluang: itulah tugas Regresi Logistik .

Referensi #

  1. scikit-learn — LinearRegression
  2. NumPy — The absolute basics for beginners