๐Ÿ”ฌModul Praktikum 02

Supervised Learning โ€” Regresi

Hands-on membangun model prediksi Rate of Penetration (ROP) menggunakan Linear Regression, Decision Tree, dan Random Forest. Dari data preparation hingga evaluasi & interpretasi model.

โฑ๏ธ2 ร— 50 menit
๐Ÿ“ŠDataset: Well Drilling (Clean)
๐ŸPython, Scikit-learn
๐ŸŽฏTujuan Pembelajaran
๐Ÿ“Š

Dataset Praktikum

Well Dataset (Clean) โ€” Hasil cleaning & Feature engineering dari Praktikum 01

๐Ÿ“ CSV Format ๐Ÿ’พ ~200 KB ๐Ÿ”ข 2,058 rows ร— 17 cols
โฌ‡๏ธ Download Dataset
1

Pendahuluan & Konteks

Pada praktikum sebelumnya, kita telah melakukan Data Wrangling & EDA pada dataset drilling dan menghasilkan dataset bersih (Well_Dataset_Clean.csv) dengan 2,058 baris dan 17 kolom โ€” termasuk fitur engineering MW_DIFF, DEPTH_CAT, dan FORMATION_CODE.

Sekarang, kita akan menggunakan dataset tersebut untuk membangun model supervised learning โ€” khususnya kasus regresi. Supervised learning adalah paradigma machine learning di mana model belajar dari data berlabel (input โ†’ output). Pada regresi, output berupa nilai kontinu (angka).

Target prediksi kita adalah ROP (Rate of Penetration) โ€” kecepatan pengeboran dalam m/hr. Prediksi ROP sangat penting untuk optimasi biaya operasional, perencanaan waktu drilling, dan pemilihan parameter optimal.

2,058
Data Points (Clean)
14
Fitur Input
ROP
Target Prediksi
3
Model Dibangun

๐Ÿ”„ Workflow Supervised Learning

1๏ธโƒฃ

Data Preparation

Encoding, scaling, definisi fitur & target

2๏ธโƒฃ

Train-Test Split

Memisahkan data latih dan data uji (80:20)

3๏ธโƒฃ

Training

Model belajar pola dari data latih

4๏ธโƒฃ

Evaluation

Mengukur performa di data uji

2

Persiapan Data untuk Modeling

Kita memuat dataset yang sudah bersih dari Praktikum 01 beserta fitur engineering yang telah dibuat.

๐Ÿ“ฆ 2.1 Import Library & Load Data

Py
Python
[1]
# Import library
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OrdinalEncoder
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor, plot_tree
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

plt.style.use('dark_background')
print("โœ… Library berhasil di-import!")
โœ… Library berhasil di-import!
Py
Python
[2]
# Load dataset clean dari Praktikum 01
df = pd.read_csv('Well_Dataset_Clean.csv')
print(f"๐Ÿ“Š Dimensi Data: {df.shape[0]:,} baris ร— {df.shape[1]} kolom")
print(f"๐Ÿ“‹ Kolom:")
for i, col in enumerate(df.columns, 1):
    print(f"   {i:2d}. {col}")
๐Ÿ“Š Dimensi Data: 2,058 baris ร— 17 kolom
๐Ÿ“‹ Kolom:
    1. Formation
    2. TMD (m)
    3. TVD (m)
    4. ROP (m/hr)
    5. WOB (ton)
    6. FR (gpm)
    7. SPP (psi)
    8. RPM (rpm)
    9. TQ (lb.ft)
   10. MW IN (ppg)
   11. MW OUT (ppg)
   12. RETURN (%)
   13. BIT TIME (hr)
   14. DATE TIME
   15. MW_DIFF
   16. DEPTH_CAT
   17. FORMATION_CODE

๐ŸŽฏ 2.2 Definisi Fitur & Target

Dalam supervised learning, kita memisahkan data menjadi fitur (X) โ€” variabel input, dan target (y) โ€” variabel yang diprediksi. Dari 17 kolom, kita pilih fitur numerik dan fitur engineering yang telah dibuat di Praktikum 01. Kolom Formation (teks), DATE TIME, dan DEPTH_CAT (perlu encoding) ditangani secara khusus.

Py
Python
[3]
# Encoding DEPTH_CAT (ordinal: Shallow < Medium < Deep < Very Deep)
from sklearn.preprocessing import OrdinalEncoder

