{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "939c6588-f140-4b50-a0b8-d4a5a36fc86e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset berhasil dimuat: (10000, 8)\n",
      "\n",
      "============================================================\n",
      "PROSES TRAINING DAN TUNING SEDANG BERJALAN...\n",
      "============================================================\n",
      ">> Melatih KNN (Mencari K terbaik)...\n",
      "   KNN Terbaik: {'classifier__n_neighbors': 3, 'classifier__weights': 'uniform'}\n",
      "\n",
      ">> Melatih Random Forest...\n",
      "   RF Terbaik: {'classifier__max_depth': None, 'classifier__min_samples_split': 2, 'classifier__n_estimators': 100}\n",
      "\n",
      "============================================================\n",
      "HASIL EVALUASI AKHIR\n",
      "============================================================\n",
      "\n",
      "MODEL: KNN\n",
      "--------------------\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "      Lewati       0.83      0.87      0.85      1494\n",
      "      Tonton       0.55      0.46      0.50       506\n",
      "\n",
      "    accuracy                           0.77      2000\n",
      "   macro avg       0.69      0.67      0.67      2000\n",
      "weighted avg       0.76      0.77      0.76      2000\n",
      "\n",
      "\n",
      "MODEL: Random Forest\n",
      "--------------------\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "      Lewati       0.87      0.93      0.90      1494\n",
      "      Tonton       0.74      0.60      0.66       506\n",
      "\n",
      "    accuracy                           0.85      2000\n",
      "   macro avg       0.81      0.76      0.78      2000\n",
      "weighted avg       0.84      0.85      0.84      2000\n",
      "\n",
      "\n",
      "============================================================\n",
      "ANALISIS FITUR TERPENTING (Khusus Random Forest)\n",
      "(Catatan: KNN tidak memiliki fitur ini karena berbasis jarak)\n",
      "============================================================\n",
      "Menampilkan semua grafik evaluasi...\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 101584 characters]",
      "text/plain": [
       "<Figure size 1400x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 29704 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 42328 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.model_selection import train_test_split, GridSearchCV\n",
    "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.metrics import (classification_report, confusion_matrix, \n",
    "                             ConfusionMatrixDisplay, roc_curve, auc, \n",
    "                             accuracy_score, f1_score)\n",
    "\n",
    "# Mengatur gaya plot agar rapi tanpa seaborn\n",
    "plt.style.use('ggplot')\n",
    "\n",
    "def main():\n",
    "    # --- 1. MEMUAT DATA ---\n",
    "    try:\n",
    "        df = pd.read_csv('Latest 2025 movies Datasets.csv')\n",
    "        print(f\"Dataset berhasil dimuat: {df.shape}\")\n",
    "    except FileNotFoundError:\n",
    "        print(\"Error: File csv tidak ditemukan. Pastikan nama file sesuai.\")\n",
    "        return\n",
    "\n",
    "    # --- 2. PREPARASI DATA ---\n",
    "    # Target: Rating > 7.0 dianggap \"Layak Tonton\" (1)\n",
    "    df['is_watchable'] = (df['vote_average'] > 7.0).astype(int)\n",
    "    \n",
    "    # Fitur yang digunakan\n",
    "    X = df[['popularity', 'vote_count', 'original_language']]\n",
    "    y = df['is_watchable']\n",
    "\n",
    "    # Split Data (80% Train, 20% Test)\n",
    "    # Stratify=y memastikan proporsi kelas seimbang di train dan test\n",
    "    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n",
    "\n",
    "    # --- 3. PIPELINE PREPROCESSING ---\n",
    "    # Pipeline ini akan menangani data kosong dan scaling secara otomatis\n",
    "    numeric_features = ['popularity', 'vote_count']\n",
    "    categorical_features = ['original_language']\n",
    "\n",
    "    preprocessor = ColumnTransformer(\n",
    "        transformers=[\n",
    "            ('num', Pipeline([\n",
    "                ('imputer', SimpleImputer(strategy='median')),\n",
    "                ('scaler', StandardScaler()) # Scaling wajib untuk KNN\n",
    "            ]), numeric_features),\n",
    "            ('cat', Pipeline([\n",
    "                ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),\n",
    "                ('onehot', OneHotEncoder(handle_unknown='ignore'))\n",
    "            ]), categorical_features)\n",
    "        ])\n",
    "\n",
    "    # --- 4. HYPERPARAMETER TUNING (Grid Search) ---\n",
    "    print(\"\\n\" + \"=\"*60)\n",
    "    print(\"PROSES TRAINING DAN TUNING SEDANG BERJALAN...\")\n",
    "    print(\"=\"*60)\n",
    "\n",
    "    # --- A. TUNING KNN ---\n",
    "    print(\">> Melatih KNN (Mencari K terbaik)...\")\n",
    "    knn_pipeline = Pipeline([\n",
    "        ('preprocessor', preprocessor),\n",
    "        ('classifier', KNeighborsClassifier())\n",
    "    ])\n",
    "    \n",
    "    # Mencari kombinasi parameter terbaik\n",
    "    knn_params = {\n",
    "        'classifier__n_neighbors': [3, 5, 7, 11],\n",
    "        'classifier__weights': ['uniform', 'distance']\n",
    "    }\n",
    "    \n",
    "    grid_knn = GridSearchCV(knn_pipeline, knn_params, cv=3, scoring='f1', n_jobs=-1)\n",
    "    grid_knn.fit(X_train, y_train)\n",
    "    print(f\"   KNN Terbaik: {grid_knn.best_params_}\")\n",
    "\n",
    "    # --- B. TUNING RANDOM FOREST ---\n",
    "    print(\"\\n>> Melatih Random Forest...\")\n",
    "    rf_pipeline = Pipeline([\n",
    "        ('preprocessor', preprocessor),\n",
    "        ('classifier', RandomForestClassifier(random_state=42))\n",
    "    ])\n",
    "    \n",
    "    rf_params = {\n",
    "        'classifier__n_estimators': [50, 100],\n",
    "        'classifier__max_depth': [None, 10, 20],\n",
    "        'classifier__min_samples_split': [2, 5]\n",
    "    }\n",
    "    \n",
    "    grid_rf = GridSearchCV(rf_pipeline, rf_params, cv=3, scoring='f1', n_jobs=-1)\n",
    "    grid_rf.fit(X_train, y_train)\n",
    "    print(f\"   RF Terbaik: {grid_rf.best_params_}\")\n",
    "\n",
    "    # --- 5. EVALUASI DAN VISUALISASI ---\n",
    "    \n",
    "    best_models = {\n",
    "        'KNN': grid_knn.best_estimator_,\n",
    "        'Random Forest': grid_rf.best_estimator_\n",
    "    }\n",
    "\n",
    "    # Canvas 1: Matriks Evaluasi (Confusion Matrix & ROC)\n",
    "    fig1, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
    "    fig1.suptitle('Evaluasi Detail: KNN vs Random Forest', fontsize=16)\n",
    "    plt.subplots_adjust(hspace=0.4, wspace=0.3)\n",
    "\n",
    "    results_summary = {'Model': [], 'Accuracy': [], 'F1-Score': []}\n",
    "\n",
    "    print(\"\\n\" + \"=\"*60)\n",
    "    print(\"HASIL EVALUASI AKHIR\")\n",
    "    print(\"=\"*60)\n",
    "\n",
    "    for i, (name, model) in enumerate(best_models.items()):\n",
    "        # Prediksi ke data test\n",
    "        y_pred = model.predict(X_test)\n",
    "        \n",
    "        # Simpan skor untuk perbandingan nanti\n",
    "        acc = accuracy_score(y_test, y_pred)\n",
    "        f1 = f1_score(y_test, y_pred)\n",
    "        results_summary['Model'].append(name)\n",
    "        results_summary['Accuracy'].append(acc)\n",
    "        results_summary['F1-Score'].append(f1)\n",
    "\n",
    "        # Print Laporan Teks\n",
    "        print(f\"\\nMODEL: {name}\")\n",
    "        print(\"-\" * 20)\n",
    "        print(classification_report(y_test, y_pred, target_names=['Lewati', 'Tonton']))\n",
    "        \n",
    "        # Plot 1: Confusion Matrix\n",
    "        cm = confusion_matrix(y_test, y_pred)\n",
    "        disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['Lewati', 'Tonton'])\n",
    "        disp.plot(cmap='Blues', ax=axes[0, i], values_format='d', colorbar=False)\n",
    "        axes[0, i].set_title(f'Confusion Matrix - {name}')\n",
    "        axes[0, i].grid(False)\n",
    "\n",
    "        # Plot 2: ROC Curve\n",
    "        y_prob = model.predict_proba(X_test)[:, 1]\n",
    "        fpr, tpr, _ = roc_curve(y_test, y_prob)\n",
    "        roc_auc = auc(fpr, tpr)\n",
    "        \n",
    "        axes[1, i].plot(fpr, tpr, color='darkorange', lw=2, label=f'AUC = {roc_auc:.2f}')\n",
    "        axes[1, i].plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
    "        axes[1, i].set_xlim([0.0, 1.0])\n",
    "        axes[1, i].set_ylim([0.0, 1.05])\n",
    "        axes[1, i].set_xlabel('False Positive Rate')\n",
    "        axes[1, i].set_ylabel('True Positive Rate')\n",
    "        axes[1, i].set_title(f'ROC Curve - {name}')\n",
    "        axes[1, i].legend(loc=\"lower right\")\n",
    "\n",
    "    # --- 6. PERBANDINGAN FINAL (BAR CHART) ---\n",
    "    # Ini memastikan KNN muncul berdampingan dengan RF di akhir\n",
    "    \n",
    "    fig2, ax2 = plt.subplots(figsize=(10, 6))\n",
    "    x = np.arange(len(results_summary['Model']))\n",
    "    width = 0.35\n",
    "    \n",
    "    rects1 = ax2.bar(x - width/2, results_summary['Accuracy'], width, label='Akurasi', color='#3498db')\n",
    "    rects2 = ax2.bar(x + width/2, results_summary['F1-Score'], width, label='F1-Score (Kelas Tonton)', color='#2ecc71')\n",
    "\n",
    "    ax2.set_ylabel('Skor')\n",
    "    ax2.set_title('Perbandingan Performa Akhir: KNN vs Random Forest')\n",
    "    ax2.set_xticks(x)\n",
    "    ax2.set_xticklabels(results_summary['Model'])\n",
    "    ax2.set_ylim([0, 1.1])\n",
    "    ax2.legend()\n",
    "    \n",
    "    # Menambah label nilai di atas batang\n",
    "    def autolabel(rects):\n",
    "        for rect in rects:\n",
    "            height = rect.get_height()\n",
    "            ax2.annotate(f'{height:.2f}',\n",
    "                        xy=(rect.get_x() + rect.get_width() / 2, height),\n",
    "                        xytext=(0, 3),  # 3 points vertical offset\n",
    "                        textcoords=\"offset points\",\n",
    "                        ha='center', va='bottom')\n",
    "\n",
    "    autolabel(rects1)\n",
    "    autolabel(rects2)\n",
    "\n",
    "    # --- 7. FEATURE IMPORTANCE (Hanya RF) ---\n",
    "    # Kita jelaskan kenapa hanya RF yang punya ini\n",
    "    print(\"\\n\" + \"=\"*60)\n",
    "    print(\"ANALISIS FITUR TERPENTING (Khusus Random Forest)\")\n",
    "    print(\"(Catatan: KNN tidak memiliki fitur ini karena berbasis jarak)\")\n",
    "    print(\"=\"*60)\n",
    "    \n",
    "    rf_model = grid_rf.best_estimator_.named_steps['classifier']\n",
    "    preprocessor_step = grid_rf.best_estimator_.named_steps['preprocessor']\n",
    "    ohe_features = preprocessor_step.named_transformers_['cat']['onehot'].get_feature_names_out(categorical_features)\n",
    "    all_features = numeric_features + list(ohe_features)\n",
    "    \n",
    "    importances = rf_model.feature_importances_\n",
    "    indices = np.argsort(importances)[::-1][:10]\n",
    "    \n",
    "    plt.figure(figsize=(10, 5))\n",
    "    plt.title(\"Top 10 Fitur Paling Berpengaruh (Random Forest Only)\")\n",
    "    plt.barh(range(len(indices)), importances[indices], color='#e74c3c', align='center')\n",
    "    plt.yticks(range(len(indices)), [all_features[i] for i in indices])\n",
    "    plt.xlabel('Importance')\n",
    "    plt.gca().invert_yaxis()\n",
    "    \n",
    "    print(\"Menampilkan semua grafik evaluasi...\")\n",
    "    plt.show()\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f541285b-b045-41e3-9550-800c7a192678",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
