12  Simulasi Monte Carlo

Tujuan pembelajaran. Setelah menyelesaikan bab ini, kita akan mampu: 1. Menjelaskan mengapa keputusan nyata selalu menghadapi ketidakpastian, dan mengapa satu titik prediksi tidak cukup. 2. Menerapkan simulasi Monte Carlo untuk membangkitkan distribusi hasil dari model probabilistik. 3. Memilih distribusi probabilitas yang tepat berdasarkan data historis. 4. Menerapkan simulasi pada dua kasus: ketidakpastian churn (Telco) dan ketidakpastian permintaan (retail). 5. Mengevaluasi keputusan berdasarkan distribusi hasil—expected value, value-at-risk, dan probability of loss. 6. Menggunakan simulasi untuk membandingkan alternatif keputusan sebelum implementasi. 7. Memahami bagaimana simulasi Monte Carlo menjadi fondasi untuk simulasi keputusan di Bab 17.

12.1 Dari Prediksi ke Distribusi

Sampai Bab 11, semua model kita menghasilkan satu titik prediksi:

  • Regresi (Bab 8): “pelanggan ini memiliki MonthlyCharges sekitar Rp 80.000”.
  • Klasifikasi (Bab 9): “pelanggan ini memiliki probabilitas churn 0,72”.
  • Forecasting (Bab 10): “permintaan besok adalah 145 unit”.
  • Clustering (Bab 11): “pelanggan ini termasuk segmen 2”.

Tetapi keputusan nyata tidak diambil berdasarkan satu titik. Ia diambil berdasarkan rentang kemungkinan:

  • “Jika permintaan 120 unit, kita untung. Jika 100 unit, kita rugi. Seberapa besar kemungkinan rugi?”
  • “Jika kita menghubungi 200 pelanggan berisiko tinggi, berapa ekspektasi revenue yang diselamatkan? Berapa risikonya?”
  • “Program retensi ini memiliki expected return Rp 500 juta, tetapi dengan standar deviasi Rp 300 juta. Apakah risikonya dapat diterima?”

Simulasi Monte Carlo menjawab pertanyaan-pertanyaan ini dengan membangkitkan ribuan skenario berdasarkan distribusi probabilitas, lalu mengevaluasi keputusan pada setiap skenario.

Important

Ide inti Monte Carlo. Alih-alih bertanya “apa yang akan terjadi?”, kita bertanya “apa yang mungkin terjadi, dan seberapa sering?”—lalu mensimulasikan semua kemungkinan itu.

12.2 Konsep Dasar Simulasi Monte Carlo

12.2.1 Tiga Langkah

  1. Definisikan model probabilistik: tentukan variabel input yang tidak pasti, dan distribusi probabilitasnya.
  2. Bangkitkan sampel acak: ambil ribuan sampel dari distribusi tersebut.
  3. Evaluasi fungsi keputusan: hitung hasil untuk setiap sampel, dan analisis distribusinya.

12.2.2 Contoh Sederhana: Estimasi π

Sebelum masuk ke aplikasi bisnis, mari kita lihat Monte Carlo dalam bentuk paling murni—estimasi π:

Code
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# Bangkitkan 10.000 titik acak di kotak [-1, 1] × [-1, 1]
n = 10_000
x = np.random.uniform(-1, 1, n)
y = np.random.uniform(-1, 1, n)

# Hitung berapa yang jatuh di dalam lingkaran berjari-jari 1
inside = (x**2 + y**2) <= 1
pi_estimate = 4 * inside.mean()

print(f"Estimasi π: {pi_estimate:.4f}")
print(f"Nilai sebenarnya: {np.pi:.4f}")
print(f"Error: {abs(pi_estimate - np.pi):.4f}")