depth_order = ['Shallow', 'Medium', 'Deep', 'Very Deep']
oe = OrdinalEncoder(categories=[depth_order])
df['DEPTH_CAT_encoded'] = oe.fit_transform(df[['DEPTH_CAT']]).astype(int)

print("๐Ÿท๏ธ Ordinal Encoding โ€” DEPTH_CAT:")
for cat, code in zip(depth_order, range(4)):
    print(f"   {cat:12s} โ†’ {code}")
๐Ÿท๏ธ Ordinal Encoding โ€” DEPTH_CAT:
   Shallow      โ†’ 0
   Medium       โ†’ 1
   Deep         โ†’ 2
   Very Deep    โ†’ 3
Py
Python
[4]
# Definisi fitur dan target
feature_cols = [
    'TMD (m)', 'TVD (m)', 'WOB (ton)', 'FR (gpm)', 'SPP (psi)',
    'RPM (rpm)', 'TQ (lb.ft)', 'MW IN (ppg)', 'MW OUT (ppg)',
    'RETURN (%)', 'BIT TIME (hr)',   # 11 fitur asli
    'MW_DIFF', 'FORMATION_CODE',       # 2 fitur dari Praktikum 01
    'DEPTH_CAT_encoded'               # 1 fitur encoded baru
]
target_col = 'ROP (m/hr)'

X = df[feature_cols].copy()
y = df[target_col].copy()

print(f"๐Ÿ“ฅ Fitur (X): {len(feature_cols)} variabel")
for i, col in enumerate(feature_cols, 1):
    print(f"   {i:2d}. {col}")
print(f"\n๐ŸŽฏ Target (y): {target_col}")
print(f"๐Ÿ“Š X shape: {X.shape} | y shape: {y.shape}")
๐Ÿ“ฅ Fitur (X): 14 variabel
    1. TMD (m)
    2. TVD (m)
    3. WOB (ton)
    4. FR (gpm)
    5. SPP (psi)
    6. RPM (rpm)
    7. TQ (lb.ft)
    8. MW IN (ppg)
    9. MW OUT (ppg)
   10. RETURN (%)
   11. BIT TIME (hr)
   12. MW_DIFF
   13. FORMATION_CODE
   14. DEPTH_CAT_encoded

๐ŸŽฏ Target (y): ROP (m/hr)
๐Ÿ“Š X shape: (2058, 14) | y shape: (2058,)

๐Ÿ“ 2.3 Recap: Feature Engineering dari Praktikum 01

Di Praktikum 01 kita sudah membuat 3 fitur baru. Mari lihat distribusinya untuk memastikan data sudah benar:

Py
Python
[5]
# Visualisasi fitur engineering
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
axes[0].hist(df['MW_DIFF'], bins=30, color='#4ecdc4')
axes[0].set_title('Distribusi MW_DIFF')
df['DEPTH_CAT'].value_counts().reindex(depth_order).plot.bar(ax=axes[1])
axes[1].set_title('DEPTH_CAT Distribution')
# FORMATION_CODE mapping
fm = df.groupby('FORMATION_CODE')['Formation'].first().reset_index()
axes[2].barh(fm['Formation'], fm['FORMATION_CODE'])
axes[2].set_title('FORMATION_CODE Mapping')
plt.tight_layout(); plt.show()
Feature Engineering
๐Ÿ’กRecap Feature Engineering

โ€ข MW_DIFF = MW OUT โˆ’ MW IN: Selisih densitas lumpur, mengindikasikan kontaminasi formasi.

โ€ข DEPTH_CAT: Kategorisasi kedalaman (Shallow/Medium/Deep/Very Deep) โ†’ di-encode menjadi ordinal 0โ€“3.

โ€ข FORMATION_CODE: Kode numerik untuk jenis formasi batuan (0โ€“9), sudah dibuat di Praktikum 01.

๐Ÿ“Š 2.4 Distribusi Target

Py
Python
[6]
# Distribusi target ROP
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].hist(df[target_col], bins=40, color='#00d9ff', alpha=0.85)
axes[0].axvline(df[target_col].mean(), color='red', ls='--', label=f'Mean: {df[target_col].mean():.1f}')
axes[0].legend()
# Boxplot per formasi
df.boxplot(column=target_col, by='Formation', ax=axes[1])
plt.tight_layout(); plt.show()
Target Distribution

