{"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":130062,"databundleVersionId":15606741,"sourceType":"competition"},{"sourceId":14766305,"sourceType":"datasetVersion","datasetId":9438580}],"dockerImageVersionId":31259,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Loading and previewing the csvs....","metadata":{}},{"cell_type":"code","source":"import pandas as pd \nimport numpy as np\n\ntest_raw = pd.read_csv(\"/kaggle/input/bidding-predictions-for-construction/raw_test.csv\")\ntrain_raw = pd.read_csv(\"/kaggle/input/bidding-predictions-for-construction/raw_train.csv\")\ntest = pd.read_csv(\"/kaggle/input/bidding-predictions-for-construction/test.csv\")\n\ntrain_summary = pd.read_csv(\"/kaggle/input/bidding-predictions-for-construction/train_summary.csv\")\n\n# run these at ur discretion to preview the datasets! :)\n# test_raw.head()\n# train_raw.head()\n# train_summary.head()\n# test.head()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:17:52.023473Z","iopub.execute_input":"2026-02-07T21:17:52.023853Z","iopub.status.idle":"2026-02-07T21:17:54.395075Z","shell.execute_reply.started":"2026-02-07T21:17:52.023814Z","shell.execute_reply":"2026-02-07T21:17:54.393987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"dataset looks nicely organized. moving on...\n\nnext, we create some temporal features and one hot encode the categoricals. These are the chosen temporal ones: relative day of week relative day of month relative day of year relative year of full time period.","metadata":{}},{"cell_type":"code","source":"train_summary[\"row_id\"] = train_summary[\"job_id\"] + \"__\" + train_summary[\"contractor_id\"]\ntest[\"row_id\"] = test[\"job_id\"] + \"__\" + test[\"contractor_id\"]\n\n\nfor df in [train_summary, test]:\n    df[\"bid_date\"] = pd.to_datetime(df[\"bid_date\"])\n    df[\"bid_year\"] = df[\"bid_date\"].dt.year\n    df[\"bid_month\"] = df[\"bid_date\"].dt.month\n    df[\"bid_day\"] = df[\"bid_date\"].dt.day\n    df[\"bid_dow\"] = df[\"bid_date\"].dt.dayofweek\n    df[\"bid_week\"] = df[\"bid_date\"].dt.isocalendar().week.astype(int)\n    df[\"bid_date_ordinal\"] = df[\"bid_date\"].map(pd.Timestamp.toordinal)\n\n\ndef build_raw_agg(raw_df):\n    agg_num = raw_df.groupby(\"row_id\").agg(\n        quantity_sum=(\"quantity\", \"sum\"),\n        quantity_mean=(\"quantity\", \"mean\"),\n        quantity_std=(\"quantity\", \"std\"),\n        quantity_min=(\"quantity\", \"min\"),\n        quantity_max=(\"quantity\", \"max\"),\n        num_pay_items_mean=(\"num_pay_items\", \"mean\"),\n        num_pay_items_max=(\"num_pay_items\", \"max\"),\n        num_pay_items_min=(\"num_pay_items\", \"min\"),\n    )\n\n\n    agg_cat = raw_df.groupby(\"row_id\").agg(\n        n_pay_item_id=(\"pay_item_id\", \"nunique\"),\n        n_pay_item_desc=(\"pay_item_description\", \"nunique\"),\n        n_unit_english=(\"unit_english_id\", \"nunique\"),\n        n_category_id=(\"category_id\", \"nunique\"),\n        n_category_desc=(\"category_description\", \"nunique\"),\n        n_job_category=(\"job_category_description\", \"nunique\"),\n    )\n\n\n    agg_cnt = raw_df.groupby(\"row_id\").size().to_frame(\"n_line_items\")\n\n\n    agg = pd.concat([agg_num, agg_cat, agg_cnt], axis=1)\n    return agg\n\ntrain_raw_agg = build_raw_agg(train_raw)\ntest_raw_agg = build_raw_agg(test_raw)\n\ntrain_merged = train_summary.merge(train_raw_agg, on=\"row_id\", how=\"left\")\ntest_merged = test.merge(test_raw_agg, on=\"row_id\", how=\"left\")\n\ntrain_merged.to_csv(\"my_train_dataset.csv\", index=False)\ntest_merged.to_csv(\"my_test_dataset.csv\", index=False)\n\nprint(\"saved: my_train_dataset.csv and my_test_dataset.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:17:54.396601Z","iopub.execute_input":"2026-02-07T21:17:54.396939Z","iopub.status.idle":"2026-02-07T21:17:55.607813Z","shell.execute_reply.started":"2026-02-07T21:17:54.396910Z","shell.execute_reply":"2026-02-07T21:17:55.606595Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_merged.head(15)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:17:55.608877Z","iopub.execute_input":"2026-02-07T21:17:55.609168Z","iopub.status.idle":"2026-02-07T21:17:55.631956Z","shell.execute_reply.started":"2026-02-07T21:17:55.609140Z","shell.execute_reply":"2026-02-07T21:17:55.630921Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Next, ive decided to add in something new (as recommended by the competition host) which is to transform [DEFLATE] the target column, based on inflation. Ive also added the inflation index as a column to use as a feature, incase its helpful, but, the main reason i keep it as a column is so that I can access it during the final model script. The specific input for inflation index comes from a dataset downloaded from fred.stlouisfed.org \n\n","metadata":{}},{"cell_type":"code","source":"inflation = pd.read_csv(\"/kaggle/input/inflation/inflation.csv\")\ninflation[\"observation_date\"] = pd.to_datetime(inflation[\"observation_date\"])\ninflation[\"year\"] = inflation[\"observation_date\"].dt.year\ninflation[\"month\"] = inflation[\"observation_date\"].dt.month\ninflation = inflation.rename(columns={\"CPIAUCSL\": \"inflation_index\"})\n\ninflation.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:17:55.634752Z","iopub.execute_input":"2026-02-07T21:17:55.635379Z","iopub.status.idle":"2026-02-07T21:17:55.660118Z","shell.execute_reply.started":"2026-02-07T21:17:55.635345Z","shell.execute_reply":"2026-02-07T21:17:55.658994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_merged = pd.read_csv(\"my_train_dataset.csv\")\ntest_merged = pd.read_csv(\"my_test_dataset.csv\")\n\ninflation = pd.read_csv(\"/kaggle/input/inflation/inflation.csv\")\ninflation[\"observation_date\"] = pd.to_datetime(inflation[\"observation_date\"])\ninflation[\"year\"] = inflation[\"observation_date\"].dt.year\ninflation[\"month\"] = inflation[\"observation_date\"].dt.month\ninflation = inflation.rename(columns={\"CPIAUCSL\": \"inflation_index\"})\n\ninflation = inflation.sort_values([\"year\", \"month\"])\ninflation[\"inflation_index\"] = inflation[\"inflation_index\"].ffill().bfill()\n\ntrain_merged = train_merged.merge(\n    inflation[[\"year\",\"month\",\"inflation_index\"]],\n    left_on=[\"bid_year\",\"bid_month\"],\n    right_on=[\"year\",\"month\"],\n    how=\"left\"\n).drop(columns=[\"year\",\"month\"])\n\ntest_merged = test_merged.merge(\n    inflation[[\"year\",\"month\",\"inflation_index\"]],\n    left_on=[\"bid_year\",\"bid_month\"],\n    right_on=[\"year\",\"month\"],\n    how=\"left\"\n).drop(columns=[\"year\",\"month\"])\n\ntrain_merged[\"inflation_index\"] = train_merged[\"inflation_index\"].ffill().bfill()\ntest_merged[\"inflation_index\"] = test_merged[\"inflation_index\"].ffill().bfill()\n\nbase_index = inflation.loc[inflation[\"observation_date\"].idxmax(), \"inflation_index\"]\n\ntrain_merged[\"total_bid_real\"] = (\n    train_merged[\"total_bid\"] * (base_index / train_merged[\"inflation_index\"])\n)\n\ntrain_merged.to_csv(\"my_train_dataset_inflated.csv\", index=False)\ntest_merged.to_csv(\"my_test_dataset_inflated.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:11.679739Z","iopub.execute_input":"2026-02-07T21:19:11.680121Z","iopub.status.idle":"2026-02-07T21:19:11.957352Z","shell.execute_reply.started":"2026-02-07T21:19:11.680091Z","shell.execute_reply":"2026-02-07T21:19:11.956398Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Scaling:\nnumeric features: z score\ntemporal features: transformed into \n    - relative day (day / number of days in entire time period that the dataset encompasses)\n    - relative day in year (day / total number of days in that year) \n    - relative day in month (day / total number of days in that month)","metadata":{}},{"cell_type":"code","source":"train_scaled = pd.read_csv(\"my_train_dataset_inflated.csv\")\ntest_scaled = pd.read_csv(\"my_test_dataset_inflated.csv\")\n\ntrain_scaled[\"bid_date\"] = pd.to_datetime(train_scaled[\"bid_date\"])\ntest_scaled[\"bid_date\"] = pd.to_datetime(test_scaled[\"bid_date\"])\n\nmin_date = train_scaled[\"bid_date\"].min()\nmax_date = train_scaled[\"bid_date\"].max()\ndate_range_days = max((max_date - min_date).days, 1)\n\nfor df in [train_scaled, test_scaled]:\n    df[\"day_relative_to_full_time_period\"] = (\n        (df[\"bid_date\"] - min_date).dt.days / date_range_days\n    ).clip(0, 1)\n\n    day_of_year = df[\"bid_date\"].dt.dayofyear\n    days_in_year = df[\"bid_date\"].dt.is_leap_year.astype(int).map({1: 366, 0: 365})\n    df[\"day_relative_to_full_year\"] = day_of_year / days_in_year\n\n    df[\"day_relative_to_month\"] = df[\"bid_date\"].dt.day / df[\"bid_date\"].dt.days_in_month\n    df[\"day_relative_to_week\"] = df[\"bid_date\"].dt.dayofweek / 6\n\ndate_cols_to_drop = [\n    \"bid_date\", \"bid_year\", \"bid_month\", \"bid_day\", \"bid_dow\", \"bid_week\", \"bid_date_ordinal\"\n]\nfor col in date_cols_to_drop:\n    if col in train_scaled.columns:\n        train_scaled.drop(columns=col, inplace=True)\n    if col in test_scaled.columns:\n        test_scaled.drop(columns=col, inplace=True)\n\ndate_ratio_cols = [\n    \"day_relative_to_full_time_period\",\n    \"day_relative_to_full_year\",\n    \"day_relative_to_month\"\n]\n\ntarget_col = \"total_bid_real\"\nexclude_cols = set(date_ratio_cols + [target_col, \"inflation_index\", \"total_bid\"])\n\ntrain_num_cols = set(train_scaled.select_dtypes(include=[np.number]).columns)\ntest_num_cols = set(test_scaled.select_dtypes(include=[np.number]).columns)\n\nnum_cols = sorted(list((train_num_cols & test_num_cols) - exclude_cols))\n\nmeans = train_scaled[num_cols].mean()\nstds = train_scaled[num_cols].std().replace(0, 1)\n\ntrain_scaled[num_cols] = (train_scaled[num_cols] - means) / stds\ntest_scaled[num_cols] = (test_scaled[num_cols] - means) / stds\n\ntrain_scaled.to_csv(\"train_scaled.csv\", index=False)\ntest_scaled.to_csv(\"test_scaled.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:15.094697Z","iopub.execute_input":"2026-02-07T21:19:15.095074Z","iopub.status.idle":"2026-02-07T21:19:15.626670Z","shell.execute_reply.started":"2026-02-07T21:19:15.095044Z","shell.execute_reply":"2026-02-07T21:19:15.625694Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_scaled = pd.read_csv(\"train_scaled.csv\")\ntest_scaled = pd.read_csv(\"test_scaled.csv\")\n\nhigh_card_cols = [\"job_id\", \"contractor_id\", \"primary_location\"]\nfor col in high_card_cols:\n    if col in train_scaled.columns:\n        all_vals = pd.concat([train_scaled[col], test_scaled[col]], axis=0).astype(str)\n        codes, uniques = pd.factorize(all_vals, sort=True)\n        train_scaled[col] = codes[:len(train_scaled)]\n        test_scaled[col] = codes[len(train_scaled):]\n\nlow_card_cols = [\"job_category_description\"]\ntrain_encoded = pd.get_dummies(train_scaled, columns=low_card_cols, drop_first=False)\ntest_encoded = pd.get_dummies(test_scaled, columns=low_card_cols, drop_first=False)\n\ntrain_encoded, test_encoded = train_encoded.align(\n    test_encoded, join=\"left\", axis=1, fill_value=0\n)\n\ntarget_col = \"total_bid_real\"\nif target_col not in train_encoded.columns and target_col in train_scaled.columns:\n    train_encoded[target_col] = train_scaled[target_col]\n\nbool_cols = train_encoded.select_dtypes(include=[\"bool\"]).columns\ntrain_encoded[bool_cols] = train_encoded[bool_cols].astype(int)\n\nbool_cols_test = test_encoded.select_dtypes(include=[\"bool\"]).columns\ntest_encoded[bool_cols_test] = test_encoded[bool_cols_test].astype(int)\n\ntrain_encoded.to_csv(\"train_encoded.csv\", index=False)\ntest_encoded.to_csv(\"test_encoded.csv\", index=False)\n\nprint(\"Saved: train_encoded.csv and test_encoded.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:19.272076Z","iopub.execute_input":"2026-02-07T21:19:19.272412Z","iopub.status.idle":"2026-02-07T21:19:19.839149Z","shell.execute_reply.started":"2026-02-07T21:19:19.272376Z","shell.execute_reply":"2026-02-07T21:19:19.838267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id_cols = [\"row_id\", \"job_id\", \"contractor_id\"]\ntrain_encoded = train_encoded.drop(columns=[c for c in id_cols if c in train_encoded.columns])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:22.449663Z","iopub.execute_input":"2026-02-07T21:19:22.450027Z","iopub.status.idle":"2026-02-07T21:19:22.458521Z","shell.execute_reply.started":"2026-02-07T21:19:22.449998Z","shell.execute_reply":"2026-02-07T21:19:22.457516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport lightgbm as lgb\nfrom sklearn.model_selection import KFold\n\ntrain_df = train_encoded\ntest_df = test_encoded\n\nif \"row_id\" in test_df.columns:\n    test_ids = test_df[\"row_id\"].astype(str)\nelse:\n    test_ids = pd.read_csv(\"/kaggle/input/bidding-predictions-for-construction/test.csv\")\n    test_ids = test_ids[\"job_id\"].astype(str) + \"__\" + test_ids[\"contractor_id\"].astype(str)\n\nbase_index = inflation.loc[inflation[\"observation_date\"].idxmax(), \"inflation_index\"]\n\ntarget_col = \"total_bid_real\"\ndrop_cols = [target_col, \"total_bid\", \"row_id\", \"job_id\", \"contractor_id\"]\n\nX = train_df.drop(columns=[c for c in drop_cols if c in train_df.columns])\ny = train_df[target_col].values\n\nX_test = test_df.drop(columns=[c for c in drop_cols if c in test_df.columns])\n\nX, X_test = X.align(X_test, join=\"left\", axis=1, fill_value=0)\n\ndef rmsle_eval(preds, dataset):\n    y_true = dataset.get_label()\n    preds = np.maximum(preds, 0)\n    return \"rmsle\", np.sqrt(np.mean((np.log1p(preds) - np.log1p(y_true))**2)), False\n\nparams = {\n    \"objective\": \"regression\",\n    \"metric\": \"None\",\n    \"learning_rate\": 0.002,\n    \"num_leaves\": 188,\n    \"feature_fraction\": 0.7,\n    \"min_data_in_leaf\": 22,\n    \"bagging_fraction\": 0.78,\n    \"bagging_freq\": 3,\n    \"seed\": 42,\n    \"shrinkage_rate\": 0.1,\n    \"verbosity\": -1\n}\n\nkf = KFold(n_splits=5, shuffle=True, random_state=42)\nfold_scores = []\nbest_iters = []\n\nfor fold, (tr_idx, va_idx) in enumerate(kf.split(X), 1):\n    X_tr, X_va = X.iloc[tr_idx], X.iloc[va_idx]\n    y_tr, y_va = y[tr_idx], y[va_idx]\n\n    dtrain = lgb.Dataset(X_tr, label=y_tr)\n    dvalid = lgb.Dataset(X_va, label=y_va)\n\n    model = lgb.train(\n        params,\n        dtrain,\n        valid_sets=[dvalid],\n        feval=rmsle_eval,\n        num_boost_round=50000,\n        callbacks=[\n            lgb.early_stopping(300, verbose=True),\n            lgb.log_evaluation(100)\n        ]\n    )\n\n    preds = np.maximum(model.predict(X_va, num_iteration=model.best_iteration), 0)\n    rmsle = np.sqrt(np.mean((np.log1p(preds) - np.log1p(y_va))**2))\n    fold_scores.append(rmsle)\n    best_iters.append(model.best_iteration)\n\nbest_iter = int(np.mean(best_iters))\n\ndtrain_full = lgb.Dataset(X, label=y)\nfinal_model = lgb.train(\n    params,\n    dtrain_full,\n    feval=rmsle_eval,\n    num_boost_round=best_iter\n)\n\ntest_preds_real = np.maximum(final_model.predict(X_test), 0)\ntest_preds_nominal = test_preds_real * (test_df[\"inflation_index\"].values / base_index)\n\nsubmission = pd.DataFrame({\n    \"row_id\": test_ids.values,\n    \"total_bid\": test_preds_nominal\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Saved: submission.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:34.042698Z","iopub.execute_input":"2026-02-07T21:19:34.043089Z","iopub.status.idle":"2026-02-07T21:19:46.191048Z","shell.execute_reply.started":"2026-02-07T21:19:34.043060Z","shell.execute_reply":"2026-02-07T21:19:46.189885Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:19:47.294043Z","iopub.execute_input":"2026-02-07T21:19:47.294412Z","iopub.status.idle":"2026-02-07T21:19:47.304534Z","shell.execute_reply.started":"2026-02-07T21:19:47.294376Z","shell.execute_reply":"2026-02-07T21:19:47.303436Z"}},"outputs":[],"execution_count":null},{"cell_type":"raw","source":"","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}