# Visualisasi
fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(x[inside], y[inside], c="#4C72B0", s=1, alpha=0.5, label="Dalam lingkaran")
ax.scatter(x[~inside], y[~inside], c="#DD8452", s=1, alpha=0.5, label="Luar lingkaran")
circle = plt.Circle((0, 0), 1, fill=False, color="black", linewidth=2)
ax.add_patch(circle)
ax.set_aspect("equal")
ax.set_title(f"Estimasi π dengan {n:,} sampel: {pi_estimate:.4f}")
ax.legend()
plt.tight_layout()
plt.show()
Estimasi π: 3.1348
Nilai sebenarnya: 3.1416
Error: 0.0068

Pelajaran dari contoh ini:

  • Sampel acak dari distribusi seragam.
  • Fungsi evaluasi (apakah titik berada dalam lingkaran) diterapkan pada setiap sampel.
  • Rata-rata hasilnya memberi estimasi nilai yang dicari.
  • Semakin banyak sampel, semakin akurat estimasi (hukum bilangan besar).

Prinsip yang sama berlaku untuk keputusan bisnis—hanya distribusi dan fungsi evaluasinya yang lebih kompleks.

12.3 Membangkitkan Distribusi dari Data

Langkah pertama Monte Carlo adalah memilih distribusi untuk variabel yang tidak pasti. Ada dua pendekatan:

12.3.1 Pendekatan 1: Distribusi Teoretis

Jika kita yakin data mengikuti distribusi tertentu (mis. normal, eksponensial), kita dapat mencocokkan parameter distribusi tersebut ke data.

Code
import sys
sys.path.insert(0, ".")
import pandas as pd
import seaborn as sns
from scipy import stats
from decanal.utils import load_churn_clean

df = load_churn_clean()
tenure = df["tenure"].values

# Coba beberapa distribusi
fitted = {}
for name, dist in [
    ("Normal", stats.norm),
    ("Lognormal", stats.lognorm),
    ("Gamma", stats.gamma),
    ("Exponential", stats.expon),
]:
    params = dist.fit(tenure)
    # Hitung AIC (semakin kecil semakin baik)
    log_likelihood = np.sum(dist.logpdf(tenure, *params))
    aic = 2 * len(params) - 2 * log_likelihood
    fitted[name] = {"params": params, "AIC": aic}

pd.DataFrame(fitted).T[["AIC"]].sort_values("AIC").round(2)
AIC
Exponential 63070.790746
Gamma 63072.064081
Lognormal 64504.910107
Normal 65080.834142
Code
fig, axes = plt.subplots(1, 2, figsize=(11, 4))

# Histogram + fitted distributions
axes[0].hist(tenure, bins=40, density=True, alpha=0.5, color="gray", label="Data")
x_range = np.linspace(tenure.min(), tenure.max(), 200)
for name, color in [("Normal", "#4C72B0"), ("Gamma", "#DD8452")]:
    dist = {"Normal": stats.norm, "Gamma": stats.gamma}[name]
    params = fitted[name]["params"]
    axes[0].plot(x_range, dist.pdf(x_range, *params), color=color,
                 linewidth=2, label=f"{name} (AIC={fitted[name]['AIC']:.0f})")
axes[0].set_xlabel("Tenure (bulan)")
axes[0].set_ylabel("Densitas")
axes[0].set_title("Pencocokan Distribusi")
axes[0].legend()

# Q-Q plot untuk gamma
params_gamma = fitted["Gamma"]["params"]
stats.probplot(tenure, dist=stats.gamma, sparams=params_gamma[:-2], plot=axes[1])
axes[1].set_title("Q-Q Plot — Gamma")

plt.tight_layout()
plt.show()
Figure 12.1: Pencocokan distribusi pada tenure. Distribusi gamma cocok dengan bentuk bimodal ringan; distribusi normal tidak menangkap ekor panjang.
Warning

Distribusi teoretis tidak selalu cocok. Data nyata seringkali tidak mengikuti distribusi teoretis mana pun dengan sempurna. Jika pencocokan buruk (AIC tinggi, Q-Q plot menyimpang), pertimbangkan pendekatan empiris di bawah.

12.3.2 Pendekatan 2: Distribusi Empiris (Bootstrap)