โš–๏ธ 2.5 Feature Scaling

Feature scaling menyamakan skala antar fitur. Tanpa scaling, fitur bernilai besar (SPP: ribuan, TQ: ribuan) akan mendominasi model linear. Kita gunakan StandardScaler (mean=0, std=1). Catatan: scaling diterapkan setelah train-test split untuk mencegah data leakage.

Py
Python
[7]
# Demo: Before vs After Scaling
scaler_demo = StandardScaler()
X_demo = pd.DataFrame(scaler_demo.fit_transform(X), columns=X.columns)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))
show = ['WOB (ton)', 'FR (gpm)', 'SPP (psi)', 'RPM (rpm)', 'TVD (m)', 'TQ (lb.ft)']
X[show].boxplot(ax=axes[0]); axes[0].set_title('Sebelum Scaling')
X_demo[show].boxplot(ax=axes[1]); axes[1].set_title('Setelah StandardScaler')
plt.tight_layout(); plt.show()
Scaling
๐Ÿ’กKapan Scaling Diperlukan?

โ€ข Wajib: Linear Regression, SVM, KNN, Neural Network (model sensitif terhadap skala).

โ€ข Tidak perlu: Decision Tree, Random Forest (model berbasis aturan split, bukan jarak).

3

Train-Test Split

Data dibagi 80:20 โ€” 80% untuk training, 20% untuk testing. Scaling dilakukan setelah split: fit_transform pada train, transform saja pada test.

Py
Python
[8]
# Train-Test Split (80:20)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Scaling SETELAH split (mencegah data leakage!)
scaler = StandardScaler()
X_train_scaled = pd.DataFrame(
    scaler.fit_transform(X_train),
    columns=X_train.columns, index=X_train.index
)
X_test_scaled = pd.DataFrame(
    scaler.transform(X_test),  # transform saja, BUKAN fit_transform!
    columns=X_test.columns, index=X_test.index
)

print(f"๐Ÿ“Š Train set: {X_train.shape[0]:,} data ({X_train.shape[0]/len(X)*100:.0f}%)")
print(f"๐Ÿ“Š Test set:  {X_test.shape[0]:,} data ({X_test.shape[0]/len(X)*100:.0f}%)")
print(f"โœ… Scaling selesai (fit pada train, transform pada test)")
๐Ÿ“Š Train set: 1,646 data (80%)
๐Ÿ“Š Test set:  412 data (20%)
โœ… Scaling selesai (fit pada train, transform pada test)
Py
Python
[9]
# Visualisasi proporsi dan distribusi
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].pie([len(X_train), len(X_test)], labels=['Train', 'Test'], autopct='%1.0f%%')
axes[1].hist(y_train, alpha=0.7, label='Train')
axes[1].hist(y_test, alpha=0.7, label='Test')
axes[1].legend()
plt.show()
Train Test Split
๐Ÿ”‘Mencegah Data Leakage

scaler.fit_transform() hanya pada data train. Data test hanya scaler.transform(). Statistik scaling (mean, std) hanya berasal dari train โ€” simulasi evaluasi benar-benar pada data "baru" yang belum pernah dilihat model.

4

Model 1: Linear Regression

Linear Regression mencari hubungan linear: ลท = ฮฒโ‚€ + ฮฒโ‚xโ‚ + ฮฒโ‚‚xโ‚‚ + ... + ฮฒโ‚™xโ‚™. Model ini meminimalkan sum of squared errors (OLS). Menggunakan data yang sudah di-scale.

Py
Python
[10]
# Training Linear Regression (data SCALED)
lr = LinearRegression()
lr.fit(X_train_scaled, y_train)
y_pred_lr = lr.predict(X_test_scaled)

print("๐Ÿ“Š Linear Regression โ€” Hasil Evaluasi:")
print(f"   MAE  = {mean_absolute_error(y_test, y_pred_lr):.4f} m/hr")
print(f"   RMSE = {np.sqrt(mean_squared_error(y_test, y_pred_lr)):.4f} m/hr")
print(f"   Rยฒ   = {r2_score(y_test, y_pred_lr):.4f}")
๐Ÿ“Š Linear Regression โ€” Hasil Evaluasi:
   MAE  = 18.7175 m/hr
   RMSE = 29.3290 m/hr
   Rยฒ   = 0.2112
