| import pandas as pd |
| import numpy as np |
| from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor, StackingRegressor |
| from sklearn.tree import DecisionTreeRegressor |
| from sklearn.linear_model import Ridge |
| from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_absolute_percentage_error |
| import joblib |
| import sys |
| import io |
| import os |
| try: |
| import matplotlib.pyplot as plt |
| HAS_MATPLOTLIB = True |
| except ImportError: |
| HAS_MATPLOTLIB = False |
| import warnings |
| warnings.filterwarnings('ignore') |
|
|
| |
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) |
| try: |
| from generate_real_kecamatan_dataset import generate_dataset |
| except ImportError: |
| from scripts.generate_real_kecamatan_dataset import generate_dataset |
|
|
| print("STARTING SPATIAL ENSEMBLE STACKING REGRESSOR TRAINING (AETERNA AI 44 KECAMATAN)...\n") |
|
|
| |
| |
| |
| csv_file = "data/dataset_real_kecamatan_2024_2025.csv" |
| if not os.path.exists(csv_file) and os.path.exists("waste-prediction-api/data/dataset_real_kecamatan_2024_2025.csv"): |
| csv_file = "waste-prediction-api/data/dataset_real_kecamatan_2024_2025.csv" |
|
|
| if not os.path.exists(csv_file): |
| print("[Dataset] Dataset tidak ditemukan. Membuat dataset spasial 44 Kecamatan baru...") |
| df = generate_dataset() |
| else: |
| print(f"[Dataset] Loading dataset dari '{csv_file}'...") |
| df = pd.read_csv(csv_file) |
|
|
| print(f"[Status] Dataset terload: {len(df)} total baris sampel dari 44 Kecamatan (2024-2025).\n") |
|
|
| |
| df['Tanggal'] = pd.to_datetime(df['Tanggal']) |
| df = df.sort_values('Tanggal').reset_index(drop=True) |
|
|
| |
| |
| |
| print("[Info] Ekstraksi & Enkodasi Fitur Spasial-Temporal...") |
|
|
| |
| zone_map = { |
| "Pusat Komersial": 1, |
| "Permukiman Padat": 2, |
| "Permukiman Menengah": 3, |
| "Pariwisata & Olahraga": 4, |
| "Pesisir & Pelabuhan": 5, |
| "Industri & Pergudangan": 6, |
| "Kepulauan": 7 |
| } |
| df['Zone_Type_Code'] = df['Zone_Type'].map(zone_map).fillna(0) |
|
|
| |
| feature_cols = [ |
| 'Population_Jiwa', |
| 'Normal_Avg_Ton', |
| 'Zone_Type_Code', |
| 'Rainfall_mm', |
| 'Rain_Lag_1', |
| 'Is_Weekend', |
| 'Hari_Dalam_Minggu', |
| 'Bulan', |
| 'Is_Mudik', |
| 'Ada_Event', |
| 'Event_Crowd_Headcount' |
| ] |
|
|
| X = df[feature_cols] |
| y = df['Volume_Sampah_Ton'] |
|
|
| |
| |
| |
| train_idx = df['Tanggal'] < pd.Timestamp("2025-07-01") |
|
|
| X_train, X_test = X[train_idx], X[~train_idx] |
| y_train, y_test = y[train_idx], y[~train_idx] |
|
|
| print(f"[Split] Split Data Kronologis: Train={len(X_train)} baris, Test={len(X_test)} baris.") |
|
|
| |
| |
| |
| print("\n[Train] Melatih Model Stacking Regressor (Decision Tree + Random Forest + GBR)...") |
|
|
| estimators = [ |
| ('dt', DecisionTreeRegressor(max_depth=6, random_state=42)), |
| ('rf', RandomForestRegressor(n_estimators=150, max_depth=6, random_state=42, n_jobs=-1)), |
| ('gbr', GradientBoostingRegressor(n_estimators=150, max_depth=5, learning_rate=0.05, random_state=42)) |
| ] |
|
|
| best_model = StackingRegressor( |
| estimators=estimators, |
| final_estimator=Ridge(alpha=1.0), |
| cv=3, |
| n_jobs=-1 |
| ) |
| best_model.fit(X_train, y_train) |
|
|
| pred_test = best_model.predict(X_test) |
|
|
| |
| mae = mean_absolute_error(y_test, pred_test) |
| rmse = mean_squared_error(y_test, pred_test) ** 0.5 |
| r2 = r2_score(y_test, pred_test) |
| mape = mean_absolute_percentage_error(y_test, pred_test) * 100 |
|
|
| |
| |
| |
| print("\n[Metrics] HASIL EVALUASI MODEL STACKING REGRESSOR (OUT-OF-SAMPLE TEST SET):") |
| print(f"βββββββββββββββββββββββββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ") |
| print(f"β Metric β Stacking Regressor β Interpretation β") |
| print(f"βββββββββββββββββββββββββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ€") |
| print(f"β Mean Absolute Error β {mae:16.2f} Ton β Rata-rata deviasi tebakan vs riil β") |
| print(f"β Root Mean Squared Error β {rmse:16.2f} Ton β Penalti deviasi ekstrem β") |
| print(f"β R-Squared (RΒ² Score) β {r2*100:15.2f}% β Varian data riil yang dapat dijelaskan β") |
| print(f"β MAPE (Error Persentase) β {mape:15.2f}% β Tingkat persentase eror rata-rata β") |
| print(f"βββββββββββββββββββββββββββ΄βββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββ") |
|
|
| |
| meta_coefs = np.abs(best_model.final_estimator_.coef_) |
| meta_coefs /= (np.sum(meta_coefs) + 1e-9) |
|
|
| importances = np.zeros(len(feature_cols)) |
| for i, (name, est) in enumerate(best_model.estimators): |
| fitted_est = best_model.estimators_[i] |
| if hasattr(fitted_est, 'feature_importances_'): |
| importances += fitted_est.feature_importances_ * meta_coefs[i] |
| elif hasattr(fitted_est, 'coef_'): |
| coefs = np.abs(fitted_est.coef_) |
| importances += (coefs / (np.sum(coefs) + 1e-9)) * meta_coefs[i] |
|
|
| importances /= (np.sum(importances) + 1e-9) |
|
|
| print("\n[Features] FITUR SPASIAL PALING BERPENGARUH PADA TIMBULAN SAMPAH:") |
| for name, imp in sorted(zip(feature_cols, importances), key=lambda x: x[1], reverse=True): |
| print(f" - {name:22s}: {imp*100:5.2f}%") |
|
|
| |
| |
| |
| if HAS_MATPLOTLIB: |
| print("\n[Plot] Membuat Visualisasi Scatter Plot Actual vs Predicted...") |
| plt.figure(figsize=(10, 6)) |
| plt.scatter(y_test, pred_test, alpha=0.4, color='#00f2fe', edgecolors='#0072ff', label='Stacking Regressor Predictions') |
| |
| |
| min_val = min(y_test.min(), pred_test.min()) |
| max_val = max(y_test.max(), pred_test.max()) |
| plt.plot([min_val, max_val], [min_val, max_val], color='#ff007f', linestyle='--', linewidth=2, label='Perfect Prediction') |
| |
| plt.title('Stacking Regressor: Actual vs Predicted Waste Volume (DKI Jakarta)', fontsize=14, color='#0f172a', pad=15) |
| plt.xlabel('Actual Waste Volume (tons)', fontsize=12) |
| plt.ylabel('Predicted Waste Volume (tons)', fontsize=12) |
| plt.grid(True, linestyle=':', alpha=0.6) |
| plt.legend(loc='upper left') |
| |
| |
| plt.tight_layout() |
| |
| |
| os.makedirs("frontend", exist_ok=True) |
| plot_path = "frontend/model_actual_vs_predicted.png" |
| plt.savefig(plot_path, dpi=150) |
| plt.close() |
| print(f"[Plot] Saved performance plot to '{plot_path}'!") |
| else: |
| print("\n[Plot] Skipping visualization plot generation because matplotlib is not installed.") |
|
|
| |
| os.makedirs("models", exist_ok=True) |
| model_file_path = "models/model_sampah_advanced.pkl" |
| meta_file_path = "models/model_metadata.pkl" |
|
|
| metadata = { |
| "feature_cols": feature_cols, |
| "zone_map": zone_map, |
| "metrics": { |
| "mae": float(mae), |
| "rmse": float(rmse), |
| "r2": float(r2), |
| "mape": float(mape) |
| }, |
| "best_params": { |
| "meta_coefs": meta_coefs.tolist() |
| } |
| } |
|
|
| joblib.dump(best_model, model_file_path) |
| joblib.dump(metadata, meta_file_path) |
|
|
| print(f"\n[Save] SUCCESS! Saved Stacking Regressor model to '{model_file_path}' and metadata to '{meta_file_path}'!") |
|
|