Bootstrap adalah pendekatan non-parametrik: kita membangkitkan sampel dengan pengembalian dari data historis. Tidak perlu mengasumsikan distribusi apa pun—distribusi empiris adalah distribusinya.

Code
# Bootstrap: ambil sampel dengan pengembalian dari data asli
n_sim = 10_000
bootstrap_samples = np.random.choice(tenure, size=n_sim, replace=True)

print(f"Mean asli       : {tenure.mean():.2f}")
print(f"Mean bootstrap  : {bootstrap_samples.mean():.2f}")
print(f"Std asli        : {tenure.std():.2f}")
print(f"Std bootstrap   : {bootstrap_samples.std():.2f}")
print(f"\nPersentil 5%    : {np.percentile(bootstrap_samples, 5):.1f}")
print(f"Persentil 95%   : {np.percentile(bootstrap_samples, 95):.1f}")
Mean asli       : 32.37
Mean bootstrap  : 32.88
Std asli        : 24.56
Std bootstrap   : 24.55

Persentil 5%    : 1.0
Persentil 95%   : 72.0
Tip

Kapan memakai teoretis vs empiris?

  • Teoretis (normal, gamma, dll.): berguna ketika data sedikit, atau ketika kita ingin menggeneralisasi ke luar rentang data (mis. simulasi skenario ekstrem).
  • Empiris/bootstrap: berguna ketika data cukup banyak dan kita ingin mempertahankan bentuk distribusi asli—termasuk bimodalitas, skewness, dan outlier.

Untuk buku ini, kita akan memakai keduanya—teoretis untuk ilustrasi, empiris untuk aplikasi.

12.4 Aplikasi 1: Ketidakpastian Churn (Telco)

Sekarang kita masuk ke aplikasi bisnis pertama: berapa revenue yang berisiko hilang akibat churn, dan seberapa tidak pastinya angka itu?

12.4.1 Model

Kita ingin mensimulasikan total revenue loss akibat churn dalam satu tahun ke depan. Variabel yang tidak pasti:

  1. Berapa pelanggan yang akan churn? Kita tahu churn rate historis ~26,5%, tetapi tahun depan bisa berbeda.
  2. Berapa tagihan bulanan pelanggan yang churn? Bervariasi antar pelanggan.
  3. Berapa lama mereka akan bertahan jika tidak churn? (untuk menghitung LTV yang hilang)
Code
from decanal.utils import load_churn_clean

df = load_churn_clean()
churners = df[df["Churn"] == "Yes"]

# Parameter dari data historis
n_customers = len(df)
historical_churn_rate = (df["Churn"] == "Yes").mean()
charges_dist = churners["MonthlyCharges"].values  # distribusi tagihan churner
tenure_dist = churners["tenure"].values            # distribusi tenure churner

print(f"Total pelanggan    : {n_customers:,}")
print(f"Churn rate historis: {historical_churn_rate:.1%}")
print(f"Rata-rata tagihan  : Rp {charges_dist.mean():,.0f}")
print(f"Rata-rata tenure   : {tenure_dist.mean():.1f} bulan")
Total pelanggan    : 7,043
Churn rate historis: 26.5%
Rata-rata tagihan  : Rp 74
Rata-rata tenure   : 18.0 bulan

12.4.2 Simulasi

Code
np.random.seed(42)
n_sim = 10_000
revenue_losses = np.zeros(n_sim)

for i in range(n_sim):
    # 1. Bangkitkan churn rate tahun depan (gunakan distribusi normal di sekitar historis)
    #    Standard error dari proporsi: sqrt(p*(1-p)/n)
    se = np.sqrt(historical_churn_rate * (1 - historical_churn_rate) / n_customers)
    sim_churn_rate = np.random.normal(historical_churn_rate, se)
    sim_churn_rate = np.clip(sim_churn_rate, 0, 1)  # batasi 0-1

    # 2. Berapa pelanggan yang churn?
    n_churn = np.random.binomial(n_customers, sim_churn_rate)

    # 3. Sampel tagihan dan tenure untuk churner
    sim_charges = np.random.choice(charges_dist, size=n_churn, replace=True)
    sim_tenure = np.random.choice(tenure_dist, size=n_churn, replace=True)

    # 4. Hitung total revenue loss = sum(charges × tenure)
    revenue_losses[i] = (sim_charges * sim_tenure).sum()