Py
Python
[11]
# Predicted vs Actual + Residual Plot
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].scatter(y_test, y_pred_lr, alpha=0.5, s=15)
axes[0].plot([y_test.min(), y_test.max()],
             [y_test.min(), y_test.max()], 'r--')
residuals = y_test - y_pred_lr
axes[1].scatter(y_pred_lr, residuals, alpha=0.5, s=15)
axes[1].axhline(0, color='red', ls='--')
plt.tight_layout(); plt.show()
LR Result
Py
Python
[12]
# Koefisien model
coef_df = pd.DataFrame({
    'Feature': feature_cols,
    'Coefficient': lr.coef_
}).sort_values('Coefficient')
print(f"Intercept: {lr.intercept_:.4f}")
print(coef_df.to_string(index=False))
coef_df.plot.barh(x='Feature', y='Coefficient'); plt.show()
LR Coef
๐Ÿ”Interpretasi

Rยฒ = 0.2112 โ€” Linear Regression hanya menjelaskan ~21% variasi ROP. Ini mengindikasikan bahwa hubungan antara parameter drilling dan ROP bersifat non-linear. Koefisien BIT TIME dan SPP positif besar, sementara WOB dan MW_DIFF berkoefisien negatif. Perhatikan juga bahwa TMD dan TVD memiliki koefisien sangat besar (berlawanan arah) karena keduanya sangat berkorelasi โ€” ini menunjukkan masalah multicollinearity.

5

Model 2: Decision Tree Regressor

Decision Tree mempartisi data secara rekursif berdasarkan aturan if-else. Mampu menangkap hubungan non-linear tanpa perlu scaling. Parameter max_depth=8 membatasi kedalaman tree.

Py
Python
[13]
# Training Decision Tree (tanpa scaling!)
dt = DecisionTreeRegressor(max_depth=8, random_state=42)
dt.fit(X_train, y_train)
y_pred_dt = dt.predict(X_test)

print("๐ŸŒณ Decision Tree โ€” Hasil Evaluasi:")
print(f"   MAE  = {mean_absolute_error(y_test, y_pred_dt):.4f} m/hr")
print(f"   RMSE = {np.sqrt(mean_squared_error(y_test, y_pred_dt)):.4f} m/hr")
print(f"   Rยฒ   = {r2_score(y_test, y_pred_dt):.4f}")
๐ŸŒณ Decision Tree โ€” Hasil Evaluasi:
   MAE  = 14.0066 m/hr
   RMSE = 20.0478 m/hr
   Rยฒ   = 0.6315
Py
Python
[14]
# Visualisasi Decision Tree (max_depth=3 agar terbaca)
dt_viz = DecisionTreeRegressor(max_depth=3, random_state=42)
dt_viz.fit(X_train, y_train)
fig, ax = plt.subplots(figsize=(26, 12))
plot_tree(dt_viz, feature_names=feature_cols,
          filled=True, rounded=True, ax=ax, fontsize=8)
plt.show()
DT Tree
Py
Python
[15]
# Predicted vs Actual + Residual
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].scatter(y_test, y_pred_dt, alpha=0.5, s=15, color='#7c3aed')
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
residuals_dt = y_test - y_pred_dt
axes[1].scatter(y_pred_dt, residuals_dt, alpha=0.5, s=15, color='#7c3aed')
axes[1].axhline(0, color='red', ls='--')
plt.tight_layout(); plt.show()
DT Result
๐ŸŒณInterpretasi

Rยฒ = 0.6315 โ€” peningkatan drastis +42% dibanding Linear Regression. Decision Tree mampu menangkap hubungan non-linear yang tidak bisa ditangkap model linear. Dari visualisasi tree, split pertama terjadi pada parameter kedalaman โ€” mengkonfirmasi peran dominan kedalaman terhadap ROP.

6

Model 3: Random Forest Regressor

Random Forest adalah metode ensemble: membangun 100 Decision Tree independen pada subset data/fitur yang berbeda, lalu merata-ratakan prediksinya. Ini mengurangi overfitting dan meningkatkan generalisasi.

Py
Python
[16]
# Training Random Forest
rf = RandomForestRegressor(
    n_estimators=100,   # 100 trees
    max_depth=12,       # Kedalaman per tree
    random_state=42,
    n_jobs=-1           # Semua CPU core
)
rf.fit(X_train, y_train)
y_pred_rf = rf.predict(X_test)

