{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":84896,"databundleVersionId":10305135,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">imports</p>","metadata":{}},{"cell_type":"code","source":"!pip install -q scikit-learn==1.5.2","metadata":{"trusted":true,"_kg_hide-output":true,"_kg_hide-input":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nfrom pathlib import Path\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"_kg_hide-input":false,"_kg_hide-output":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport polars as pl\nimport pandas as pd\nimport plotly.colors as pc\nimport plotly.express as px\nimport plotly.graph_objects as go","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import plotly.io as pio\npio.renderers.default = 'iframe'","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd.options.display.max_columns = None","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from catboost import CatBoostRegressor\nfrom sklearn.model_selection import KFold\nfrom sklearn.metrics import root_mean_squared_log_error as rmsle","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">configuration class</p>","metadata":{}},{"cell_type":"code","source":"class CFG:\n\n    train_path = Path('/kaggle/input/playground-series-s4e12/train.csv')\n    test_path = Path('/kaggle/input/playground-series-s4e12/test.csv')\n    subm_path = Path('/kaggle/input/playground-series-s4e12/sample_submission.csv')\n\n    batch_size = 262144\n    color = '#EADDCA'\n\n    early_stop = 200\n    n_splits = 5\n\n    ctb_params = {\n        'bootstrap_type': 'Bernoulli',\n        'loss_function': 'RMSE',\n        'learning_rate': 0.03,\n        'num_trees': 20000,\n        'random_state': 42,\n        'task_type': 'GPU',\n        'subsample': 0.85,\n        'reg_lambda': 8.0,\n        'depth': 10\n    }","metadata":{"trusted":true,"_kg_hide-input":false},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">feature engineering</p>","metadata":{}},{"cell_type":"code","source":"class FE:\n\n    def __init__(self, batch_size):\n        self.batch_size = batch_size\n\n    def load_data(self, path):\n\n        return pl.read_csv(path, batch_size=self.batch_size).drop('id')\n\n    def cast_datatypes(self, df):\n\n        cat_cols =  [\n            'Gender',\n            'Marital Status',\n            'Education Level',\n            'Occupation',\n            'Location',\n            'Policy Type',\n            'Policy Start Date',\n            'Customer Feedback',\n            'Smoking Status',\n            'Exercise Frequency',\n            'Property Type'            \t\t\t\t\n        ]\n\n        '''\n        Replace missing values with:\n\n        `-1` for numeric columns\n        `Unknown` for categorical columns\n        '''\n\n        for col in df.columns:\n\n            if col in cat_cols:\n                df = df.with_columns(pl.col(col).fill_null('Unknown').cast(pl.String))\n\n            else:\n                df = df.with_columns(pl.col(col).fill_null(-1).cast(pl.Float32))  \n\n        return df   \n\n    def process_data(self, df):\n\n        df = df.with_columns([\n            \n            pl.when(pl.col('Gender').eq('Male')).then(0).otherwise(1)\n            .cast(pl.Int8).alias('Gender'),\n            \n            pl.when(pl.col('Smoking Status').eq('No')).then(0).otherwise(1)\n            .cast(pl.Int8).alias('Smoking Status')\n            \n        ])\n\n        # Regex pattern for date column (Policy Start Date): Year (4 digits) - Month (2 digits) - Day (2 digits)\n        pattern = r\"(\\d{4})-(\\d{2})-(\\d{2})\"\n    \n        df = df.with_columns([\n            pl.col('Policy Start Date').str.extract(pattern, 1).cast(pl.Int16).alias(\"Policy Start Year\"),\n            pl.col('Policy Start Date').str.extract(pattern, 2).cast(pl.Int8).alias(\"Policy Start Month\"),\n            pl.col('Policy Start Date').str.extract(pattern, 3).cast(pl.Int8).alias(\"Policy Start Day\"),\n        ])\n\n        return df.drop('Policy Start Date')\n\n    def info(self, df):\n        \n        print(f'Shape of dataframe: {df.shape}') \n        \n        mem = df.memory_usage().sum() / 1024**2\n        print('Memory usage: {:.2f} MB\\n'.format(mem))\n\n        display(df.head())\n\n    def apply_fe(self, path):\n\n        df = self.load_data(path)\n        df = self.cast_datatypes(df)\n        df = self.process_data(df)\n        df = df.to_pandas()\n\n        self.info(df)\n        \n        cat_cols = [col for col in df.columns if df[col].dtype == pl.String]\n\n        return df, cat_cols","metadata":{"trusted":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fe = FE(CFG.batch_size)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data, cat_cols = fe.apply_fe(CFG.train_path)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_data, _ = fe.apply_fe(CFG.test_path)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">exploratory data analysis</p>","metadata":{}},{"cell_type":"code","source":"class EDA:\n    \n    def __init__(self, color, df):\n        self.color = color  \n        self.df = df  \n\n    def template(self, fig, title):\n        \n        fig.update_layout(\n            title=title,\n            title_x=0.5, \n            plot_bgcolor='rgba(40, 40, 43, 1)',  \n            paper_bgcolor='rgba(40, 40, 43, 1)', \n            font=dict(color=self.color),\n            margin=dict(l=72, r=72, t=72, b=72), \n            height=720\n        )\n        \n        return fig\n\n    def distribution_plot(self, col, color):\n        \n        fig = px.histogram(\n            self.df,\n            x=col,\n            nbins=100,\n            title=f'Distribution of {col}',\n            color_discrete_sequence=[color]\n        )\n        \n        fig.update_layout(\n            xaxis_title=col,\n            yaxis_title='Count',\n            bargap=0.1,\n            xaxis=dict(gridcolor='grey'),\n            yaxis=dict(gridcolor='grey', zerolinecolor='grey')\n        )\n        \n        fig.update_traces(hovertemplate='Value: %{x:.0f}<br>Count: %{y:,}')\n        \n        fig = self.template(fig, f'Distribution of {col}')\n        fig.show()\n\n    def bubble_chart(self, col, colorscale):\n        \n        value_counts = self.df[col].value_counts().reset_index()\n        value_counts.columns = [col, 'count']\n        \n        fig = px.scatter(\n            value_counts,\n            x=col,\n            y='count',\n            size='count',\n            color='count',\n            size_max=180,\n            color_continuous_scale=colorscale,\n            labels={col: col.capitalize(), 'count': 'Count'},\n            hover_name=col,\n            hover_data={'count': True}\n        )\n        \n        fig.update_layout(\n            title_text=f'Bubble Chart of {col.capitalize()}',\n            title_x=0.5,\n            plot_bgcolor='rgba(40, 40, 43, 1)',\n            paper_bgcolor='rgba(40, 40, 43, 1)',\n            font=dict(color=self.color),\n            margin=dict(l=20, r=20, t=50, b=20),\n            xaxis_title=col.capitalize(),\n            yaxis_title='Count',\n            height=720\n        )\n        \n        fig.update_traces(\n            marker=dict(line=dict(width=1, color='DarkSlateGrey')),\n            hovertemplate=(\n                '<b>Value:</b> %{x}<br>'\n                '<b>Count:</b> %{y:,}<br>'\n            ),\n            hoverlabel=dict(\n                font=dict(color=self.color), \n                bgcolor='rgba(40, 40, 43, 1)'\n            )\n        )\n        \n        fig = self.template(fig, f'Bubble Chart of {col}')\n        fig.show()\n\n    def pie_chart(self, col):\n        \n        value_counts = self.df[col].value_counts().reset_index()\n        value_counts.columns = [col, 'count']\n        \n        fig = px.pie(\n            value_counts,\n            names=col,\n            values='count',\n            color=col,\n            color_discrete_sequence=px.colors.sequential.Redor,\n            title=f'Pie Chart of {col.capitalize()}',\n            hole=0.2\n        )\n        \n        fig.update_layout(\n            title_x=0.5,\n            plot_bgcolor='rgba(40, 40, 43, 1)',\n            paper_bgcolor='rgba(40, 40, 43, 1)',\n            font=dict(color=self.color),\n            margin=dict(l=20, r=20, t=50, b=20),\n            height=720\n        )\n        \n        fig.update_traces(\n            textinfo='percent+label',\n            hovertemplate=(\n                '<b>Value:</b> %{label}<br>'\n                '<b>Count:</b> %{value:,}<br>'\n                '<b>Percentage:</b> %{percent}<br>'\n            ),\n            hoverlabel=dict(\n                font=dict(color=self.color),\n                bgcolor='rgba(40, 40, 43, 1)'\n            )\n        )\n        \n        fig = self.template(fig, f'Pie Chart of {col}')\n        fig.show()","metadata":{"trusted":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda = EDA(CFG.color, train_data)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.distribution_plot(col='Premium Amount', color='#EADDCA') ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.pie_chart(col='Property Type')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.bubble_chart(col='Location', colorscale='Magenta')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.pie_chart(col='Number of Dependents')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.bubble_chart(col='Education Level', colorscale='Burg')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.bubble_chart(col='Occupation', colorscale='Sunset')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.distribution_plot(col='Annual Income', color='#C19A6B') ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.distribution_plot(col='Credit Score', color='#834333') ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.distribution_plot(col='Age', color='#B87333') ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.pie_chart(col='Marital Status')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.distribution_plot(col='Health Score', color='#C2B280') ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eda.bubble_chart(col='Exercise Frequency', colorscale='Sunsetdark')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">model development</p>","metadata":{}},{"cell_type":"code","source":"class MD:\n    \n    def __init__(self, early_stop, n_splits, color):\n        self.early_stop = early_stop\n        self.n_splits = n_splits\n        self.color = color\n        \n    def _plot_cv(self, fold_scores, title, metric='RMSLE'):\n        \n        fold_scores = [round(score, 3) for score in fold_scores]\n        mean_score = round(np.mean(fold_scores), 3)\n\n        fig = go.Figure()\n\n        fig.add_trace(go.Scatter(\n            x = list(range(1, len(fold_scores) + 1)),\n            y = fold_scores,\n            mode = 'markers', \n            name = 'Fold Scores',\n            marker = dict(size = 27, color=self.color, symbol='diamond'),\n            text = [f'{score:.3f}' for score in fold_scores],\n            hovertemplate = 'Fold %{x}: %{text}<extra></extra>',\n            hoverlabel=dict(font=dict(size=18))  \n        ))\n\n        fig.add_trace(go.Scatter(\n            x = [1, len(fold_scores)],\n            y = [mean_score, mean_score],\n            mode = 'lines',\n            name = f'Mean: {mean_score:.3f}',\n            line = dict(dash = 'dash', color = '#FFBF00'),\n            hoverinfo = 'none'\n        ))\n        \n        fig.update_layout(\n            title = f'{title} | Cross-validation {metric} scores: {mean_score}',\n            xaxis_title = 'Fold',\n            yaxis_title = f'{metric} Score',\n            plot_bgcolor = 'rgba(40, 40, 43, 1)',  \n            paper_bgcolor = 'rgba(40, 40, 43, 1)',\n            font = dict(color=self.color), \n            xaxis = dict(\n                gridcolor = 'grey',\n                tickmode = 'linear',\n                tick0 = 1,\n                dtick = 1,\n                range = [0.5, len(fold_scores) + 0.5],\n                zerolinecolor = 'grey'\n            ),\n            yaxis = dict(\n                gridcolor = 'grey',\n                zerolinecolor = 'grey'\n            )\n        )\n        \n        fig.show()\n        \n    def train_model(self, data, cat_cols, params, title):\n\n        for col in cat_cols:\n            data[col] = data[col].astype('category')\n            \n        X = data.drop(['Premium Amount'], axis=1)\n        y = data['Premium Amount']\n\n        cv = KFold(n_splits=self.n_splits, shuffle=True, random_state=42)\n\n        models, scores = [], []\n\n        # Initialize out-of-fold predictions array\n        oof_preds = np.zeros(len(X))\n\n        for fold, (train_index, valid_index) in enumerate(cv.split(X, y), 1):\n            \n            X_train, X_valid = X.iloc[train_index], X.iloc[valid_index]\n            y_train, y_valid = y.iloc[train_index], y.iloc[valid_index]\n\n            if title.startswith('LightGBM'):\n                \n                model = lgb.LGBMRegressor(**params)\n                \n                model.fit(X_train, y_train,\n                          eval_set=[(X_valid, y_valid)],\n                          eval_metric='rmse',\n                          callbacks=[lgb.early_stopping(self.early_stop, verbose=0), lgb.log_evaluation(0)])\n                \n            elif title.startswith('CatBoost'):\n                \n                model = CatBoostRegressor(**params, verbose=0, cat_features=cat_cols)\n                \n                model.fit(X_train, y_train,\n                          eval_set=(X_valid, y_valid),\n                          early_stopping_rounds=self.early_stop, verbose=0)\n                \n            models.append(model)\n\n            # Store out-of-fold predictions - Ensure predictions are non-negative\n            oof_preds[valid_index] = np.clip(model.predict(X_valid), 0, None)\n            \n            score = rmsle(y_valid, oof_preds[valid_index])\n            scores.append(score)\n\n        self._plot_cv(scores, title)\n        \n        return models\n\n    def infer_model(self, data, cat_cols, models):\n\n        for col in cat_cols:\n            data[col] = data[col].astype('category')\n\n        return np.mean([model.predict(data) for model in models], axis=0)","metadata":{"trusted":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"md = MD(CFG.early_stop, CFG.n_splits, CFG.color)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ctb_models = md.train_model(train_data, cat_cols, CFG.ctb_params, 'CatBoost')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<p style=\"background-color: #EADDCA; font-size: 300%; text-align: center; border-radius: 40px 40px; color: #C4A484; font-weight: bold; font-family: 'Cinzel', serif; text-transform: uppercase; border: 4px solid #C4A484; text-shadow: 2px 2px 0.5px rgba(0, 0, 0, 0.05);\">inference</p>","metadata":{}},{"cell_type":"code","source":"ctb_preds = md.infer_model(test_data, cat_cols, ctb_models)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subm_data = pd.read_csv(CFG.subm_path)\nsubm_data['Premium Amount'] = ctb_preds","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subm_data.to_csv('submission.csv', index=False)\ndisplay(subm_data.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}