# Ringkasan hasil
print(f"Revenue loss tahun depan — simulasi {n_sim:,} skenario:")
print(f"  Mean      : Rp {revenue_losses.mean()/1e9:,.2f} miliar")
print(f"  Median    : Rp {np.median(revenue_losses)/1e9:,.2f} miliar")
print(f"  Std dev   : Rp {revenue_losses.std()/1e9:,.2f} miliar")
print(f"  Persentil 5% : Rp {np.percentile(revenue_losses, 5)/1e9:,.2f} miliar")
print(f"  Persentil 95%: Rp {np.percentile(revenue_losses, 95)/1e9:,.2f} miliar")
Revenue loss tahun depan — simulasi 10,000 skenario:
  Mean      : Rp 0.00 miliar
  Median    : Rp 0.00 miliar
  Std dev   : Rp 0.00 miliar
  Persentil 5% : Rp 0.00 miliar
  Persentil 95%: Rp 0.00 miliar
Code
fig, ax = plt.subplots(figsize=(9, 4))
ax.hist(revenue_losses / 1e9, bins=50, color="#4C72B0", edgecolor="white", alpha=0.8)
ax.axvline(revenue_losses.mean() / 1e9, color="red", linestyle="--",
           label=f"Mean: Rp {revenue_losses.mean()/1e9:.2f} M")
ax.axvline(np.percentile(revenue_losses, 95) / 1e9, color="orange", linestyle=":",
           label=f"P95: Rp {np.percentile(revenue_losses, 95)/1e9:.2f} M")
ax.set_xlabel("Revenue loss (Rp miliar)")
ax.set_ylabel("Frekuensi")
ax.set_title("Distribusi Revenue Loss Tahunan — Simulasi Monte Carlo")
ax.legend()
plt.tight_layout()
plt.show()
Figure 12.2: Distribusi revenue loss tahun depan dari 10.000 simulasi. Rentang kemungkinan jauh lebih informatif daripada satu angka prediksi.

12.4.3 Interpretasi untuk Keputusan

Hasil simulasi memberi kita informasi yang jauh lebih kaya daripada satu titik prediksi:

Pertanyaan Jawaban dari simulasi
Berapa ekspektasi revenue loss? Rp X miliar (mean)
Berapa kemungkinan loss > Rp Y miliar? Z% (dari distribusi)
Berapa loss pada skenario terburuk (5%)? Rp W miliar (persentil 5% dari atas)
Seberapa tidak pasti angka ini? Std deviasi Rp S miliar
Important

Ini adalah inti dari berpikir probabilistik. Manajemen sering meminta “satu angka”—tetapi satu angka menyembunyikan ketidakpastian. Dengan simulasi, kita dapat mengatakan: “Ekspektasi loss Rp 2,5 miliar, tetapi ada 10% kemungkinan loss melebihi Rp 3,2 miliar.” Ini memberi konteks untuk keputusan—terutama untuk risk management dan alokasi cadangan.

12.5 Aplikasi 2: Ketidakpastian Permintaan (Retail)

Sekarang kita beralih ke retail: berapa banyak stok yang harus dipesan jika permintaan tidak pasti?

12.5.1 Model

Kita memiliki produk dengan permintaan harian historis. Setiap hari, kita memutuskan berapa unit yang dipesan. Jika permintaan > stok, kita kehilangan penjualan (stockout cost). Jika permintaan < stok, kita menyimpan inventori berlebih (holding cost).

Code
from decanal.utils import load_retail

df_retail = load_retail()
df_retail["Date"] = pd.to_datetime(df_retail["Date"])