print("๐ŸŒฒ Random Forest โ€” Hasil Evaluasi:")
print(f"   MAE  = {mean_absolute_error(y_test, y_pred_rf):.4f} m/hr")
print(f"   RMSE = {np.sqrt(mean_squared_error(y_test, y_pred_rf)):.4f} m/hr")
print(f"   Rยฒ   = {r2_score(y_test, y_pred_rf):.4f}")
๐ŸŒฒ Random Forest โ€” Hasil Evaluasi:
   MAE  = 11.4241 m/hr
   RMSE = 16.4361 m/hr
   Rยฒ   = 0.7523
Py
Python
[17]
# Predicted vs Actual + Residual
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].scatter(y_test, y_pred_rf, alpha=0.5, s=15, color='#10b981')
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
plt.tight_layout(); plt.show()
RF Result

๐Ÿ”‘ 6.1 Feature Importance

Py
Python
[18]
# Feature Importance
imp_df = pd.DataFrame({
    'Feature': feature_cols,
    'Importance': rf.feature_importances_
}).sort_values('Importance', ascending=False)

print("๐Ÿ”‘ Feature Importance (Random Forest):")
for _, row in imp_df.iterrows():
    bar = 'โ–ˆ' * int(row['Importance'] * 50)
    print(f"   {row['Feature']:22s} {row['Importance']:.4f} {bar}")

imp_df.plot.barh(x='Feature', y='Importance'); plt.show()
๐Ÿ”‘ Feature Importance (Random Forest):
   TMD (m)                0.1418 โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   RPM (rpm)              0.1410 โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   TVD (m)                0.1298 โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   BIT TIME (hr)          0.1062 โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   TQ (lb.ft)             0.1059 โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   FR (gpm)               0.0994 โ–ˆโ–ˆโ–ˆโ–ˆ
   WOB (ton)              0.0926 โ–ˆโ–ˆโ–ˆโ–ˆ
   FORMATION_CODE         0.0507 โ–ˆโ–ˆ
   SPP (psi)              0.0477 โ–ˆโ–ˆ
   RETURN (%)             0.0406 โ–ˆโ–ˆ
   MW_DIFF                0.0377 โ–ˆ
   MW OUT (ppg)           0.0055 
   MW IN (ppg)            0.0006 
   DEPTH_CAT_encoded      0.0005 
Feature Importance
๐ŸŽฏInterpretasi Feature Importance

TMD/TVD (kedalaman) mendominasi ~27% total importance โ€” formasi semakin keras di kedalaman, langsung memengaruhi kecepatan drilling. RPM (14.1%) adalah parameter operasional paling berpengaruh โ€” driller bisa langsung mengatur ini. BIT TIME (10.6%) dan TQ (10.6%) juga signifikan โ€” waktu pemakaian bit dan torsi mengindikasikan keausan bit. Fitur engineering FORMATION_CODE (5.1%) berkontribusi lebih besar dari SPP, membuktikan bahwa feature engineering di Praktikum 01 bermanfaat. MW IN/OUT dan DEPTH_CAT_encoded hampir tidak berkontribusi โ€” informasinya sudah tercakup oleh TVD dan fitur lain.

7

Perbandingan Model

Py
Python
[19]
# Tabel perbandingan
results = pd.DataFrame({
    'Model': ['Linear Regression', 'Decision Tree', 'Random Forest'],
    'MAE': [18.72, 14.01, 11.42],
    'RMSE': [29.33, 20.05, 16.44],
    'Rยฒ': [0.2112, 0.6315, 0.7523]
})
print(results.to_string(index=False))
ModelMAE (m/hr)RMSE (m/hr)Rยฒ
Linear Regression18.7229.330.2112
Decision Tree14.0120.050.6315
Random Forest11.4216.440.7523
Py
Python
[20]
# Bar chart perbandingan Rยฒ dan RMSE
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
models = ['Linear Reg.', 'Decision Tree', 'Random Forest']
axes[0].bar(models, results['Rยฒ'])
axes[1].bar(models, results['RMSE'])
plt.show()
Model Comparison
๐Ÿ“ŠMemahami Metrik Evaluasi

โ€ข MAE: Rata-rata selisih absolut prediksi vs aktual. Satuan sama dengan target (m/hr). Makin kecil makin baik.

โ€ข RMSE: Seperti MAE tapi lebih sensitif terhadap error besar. Makin kecil makin baik.

