{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":84896,"databundleVersionId":10305135,"sourceType":"competition"},{"sourceId":9178166,"sourceType":"datasetVersion","datasetId":5547076}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import warnings\nwarnings.simplefilter('ignore')\n\nimport pandas as pd\nimport numpy as np\nimport lightgbm as lgb\nfrom sklearn.model_selection import KFold\nfrom sklearn.metrics import mean_squared_error # Import for RMSLE calculation\nfrom itertools import combinations\nimport gc # Import the garbage collector\n\n# --- 1. Data Loading and Initial Preparation ---\n\n# Load the datasets\n# Make sure to adjust the path if you are not running this in a Kaggle environment\ntry:\n    train = pd.read_csv('/kaggle/input/playground-series-s4e12/train.csv')\n    test = pd.read_csv('/kaggle/input/playground-series-s4e12/test.csv')\n    # Load the external dataset\n    orig = pd.read_csv('/kaggle/input/insurance-premium-prediction/Insurance Premium Prediction Dataset.csv')\nexcept FileNotFoundError:\n    print(\"CSV files not found. Creating dummy data for demonstration.\")\n    # Create a dummy dataset if the original files are not available\n    def create_dummy_data(n_samples, name):\n        data = {}\n        data['id'] = range(n_samples)\n        CATS_dummy = ['Gender', 'Marital Status', 'Education Level', 'Occupation', 'Location', 'Policy Type', 'Customer Feedback', 'Smoking Status', 'Exercise Frequency', 'Property Type']\n        NUMS_dummy = ['Age', 'Annual Income', 'Number of Dependents', 'Health Score', 'Previous Claims', 'Vehicle Age', 'Credit Score', 'Insurance Duration']\n        for col in CATS_dummy:\n            data[col] = np.random.choice([f'{col}_A', f'{col}_B', f'{col}_C'], n_samples)\n        for col in NUMS_dummy:\n            data[col] = np.random.randint(0, 100, n_samples)\n        data['Policy Start Date'] = pd.to_datetime(pd.Timestamp('2023-01-01') + pd.to_timedelta(np.random.randint(0, 365*2, n_samples), 'D'))\n        if name in ['train', 'orig']:\n            data['Premium Amount'] = np.random.rand(n_samples) * 1000 + 50\n        return pd.DataFrame(data)\n    train = create_dummy_data(1000, 'train')\n    test = create_dummy_data(500, 'test')\n    orig = create_dummy_data(1200, 'orig')\n\n\nprint('Train Shape', train.shape)\nprint('Test Shape', test.shape)\nprint('Original Data Shape', orig.shape)\n\n\n# Define the target variable\nTARGET = 'Premium Amount'\n\n# --- [MODIFIED] Log-transform the target variable for RMSLE optimization ---\nprint(f\"\\nApplying log1p transformation to the target variable: {TARGET}\")\ntrain[TARGET] = np.log1p(train[TARGET])\n\n\n# Function to decompose the date feature\ndef date_feat(df):\n  \"\"\"Decomposes the 'Policy Start Date' into time-based features.\"\"\"\n  df['Policy Start Date'] = pd.to_datetime(df['Policy Start Date'])\n  df['Year'] = df['Policy Start Date'].dt.year\n  df['Month'] = df['Policy Start Date'].dt.month\n  df['Day'] = df['Policy Start Date'].dt.day\n  df['Dayofweek'] = df['Policy Start Date'].dt.dayofweek\n  return df\n\n# Apply the date feature decomposition to all datasets\ntrain = date_feat(train)\ntest = date_feat(test)\norig = date_feat(orig)\n\n# Identify categorical and numerical features\nCATS = train.select_dtypes(include='object').columns.tolist()\nNUMS = [col for col in train.select_dtypes(include='number').columns.tolist() if col not in ['id', TARGET]]\nprint(len(CATS), 'Categoricals:',CATS, '\\n')\nprint(len(NUMS), 'Numericals:',NUMS, '\\n')\nprint('-->', len(NUMS+CATS), 'Features')\n\n\n# --- 2. Memory-Efficient Feature Engineering (Unchanged) ---\n\nfeatures_to_combine = NUMS + CATS\next_te_features = []\n\nprint(f\"\\nGenerating External Target Encoding features on-the-fly...\")\n# Note: Feature engineering uses the original, non-transformed target from the 'orig' dataset\nglobal_mean_orig = orig['Premium Amount'].mean()\n\n# This loop now generates one interaction feature at a time, encodes it, and then deletes it.\nfor col1, col2 in combinations(features_to_combine, 2):\n    feature_name = f'{col1}_{col2}'\n    ext_te_name = f'EXT_TE_{feature_name}'\n    \n    # 1. Create temporary interaction feature on all three dataframes\n    train[feature_name] = train[col1].astype(str) + '_' + train[col2].astype(str)\n    test[feature_name] = test[col1].astype(str) + '_' + test[col2].astype(str)\n    orig[feature_name] = orig[col1].astype(str) + '_' + orig[col2].astype(str)\n    \n    # 2. Calculate mean of target in original data\n    agg = orig.groupby(feature_name)['Premium Amount'].agg('mean')\n    \n    # 3. Map the mean to train and test data to create the new feature\n    train[ext_te_name] = train[feature_name].map(agg).fillna(global_mean_orig)\n    test[ext_te_name] = test[feature_name].map(agg).fillna(global_mean_orig)\n    ext_te_features.append(ext_te_name)\n    \n    # 4. Drop the temporary interaction column from all dataframes to save memory\n    train = train.drop(feature_name, axis=1)\n    test = test.drop(feature_name, axis=1)\n    orig = orig.drop(feature_name, axis=1)\n\nprint(f\"Finished generating {len(ext_te_features)} external TE features.\")\n# Explicitly call the garbage collector to free up memory\ngc.collect()\n\n\n# --- 3. Cross-Validation, Modeling, and Prediction ---\n\n# KFold setup\nNFOLDS = 5\nkf = KFold(n_splits=NFOLDS, shuffle=True, random_state=42)\n\n# Placeholders for predictions (will store log-transformed values)\noof_preds = np.zeros(train.shape[0])\ntest_preds = np.zeros(test.shape[0])\nbest_iterations = []\n\n# LGBM parameters\nlgbm_params = {\n    'objective': 'regression_l1', 'metric': 'rmse', 'n_estimators': 2000,\n    'learning_rate': 0.01, 'feature_fraction': 0.8, 'bagging_fraction': 0.8,\n    'bagging_freq': 1, 'lambda_l1': 0.1, 'lambda_l2': 0.1,\n    'num_leaves': 31, 'verbose': -1, 'n_jobs': -1, 'seed': 42,\n    'boosting_type': 'gbdt',\n}\n\n# Define the features to be used in the model\nmodel_features = NUMS + ext_te_features\n\n# --- Start CV Loop ---\nprint(f\"\\nStarting {NFOLDS}-Fold CV...\")\nfor fold, (train_index, val_index) in enumerate(kf.split(train, train[TARGET])):\n    print(f\"===== FOLD {fold+1} =====\")\n    \n    X_train, X_val = train.iloc[train_index], train.iloc[val_index]\n    y_train, y_val = train[TARGET].iloc[train_index], train[TARGET].iloc[val_index]\n    \n    model = lgb.LGBMRegressor(**lgbm_params)\n    model.fit(X_train[model_features], y_train,\n              eval_set=[(X_val[model_features], y_val)],\n              eval_metric='rmse',\n              callbacks=[lgb.early_stopping(100, verbose=False)])\n    \n    val_preds = model.predict(X_val[model_features])\n    oof_preds[val_index] = val_preds\n    test_preds += model.predict(test[model_features]) / NFOLDS\n    best_iterations.append(model.best_iteration_)\n    \n    # --- [MODIFIED] Calculate and display RMSLE for the fold ---\n    # RMSE on log-transformed values is equivalent to RMSLE on original values\n    fold_rmsle = np.sqrt(mean_squared_error(y_val, val_preds))\n    print(f\"Fold {fold+1} RMSLE: {fold_rmsle:.5f}\")\n    print(f\"Fold {fold+1} Best Iteration: {model.best_iteration_}\")\n\n# --- 4. Final Model Training and Prediction ---\n\nfinal_iterations = int(np.mean(best_iterations) * 1.2)\nprint(f\"\\nAverage best iteration: {np.mean(best_iterations):.0f}\")\nprint(f\"Training final model with {final_iterations} iterations...\")\n\n# Use the same feature set for the final model\nfinal_model_features = NUMS + ext_te_features\nlgbm_params['n_estimators'] = final_iterations\n\nfinal_model = lgb.LGBMRegressor(**lgbm_params)\n# Train on the full dataset with the log-transformed target\nfinal_model.fit(train[final_model_features], train[TARGET])\n# Final predictions will be on the log scale\nfinal_test_predictions = final_model.predict(test[final_model_features])\n\n# --- 5. Save Results ---\n\n# --- [MODIFIED] Inverse transform predictions before saving ---\n# Apply expm1 to convert log-predictions back to the original scale\noof_df = pd.DataFrame({'id': train['id'], TARGET: np.expm1(oof_preds)})\noof_filename = 'oof_2way_ext_te_lgbm_rmsle.csv'\noof_df.to_csv(oof_filename, index=False)\nprint(f\"\\nOOF predictions saved to {oof_filename}\")\n\ntest_df = pd.DataFrame({'id': test['id'], TARGET: np.expm1(final_test_predictions)})\ntest_filename = 'test_2way_ext_te_lgbm_rmsle.csv'\ntest_df.to_csv(test_filename, index=False)\nprint(f\"Test predictions saved to {test_filename}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T00:26:48.449072Z","iopub.execute_input":"2025-08-19T00:26:48.449453Z","execution_failed":"2025-08-19T00:34:15.704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}