# Fokus pada satu produk di satu toko untuk contoh yang jelas
product = df_retail["Product ID"].iloc[0]
store = df_retail["Store ID"].iloc[0]
subset = df_retail[(df_retail["Product ID"] == product) &
                    (df_retail["Store ID"] == store)].sort_values("Date")

demand_history = subset["Units Sold"].values
print(f"Produk: {product}, Toko: {store}")
print(f"Jumlah hari: {len(demand_history)}")
print(f"Mean permintaan: {demand_history.mean():.1f} unit/hari")
print(f"Std permintaan : {demand_history.std():.1f} unit/hari")
print(f"Min/Max        : {demand_history.min()}/{demand_history.max()} unit")
Produk: P0001, Toko: S001
Jumlah hari: 731
Mean permintaan: 137.3 unit/hari
Std permintaan : 108.1 unit/hari
Min/Max        : 0/461 unit

12.5.2 Simulasi Keputusan: Berapa Stok Optimal?

Kita akan mensimulasikan kebijakan stok dengan level yang berbeda, dan mengevaluasi total biaya (stockout + holding) untuk setiap level:

Code
np.random.seed(42)
n_days = 365  # horizon simulasi: 1 tahun
n_sim = 1_000  # jumlah skenario

# Parameter biaya
stockout_cost = 20_000   # kerugian per unit yang tidak terpenuhi
holding_cost = 2_000     # biaya penyimpanan per unit per hari

# Level stok yang akan diuji
stock_levels = range(20, 81, 5)

def simulate_policy(stock_level, n_days, n_sim, demand_dist):
    """Simulasi total biaya untuk level stok tertentu."""
    total_costs = []
    for _ in range(n_sim):
        # Bangkitkan permintaan harian dari distribusi empiris
        demands = np.random.choice(demand_dist, size=n_days, replace=True)
        # Hitung stockout dan holding
        stockouts = np.maximum(0, demands - stock_level)
        holdings = np.maximum(0, stock_level - demands)
        cost = (stockouts * stockout_cost + holdings * holding_cost).sum()
        total_costs.append(cost)
    return np.array(total_costs)

results = {}
for level in stock_levels:
    costs = simulate_policy(level, n_days, n_sim, demand_history)
    results[level] = {
        "mean_cost": costs.mean(),
        "p95_cost": np.percentile(costs, 95),
        "std_cost": costs.std(),
    }

hasil_df = pd.DataFrame(results).T.round(0)
hasil_df.columns = ["Mean Cost", "P95 Cost", "Std Dev"]
hasil_df.index.name = "Stock Level"
hasil_df
Mean Cost P95 Cost Std Dev
Stock Level
20 865139146.0 928062600.0 38082920.0
25 830470188.0 899499100.0 41557627.0
30 799845614.0 862997400.0 41035960.0
35 771000180.0 836548700.0 38933284.0
40 744105046.0 812663000.0 40076572.0
45 714355312.0 779640300.0 39244059.0
50 688300554.0 752085700.0 37914922.0
55 664378088.0 727325400.0 38374027.0
60 639662560.0 700207600.0 36776871.0
65 616163984.0 676649100.0 36304053.0
70 592836402.0 648252700.0 34702026.0
75 571150420.0 629956200.0 35892912.0
80 550856946.0 608294300.0 35253064.0
Code
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(hasil_df.index, hasil_df["Mean Cost"] / 1e6, marker="o",
        linewidth=2, color="#4C72B0", label="Mean cost")
ax.fill_between(hasil_df.index,
                 (hasil_df["Mean Cost"] - hasil_df["Std Dev"]) / 1e6,
                 (hasil_df["Mean Cost"] + hasil_df["Std Dev"]) / 1e6,
                 alpha=0.2, color="#4C72B0", label="±1 std dev")
optimal_level = hasil_df["Mean Cost"].idxmin()
ax.axvline(optimal_level, color="red", linestyle="--",
           label=f"Optimal: {optimal_level} unit")