โ€ข Rยฒ: Proporsi variasi target yang dijelaskan model. Range 0โ€“1, makin dekat ke 1 makin baik.

8

Visualisasi Diagnostik

๐Ÿ“ˆ 8.1 Predicted vs Actual โ€” Semua Model

Py
Python
[21]
# Perbandingan Predicted vs Actual (3 model)
fig, axes = plt.subplots(1, 3, figsize=(18, 5.5))
for ax, y_pred, name, color in zip(axes,
    [y_pred_lr, y_pred_dt, y_pred_rf],
    ['Linear Regression', 'Decision Tree', 'Random Forest'],
    ['#00d9ff', '#7c3aed', '#10b981']):
    ax.scatter(y_test, y_pred, alpha=0.4, s=12, color=color)
    ax.plot([y_test.min(), y_test.max()],
            [y_test.min(), y_test.max()], 'r--')
    ax.set_title(name); ax.set_aspect('equal')
plt.tight_layout(); plt.show()
Pred Comparison

๐Ÿ“Š 8.2 Distribusi Residual

Residual yang baik seharusnya berdistribusi normal dan berpusat di nol.

Py
Python
[22]
# Distribusi residual ketiga model
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
for ax, res, name in zip(axes,
    [y_test-y_pred_lr, y_test-y_pred_dt, y_test-y_pred_rf],
    ['Linear Regression', 'Decision Tree', 'Random Forest']):
    ax.hist(res, bins=30)
    ax.axvline(0, color='red', ls='--')
plt.tight_layout(); plt.show()
Residual All
๐Ÿ”Analisis Residual

โ€ข Linear Regression: Residual tersebar sangat luas โ€” model gagal menangkap pola non-linear.

โ€ข Decision Tree: Distribusi lebih sempit, tapi ada pola "step" karena prediksi diskrit dari leaf nodes.

โ€ข Random Forest: Distribusi paling sempit dan simetris di nol โ€” prediksi paling konsisten dan tidak bias.

๐Ÿ”ฌ 8.3 Prediksi vs Data Aktual pada Profil Kedalaman

Visualisasi yang sangat penting dalam konteks geofisika: bagaimana prediksi model dibandingkan dengan data training pada profil TVD vs ROP. Plot ini menunjukkan apakah model mampu menangkap pola perubahan ROP di setiap zona kedalaman.

Py
Python
[23]
# TVD vs ROP โ€” Training Data vs Predicted (ketiga model)
tvd_train = df.loc[X_train.index, 'TVD (m)']
tvd_test = df.loc[X_test.index, 'TVD (m)']

fig, axes = plt.subplots(1, 3, figsize=(18, 8))
for ax, y_pred, name in zip(axes,
    [y_pred_lr, y_pred_dt, y_pred_rf],
    ['Linear Regression', 'Decision Tree', 'Random Forest']):
    ax.scatter(y_train, tvd_train, s=12, alpha=0.35,
              color='#4a90d9', label='Training Data')
    ax.scatter(y_pred, tvd_test, s=18, alpha=0.75,
              color='#ff8c42', label='Predicted Data')
    ax.invert_yaxis()
    ax.legend(); ax.set_title(name)
    ax.set_xlabel('ROP (m/hr)'); ax.set_ylabel('TVD (m)')
plt.suptitle('Training Data vs Predicted โ€” TVD vs ROP')
plt.tight_layout(); plt.show()
TVD vs ROP Prediction
Py
Python
[24]
# Actual vs Predicted pada test set (dengan garis error)
fig, axes = plt.subplots(1, 3, figsize=(18, 9))
for ax, y_pred, name, color in zip(axes,
    [y_pred_lr, y_pred_dt, y_pred_rf],
    ['Linear Regression', 'Decision Tree', 'Random Forest'],
    ['#00d9ff', '#7c3aed', '#10b981']):
    ax.scatter(y_test, tvd_test, s=15, alpha=0.5,
              color='#4a90d9', label='Actual (Test)')
    ax.scatter(y_pred, tvd_test, s=15, alpha=0.5,
              color=color, label='Predicted (Test)')
    # Garis error horizontal
    for yt, yp, tvd in zip(y_test, y_pred, tvd_test):
        ax.plot([yt, yp], [tvd, tvd], color='red', alpha=0.08, lw=0.5)
    ax.invert_yaxis()
    ax.legend(); ax.set_title(name)
    ax.set_xlabel('ROP (m/hr)'); ax.set_ylabel('TVD (m)')
