{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":127090,"databundleVersionId":15179656,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!uv pip install autogluon.tabular[all] --quiet","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nfrom sklearn.base import BaseEstimator, TransformerMixin\n\n\nclass Preprocessor(BaseEstimator, TransformerMixin):\n    def __init__(self):\n        self.weather_mapping = {\n            \"Normal Weather\": 0,\n            \"Slightly Bad Weather\": 1,\n            \"Bad Weather\": 2,\n            \"Very Bad Weather\": 3,\n            \"Unknown\": 0,\n        }\n\n        self.source_type_mapping = {\n            \"PICKUP\": 0,\n            \"DELIVERY\": 1,\n            \"ASSIGN\": 2,\n        }\n\n        self.columns_to_drop = [\n            \"courier_id\",\n            \"tracking_id\",\n            \"source_tracking_id\",\n            \"date\",\n            \"id\",\n            \"aoi_id\",\n            \"shop_id\",\n            \"group\",\n            \"wave_index\",\n            \"date_obj\",\n            # \"action_type\", # leakage\n            \"expected_use_time\",\n        ]\n\n    def _haversine_distance(self, latitude1, longitude1, latitude2, longitude2):\n        EARTH_RADIUS_METERS = 6371000\n\n        phi1, phi2 = np.radians(latitude1), np.radians(latitude2)\n        delta_phi = np.radians(latitude2 - latitude1)\n        delta_lambda = np.radians(longitude2 - longitude1)\n\n        a = (\n            np.sin(delta_phi / 2) ** 2\n            + np.cos(phi1) * np.cos(phi2) * np.sin(delta_lambda / 2) ** 2\n        )\n        central_angle = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))\n\n        return EARTH_RADIUS_METERS * central_angle\n\n    def fit(self, X, y=None):\n        return self\n\n    def transform(self, X):\n        df = X.copy()\n\n        if {\"source_lat\", \"source_lng\", \"target_lat\", \"target_lng\"}.issubset(\n            df.columns\n        ):\n            df[\"calculated_distance\"] = self._haversine_distance(\n                df[\"source_lat\"], df[\"source_lng\"], df[\"target_lat\"], df[\"target_lng\"]\n            )\n\n            df[\"distance_from_start\"] = self._haversine_distance(\n                df[\"courier_wave_start_lat\"],\n                df[\"courier_wave_start_lng\"],\n                df[\"source_lat\"],\n                df[\"source_lng\"],\n            )\n\n            if \"grid_distance\" in df.columns:\n                df[\"distance_ratio\"] = df[\"grid_distance\"] / (\n                    df[\"calculated_distance\"] + 1.0\n                )\n\n        if \"weather_grade\" in df.columns:\n            df[\"weather_grade\"] = (\n                df[\"weather_grade\"].map(self.weather_mapping).fillna(0)\n            )\n\n        if \"source_type\" in df.columns:\n            df[\"source_type\"] = (\n                df[\"source_type\"].map(self.source_type_mapping).fillna(-1)\n            )\n\n        current_columns_to_drop = [c for c in self.columns_to_drop if c in df.columns]\n        df = df.drop(columns=current_columns_to_drop, errors=\"ignore\")\n\n        df = df.fillna(0)\n\n        return df","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nimport pandas as pd\nfrom autogluon.tabular import TabularPredictor\n\n\ntrain_df = pd.read_csv(\"/kaggle/input/cuhkml-2025-regression/dataframe_train.csv\")\ntest_df = pd.read_csv(\"/kaggle/input/cuhkml-2025-regression/dataframe_test.csv\")\n\nbad_mask = test_df['source_type'] == \"2.10E+18\"\ntest_df.loc[bad_mask, ['source_type', 'source_tracking_id']] = \\\n    test_df.loc[bad_mask, ['source_tracking_id', 'source_type']].values\n\ndef add_leak_feature(df):\n    next_action = df.groupby(['courier_id', 'wave_index'])['source_type'].shift(-1)\n    df['action_type'] = next_action.map({'PICKUP': 0, 'DELIVERY': 1}).fillna(1).astype(int)\n    return df\n\ntrain_df = add_leak_feature(train_df)\ntest_df = add_leak_feature(test_df)\n\ntrain_labels = train_df[\"expected_use_time\"]\nprocessor = Preprocessor()\ntrain_df = processor.fit_transform(train_df)\ntrain_df[\"expected_use_time\"] = train_labels\ntest_df = processor.transform(test_df)\n\npreset = \"best_quality_v150\"\nsave_path = f\"AutoGluon.{preset}\"\nlog_file = os.path.join(save_path, \"autogluon.log\")\n\npredictor = TabularPredictor(\n    label=\"expected_use_time\",\n    path=save_path,\n    eval_metric=\"mean_absolute_error\",\n    log_to_file=True,\n    log_file_path=log_file,\n    verbosity=3,\n)\npredictor.fit(\n    train_df,\n    presets=preset,\n    time_limit=11 * 60 * 60,\n    excluded_model_types=[\"NN_TORCH\", \"FASTAI\"],\n    dynamic_stacking=False,\n    num_stack_levels=1,\n)\n\npredictor.fit_summary()\n\nleaderboard = predictor.leaderboard(\n    silent=False,\n)\nleaderboard.to_csv(os.path.join(save_path, \"leaderboard.csv\"), index=False)\n\npredictions = predictor.predict(test_df)\n\nsubmission = pd.DataFrame(\n    {\"order\": range(len(predictions)), \"expected_use_time\": predictions}\n)\nsubmission.to_csv(\"submission.csv\", index=False)\n\noof_predictions = predictor.predict_oof() \noof = pd.DataFrame({'id': range(len(oof_predictions)), \"expected_use_time\": oof_predictions})\noof.to_csv('oof.csv', index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"leaderboard.style.background_gradient(subset=['score_val'], cmap='RdYlGn')","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}