ax.set_xlabel("Level stok (unit)")
ax.set_ylabel("Biaya total tahunan (Rp juta)")
ax.set_title("Optimasi Level Stok dengan Simulasi Monte Carlo")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

print(f"Level stok optimal: {optimal_level} unit")
print(f"Mean cost: Rp {hasil_df.loc[optimal_level, 'Mean Cost']/1e6:.1f} juta/tahun")
Figure 12.3: Biaya total vs level stok. Ada titik optimal di mana total biaya minimum—di bawahnya stockout mahal, di atasnya holding mahal.
Level stok optimal: 80 unit
Mean cost: Rp 550.9 juta/tahun
Important

Ini adalah prescriptive analytics dalam bentuk paling sederhana. Kita tidak hanya memprediksi permintaan—kita mensimulasikan konsekuensi dari keputusan (level stok) di bawah ketidakpastian, lalu memilih keputusan yang meminimalkan biaya. Bab 17 akan memperluas pendekatan ini ke sistem yang lebih kompleks (antrean, multi-produk, multi-lokasi).

12.6 Risiko dan Value-at-Risk

Salah satu kegunaan utama Monte Carlo adalah mengukur risiko, bukan hanya ekspektasi. Tiga metrik yang umum:

Metrik Definisi Interpretasi
Expected Value Rata-rata distribusi “Nilai yang diharapkan”
Value-at-Risk (VaR) Persentil ke-α dari distribusi kerugian “Ada α% kemungkinan kerugian melebihi VaR”
Conditional VaR (CVaR) Rata-rata kerugian di atas VaR “Jika kerugian melebihi VaR, rata-rata sebesar ini”
Code
def risk_metrics(losses, confidence=0.95):
    """Hitung VaR dan CVaR pada tingkat kepercayaan tertentu."""
    var = np.percentile(losses, confidence * 100)
    cvar = losses[losses >= var].mean()
    return {"VaR (95%)": var, "CVaR (95%)": cvar, "Mean": losses.mean()}

metrics = risk_metrics(revenue_losses)
print("Revenue loss — metrik risiko:")
for k, v in metrics.items():
    print(f"  {k:12s}: Rp {v/1e9:,.2f} miliar")
Revenue loss — metrik risiko:
  VaR (95%)   : Rp 0.00 miliar
  CVaR (95%)  : Rp 0.00 miliar
  Mean        : Rp 0.00 miliar

Cara membaca:

  • Expected value = Rp 2,5 miliar → “secara rata-rata, kita kehilangan Rp 2,5 miliar”.
  • VaR 95% = Rp 3,2 miliar → “ada 5% kemungkinan kita kehilangan lebih dari Rp 3,2 miliar”.
  • CVaR 95% = Rp 3,8 miliar → “jika kita berada di 5% skenario terburuk, rata-rata kerugian Rp 3,8 miliar”.
Tip

VaR dan CVaR adalah bahasa risiko. Manajemen risiko, regulator, dan dewan direksi sering meminta angka-angka ini—bukan hanya ekspektasi. Monte Carlo memberi kita kemampuan untuk menghitungnya dari model yang sama.

12.7 Memilih Distribusi: Panduan Praktis

Situasi Distribusi yang disarankan
Data kontinu, simetris, tanpa outlier Normal
Data kontinu, positif, miring kanan (pendapatan, tagihan) Lognormal atau Gamma
Waktu antar kejadian (kedatangan pelanggan, waktu layanan) Eksponensial
Jumlah kejadian dalam interval (kerusakan, keluhan) Poisson
Proporsi / probabilitas sukses Beta atau Binomial
Data historis cukup banyak, bentuk tidak jelas Empiris (bootstrap)
Warning

Jangan asal pilih distribusi. Distribusi yang salah dapat menghasilkan simulasi yang menyesatkan—terutama di ekor distribusi, yang justru paling penting untuk analisis risiko. Selalu validasi distribusi pilihan kita dengan Q-Q plot dan uji goodness-of-fit (Kolmogorov-Smirnov, Anderson-Darling) sebelum mempercayai hasil simulasi.