plt.suptitle('Actual vs Predicted (Test Set) โ€” TVD vs ROP')
plt.tight_layout(); plt.show()
TVD vs ROP Actual vs Pred
๐Ÿ”ฌInterpretasi Profil Kedalaman

โ€ข Linear Regression: Prediksi menyebar ke nilai negatif (tidak fisik!) โ€” model linear gagal total menangkap pola non-linear ROP terhadap kedalaman.

โ€ข Decision Tree: Prediksi membentuk pola "garis vertikal" diskrit โ€” tiap leaf node menghasilkan satu nilai konstan. Pola umum tertangkap, tapi kurang halus.

โ€ข Random Forest: Prediksi (orange) paling rapat mengikuti sebaran data training (biru) di seluruh zona kedalaman โ€” model terbaik untuk menangkap variasi ROP per formasi.

9

Insight & Kesimpulan

๐Ÿ“ŠRingkasan Temuan

1. Performa Model
Random Forest menjadi model terbaik (Rยฒ = 0.7523, RMSE = 16.44 m/hr), diikuti Decision Tree (Rยฒ = 0.6315), dan Linear Regression (Rยฒ = 0.2112). Hubungan parameter drilling vs ROP bersifat non-linear.

2. Faktor Dominan terhadap ROP
โ€ข TMD/TVD (kedalaman) ~27%: Formasi semakin keras di kedalaman.
โ€ข RPM 14.1%: Parameter operasional paling berpengaruh, bisa dikontrol langsung.
โ€ข BIT TIME & TQ ~10.5%: Menandakan kondisi/keausan bit drilling.
โ€ข FORMATION_CODE 5.1%: Feature engineering dari Praktikum 01 terbukti berguna.

3. Implikasi Operasional
Model Random Forest bisa digunakan untuk real-time ROP prediction, membantu driller mengoptimalkan RPM, FR, dan WOB berdasarkan kedalaman dan kondisi formasi.

4. Potensi Improvement
โ€ข Hyperparameter tuning (GridSearchCV/RandomizedSearchCV)
โ€ข Menghapus fitur redundan (TMD โ‰ˆ TVD, MW_DIFF โ‰ˆ MW OUT โˆ’ MW IN)
โ€ข Model lanjutan: Gradient Boosting (XGBoost, LightGBM)
โ€ข Cross-validation (k-fold) untuk evaluasi lebih robust

Py
Python
[23]
# Simpan model terbaik
import joblib
joblib.dump(rf, 'best_model_rf.pkl')
joblib.dump(scaler, 'scaler.pkl')
joblib.dump(oe, 'ordinal_encoder.pkl')
print("โœ… Model, scaler, dan encoder berhasil disimpan!")
โœ… Model, scaler, dan encoder berhasil disimpan!
   ๐Ÿ“ best_model_rf.pkl
   ๐Ÿ“ scaler.pkl
   ๐Ÿ“ ordinal_encoder.pkl
10

Latihan Mandiri

๐Ÿ“Soal Latihan
  1. Ganti Target Variabel
    Gunakan TQ (lb.ft) sebagai target prediksi. Latih ketiga model dan bandingkan hasilnya. Apakah ranking model berubah?
  2. Eksperimen Feature Selection
    Latih Random Forest hanya dengan 5 fitur teratas (TMD, RPM, TVD, BIT TIME, TQ). Bandingkan Rยฒ dengan model 14 fitur. Berapa informasi yang hilang?
  3. Hyperparameter Tuning
    Ubah parameter n_estimators RF menjadi [10, 50, 100, 200, 500]. Plot Rยฒ vs n_estimators. Di mana titik diminishing returns?
  4. Analisis Multicollinearity
    TMD dan TVD sangat berkorelasi. Coba drop salah satu, lalu latih ulang ketiga model. Apakah performa berubah signifikan?
  5. Prediksi per Formasi
    Hitung RMSE per formasi untuk Random Forest. Formasi mana yang paling mudah dan paling sulit diprediksi? Jelaskan berdasarkan karakteristik data.
โš ๏ธTugas Praktikum

Kerjakan soal latihan dalam format laporan yang sudah diberikan. Sertakan kode, output, dan interpretasi untuk setiap soal. Submit melalui e-learning sebelum pertemuan berikutnya.