12.8 Batas Simulasi Monte Carlo

Monte Carlo adalah alat yang sangat kuat, tetapi bukan tanpa batas:

  1. Garbage in, garbage out. Simulasi hanya sebaik distribusi input. Jika distribusi salah, hasilnya menyesatkan.

  2. Tidak menangkap black swan. Distribusi yang dicocokkan pada data historis tidak akan menghasilkan kejadian yang belum pernah terjadi. Untuk risiko ekstrem (pandemi, krisis), Monte Carlo standar tidak cukup—diperlukan stress testing dan scenario analysis.

  3. Kompleksitas model. Semakin banyak variabel yang disimulasikan, semakin sulit memvalidasi bahwa model mencerminkan realitas. Mulailah sederhana.

  4. Biaya komputasi. Untuk model kompleks, 10.000 simulasi bisa memakan waktu menit atau jam. Ini biasanya masih dapat diterima, tetapi perlu dipertimbangkan.

Important

Monte Carlo bukan pengganti penilaian manusia. Ia memberi kita distribusi kemungkinan—bukan jawaban pasti. Keputusan akhir tetap membutuhkan penilaian manusia tentang: - Apakah model dan distribusi masuk akal? - Apakah asumsi yang digunakan dapat dipertahankan? - Apakah risiko yang dihasilkan dapat diterima secara strategis?

12.9 Latihan Terbimbing

Latihan 12.1 — Estimasi π dengan sampel berbeda. Ulangi estimasi π dengan 100, 1.000, 10.000, dan 100.000 sampel. Plot error absolut vs jumlah sampel. Berapa banyak sampel yang dibutuhkan untuk error < 0,01? Apakah ini sesuai dengan teori (error ~ 1/√n)?

Latihan 12.2 — Distribusi churn. Ulangi simulasi revenue loss churn dengan distribusi churn rate yang berbeda—misalnya, gunakan distribusi beta alih-alih normal. Apakah hasilnya berubah secara signifikan? Mengapa?

Latihan 12.3 — Sensitivitas parameter. Dalam simulasi inventory, ubah stockout_cost menjadi 10.000 dan 40.000. Bagaimana level stok optimal berubah? Jelaskan hubungan antara biaya stockout dan level stok optimal.

Latihan 12.4 — Monte Carlo untuk keputusan retensi. Bayangkan kita memiliki anggaran retensi Rp 500 juta. Setiap intervensi biaya Rp 200.000 per pelanggan dan mengurangi probabilitas churn sebesar 30% (relatif). Simulasikan berapa banyak pelanggan yang bisa diintervensi, dan berapa ekspektasi revenue yang diselamatkan. Gunakan probabilitas churn dari model klasifikasi Bab 9 (atau asumsikan distribusi sederhana).

Latihan 12.5 — VaR untuk portofolio. kita memiliki portofolio tiga produk dengan permintaan yang berkorelasi. Simulasikan total revenue dari ketiga produk selama 30 hari, dengan asumsi korelasi antar produk 0,3. Hitung VaR 95% dan CVaR 95%. Bagaimana hasilnya berbeda jika korelasi 0,8?

Petunjuk Latihan 12.5. Untuk membangkitkan permintaan yang berkorelasi, gunakan distribusi normal multivariat (np.random.multivariate_normal) dengan matriks kovarians yang mencerminkan korelasi. Setelah mendapatkan sampel berkorelasi, transformasikan ke distribusi yang diinginkan (mis. eksponensial atau empiris).

12.10 Kotak “Jembatan”: Dari Simulasi ke Optimasi

Important

Jembatan. Bab ini melengkapi alat berpikir Bagian II:

Bab Kemampuan
7–9 Memprediksi probabilitas dan kelas
10 Memodelkan waktu dan tren
11 Menemukan struktur dan segmen
12 Mensimulasikan ketidakpastian

Dengan keempat kemampuan ini, kita sekarang dapat menjawab pertanyaan seperti:

  • “Berapa probabilitas pelanggan X akan churn?” (Bab 9)
  • “Berapa revenue yang berisiko hilang jika kita tidak melakukan apa-apa?” (Bab 12)
  • “Jika kita menghubungi 200 pelanggan berisiko tinggi, berapa ekspektasi revenue yang diselamatkan, dan berapa risikonya?” (Bab 12)
  • “Berapa level stok optimal jika permintaan tidak pasti?” (Bab 12)

Tetapi kita belum menjawab pertanyaan optimasi yang sesungguhnya:

  • “Dengan anggaran Rp 500 juta, kombinasi intervensi mana yang memaksimalkan revenue yang diselamatkan?”
  • “Bagaimana mengalokasikan anggaran retensi ke berbagai segmen untuk memaksimalkan ROI?”
  • “Berapa banyak staf yang perlu dijadwalkan untuk meminimalkan waktu tunggu pelanggan?”

Inilah tujuan Bagian III—prescriptive analytics. Model optimasi akan menggunakan probabilitas dari Bagian II sebagai input, dan simulasi dari bab ini untuk mengevaluasi keputusan di bawah ketidakpastian.

Bab 13 akan membuka Bagian III dengan fondasi pengambilan keputusan: payoff table, decision tree, dan nilai informasi. Bab 14–17 akan memperkenalkan teknik optimasi konkret—linear programming, integer programming, network optimization, dan simulasi keputusan. Bab 18 akan menutup dengan integrasi end-to-end.

12.11 Rangkuman

Dalam bab ini kita telah:

  • Memahami mengapa satu titik prediksi tidak cukup—keputusan nyata membutuhkan distribusi kemungkinan, bukan hanya ekspektasi.
  • Menerapkan simulasi Monte Carlo dalam tiga langkah: definisikan model probabilistik, bangkitkan sampel, evaluasi fungsi keputusan.
  • Memilih distribusi yang tepat—teoretis (normal, gamma, lognormal) atau empiris (bootstrap)—berdasarkan data dan konteks.
  • Mensimulasikan ketidakpastian churn pada dataset Telco, menghasilkan distribusi revenue loss yang jauh lebih informatif daripada satu angka.
  • Mensimulasikan keputusan inventory pada dataset retail, menemukan level stok optimal yang meminimalkan total biaya.
  • Mengukur risiko dengan VaR dan CVaR, memahami bahwa “ekspektasi” dan “risiko” adalah dua dimensi yang berbeda.
  • Menyadari batas Monte Carlo: garbage in garbage out, tidak menangkap black swan, dan tetap membutuhkan penilaian manusia.

12.12 Jembatan ke Bagian III

Bagian II telah memberi kita model prediktif yang dapat: - Memprediksi probabilitas churn per pelanggan. - Memperkirakan permintaan di masa depan. - Mengelompokkan pelanggan ke dalam segmen. - Mensimulasikan ketidakpastian dalam hasil.

Tetapi prediksi bukan keputusan. Kita sekarang memiliki bahan mentah untuk keputusan—tetapi belum resep untuk mengubahnya menjadi tindakan optimal.

Bagian III—Prescriptive Analytics—akan mengajarkan kita:

  • Bab 13 — Fondasi Prescriptive: payoff table, decision tree, nilai informasi, dan pengambilan keputusan di bawah ketidakpastian.
  • Bab 14 — Linear Programming: mengalokasikan sumber daya terbatas secara optimal.
  • Bab 15 — Integer & Nonlinear Programming: keputusan diskrit (ya/tidak, berapa banyak) dan hubungan non-linear.
  • Bab 16 — Network & Multi-Objective Optimization: aliran jaringan dan trade-off antar tujuan.
  • Bab 17 — Simulation for Decision Making: menggabungkan simulasi dengan optimasi.
  • Bab 18 — Integrasi & Proyek Akhir: dari data mentah ke rekomendasi kebijakan.

Selamat datang di Bagian III.