{"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":7391,"databundleVersionId":44328,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nprint(os.listdir(\"/kaggle/input\"))\nprint(os.listdir(\"/kaggle/input/favorita-grocery-sales-forecasting\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:25:01.332171Z","iopub.execute_input":"2025-11-29T07:25:01.332485Z","iopub.status.idle":"2025-11-29T07:25:01.343579Z","shell.execute_reply.started":"2025-11-29T07:25:01.332462Z","shell.execute_reply":"2025-11-29T07:25:01.342640Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install py7zr --quiet\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:25:01.396381Z","iopub.execute_input":"2025-11-29T07:25:01.396827Z","iopub.status.idle":"2025-11-29T07:25:06.149338Z","shell.execute_reply.started":"2025-11-29T07:25:01.396790Z","shell.execute_reply":"2025-11-29T07:25:06.147902Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import py7zr\nimport pandas as pd\nimport os\n\nDATA_DIR = \"/kaggle/input/favorita-grocery-sales-forecasting\"\nWORK_DIR = \"/kaggle/working\"\n\nfiles_to_extract = [\n    \"train.csv.7z\",\n    \"holidays_events.csv.7z\",\n    \"oil.csv.7z\",\n    \"stores.csv.7z\"\n]\n\nfor fname in files_to_extract:\n    src_path = os.path.join(DATA_DIR, fname)\n    print(\"Extracting:\", src_path)\n    with py7zr.SevenZipFile(src_path, mode=\"r\") as z:\n        z.extractall(path=WORK_DIR)\n\nprint(\"Done. Files in /kaggle/working:\", os.listdir(WORK_DIR))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:25:06.151922Z","iopub.execute_input":"2025-11-29T07:25:06.152255Z","iopub.status.idle":"2025-11-29T07:26:38.604661Z","shell.execute_reply.started":"2025-11-29T07:25:06.152221Z","shell.execute_reply":"2025-11-29T07:26:38.603486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\nWORK_DIR = \"/kaggle/working\"\n\ntrain = pd.read_csv(os.path.join(WORK_DIR, \"train.csv\"), parse_dates=[\"date\"])\nholidays = pd.read_csv(os.path.join(WORK_DIR, \"holidays_events.csv\"), parse_dates=[\"date\"])\noil = pd.read_csv(os.path.join(WORK_DIR, \"oil.csv\"), parse_dates=[\"date\"])\nstores = pd.read_csv(os.path.join(WORK_DIR, \"stores.csv\"))\n\nprint(\"train:\", train.shape)\nprint(train.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:38.606252Z","iopub.execute_input":"2025-11-29T07:26:38.606904Z","iopub.status.idle":"2025-11-29T07:28:22.831380Z","shell.execute_reply.started":"2025-11-29T07:26:38.606865Z","shell.execute_reply":"2025-11-29T07:28:22.830143Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Xử lí data","metadata":{}},{"cell_type":"code","source":"# sort by store, item, date\ndf = train.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# optional: chọn một subset nhỏ để phát triển prototype\nsample_stores = df[\"store_nbr\"].unique()[:5]\nsample_items = df[\"item_nbr\"].unique()[:50]\n\ndf = df[df[\"store_nbr\"].isin(sample_stores) & df[\"item_nbr\"].isin(sample_items)].copy()\ndf = df.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# xử lý unit_sales âm (do return) → đơn giản nhất: clamp về 0\ndf[\"unit_sales\"] = df[\"unit_sales\"].clip(lower=0)\n\ndf.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:28:22.832642Z","iopub.execute_input":"2025-11-29T07:28:22.833083Z","iopub.status.idle":"2025-11-29T07:29:26.184845Z","shell.execute_reply.started":"2025-11-29T07:28:22.833054Z","shell.execute_reply":"2025-11-29T07:29:26.183401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\nWORK_DIR = \"/kaggle/working\"\n\n# giả sử bạn đã có df được tạo từ train, ví dụ:\n# df = train.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# 1) đọc lại oil từ file gốc cho chắc\noil = pd.read_csv(os.path.join(WORK_DIR, \"oil.csv\"), parse_dates=[\"date\"])\nprint(\"oil columns:\", oil.columns)\nprint(oil.head())\n\n# 2) merge trực tiếp oil vào df\n# sau merge, df sẽ có cột 'dcoilwtico'\ndf = df.merge(oil, on=\"date\", how=\"left\")\n\n# 3) đổi tên 'dcoilwtico' → 'oil_price'\ndf = df.rename(columns={\"dcoilwtico\": \"oil_price\"})\n\n# 4) kiểm tra đã có oil_price chưa\nprint(df[[\"date\", \"oil_price\"]].head())\n\n# 5) xử lý missing bằng interpolate + bfill + ffill\ns = df[\"oil_price\"].interpolate()\ns = s.bfill()\ns = s.ffill()\ndf[\"oil_price\"] = s\n\n# 6) tạo time features\ndf[\"dow\"] = df[\"date\"].dt.dayofweek\ndf[\"weekofyear\"] = df[\"date\"].dt.isocalendar().week.astype(int)\ndf[\"month\"] = df[\"date\"].dt.month\ndf[\"year\"] = df[\"date\"].dt.year\n\ndf.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:26.187490Z","iopub.execute_input":"2025-11-29T07:29:26.187830Z","iopub.status.idle":"2025-11-29T07:29:26.343880Z","shell.execute_reply.started":"2025-11-29T07:29:26.187804Z","shell.execute_reply":"2025-11-29T07:29:26.342733Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# đảm bảo sort đúng\ndf = df.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# optional: giới hạn bớt để chạy nhanh lúc dev\nsample_stores = df[\"store_nbr\"].unique()[:5]\nsample_items = df[\"item_nbr\"].unique()[:50]\n\ndf = df[df[\"store_nbr\"].isin(sample_stores) & df[\"item_nbr\"].isin(sample_items)].copy()\ndf = df.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# unit_sales không âm\ndf[\"unit_sales\"] = df[\"unit_sales\"].clip(lower=0)\n\n# tạo lag features\nfor lag in [1, 7, 14]:\n    df[f\"lag_{lag}\"] = df.groupby([\"store_nbr\", \"item_nbr\"])[\"unit_sales\"].shift(lag)\n\n# bỏ các dòng đầu tiên bị NaN do lag\ndf = df.dropna(subset=[\"lag_1\", \"lag_7\", \"lag_14\"]).reset_index(drop=True)\n\ndf.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:26.345141Z","iopub.execute_input":"2025-11-29T07:29:26.345477Z","iopub.status.idle":"2025-11-29T07:29:26.604417Z","shell.execute_reply.started":"2025-11-29T07:29:26.345450Z","shell.execute_reply":"2025-11-29T07:29:26.603381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# bỏ bớt cột dầu dư thừa nếu còn\nfor col in [\"oil_price_x\", \"oil_price_y\"]:\n    if col in df.columns:\n        df = df.drop(columns=col)\n\n# đảm bảo kiểu dữ liệu onpromotion là số, không phải string\ndf[\"onpromotion\"] = df[\"onpromotion\"].fillna(0).astype(\"int8\")\n\n# kiểm tra nhanh\nprint(df[[\"date\", \"store_nbr\", \"item_nbr\", \"unit_sales\", \"onpromotion\", \"oil_price\", \"lag_1\", \"lag_7\", \"lag_14\"]].head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:26.605534Z","iopub.execute_input":"2025-11-29T07:29:26.605891Z","iopub.status.idle":"2025-11-29T07:29:26.682058Z","shell.execute_reply.started":"2025-11-29T07:29:26.605858Z","shell.execute_reply":"2025-11-29T07:29:26.680994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import mean_squared_error\nimport numpy as np\nimport lightgbm as lgb\n\n# đảm bảo sort đúng\ndf = df.sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\n# Chia train / validation theo thời gian (30 ngày cuối làm validation)\n# CHỖ MỚI\nVALID_DAYS = 120  # bạn có thể dùng 60, 90, 120 tùy sức máy\n\ncutoff_date = df[\"date\"].max() - pd.Timedelta(days=VALID_DAYS)\nprint(\"Cutoff date:\", cutoff_date)\n\ntrain_df = df[df[\"date\"] <= cutoff_date].copy()\nvalid_df = df[df[\"date\"] > cutoff_date].copy()\n\nprint(\"Train size:\", train_df.shape)\nprint(\"Valid size:\", valid_df.shape)\n\n\n# Chọn features cho baseline\nfeatures = [\n    \"dow\", \"weekofyear\", \"month\", \"year\",\n    \"lag_1\", \"lag_7\", \"lag_14\",\n    \"onpromotion\", \"oil_price\"\n]\n\nX_train = train_df[features]\ny_train = train_df[\"unit_sales\"]\n\nX_valid = valid_df[features]\ny_valid = valid_df[\"unit_sales\"]\n\n# Khởi tạo và train LightGBM\nmodel = lgb.LGBMRegressor(\n    n_estimators=500,\n    learning_rate=0.05,\n    objective=\"regression\",\n    random_state=42\n)\n\nmodel.fit(X_train, y_train)\n\n# Dự báo cho validation\nvalid_pred = model.predict(X_valid)\n\nrmse = np.sqrt(mean_squared_error(y_valid, valid_pred))\nprint(\"Baseline validation RMSE:\", rmse)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:26.683112Z","iopub.execute_input":"2025-11-29T07:29:26.683458Z","iopub.status.idle":"2025-11-29T07:29:31.142285Z","shell.execute_reply.started":"2025-11-29T07:29:26.683427Z","shell.execute_reply":"2025-11-29T07:29:31.141165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"valid_df = valid_df.copy()\nvalid_df[\"baseline_p50\"] = valid_pred\nvalid_df[\"baseline_p90\"] = valid_df[\"baseline_p50\"] * 1.2  # tạm thời nhân 1.2 làm p90\n\nvalid_df[[\"date\", \"store_nbr\", \"item_nbr\", \"unit_sales\", \"baseline_p50\", \"baseline_p90\"]].head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:31.143212Z","iopub.execute_input":"2025-11-29T07:29:31.143448Z","iopub.status.idle":"2025-11-29T07:29:31.160561Z","shell.execute_reply.started":"2025-11-29T07:29:31.143431Z","shell.execute_reply":"2025-11-29T07:29:31.159566Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 4.1 Holiday_text: mô tả khoảng cách tới ngày lễ gần nhất\n\nkeep_types = [\"Holiday\", \"Additional\", \"Transfer\"]\nholidays_small = holidays[holidays[\"type\"].isin(keep_types)].copy()\nholidays_small[\"description_clean\"] = holidays_small[\"description\"].str.replace(\"_\", \" \")\n\ndef nearest_holiday_text(current_date, hol_df):\n    diffs = hol_df[\"date\"] - current_date\n    if diffs.isna().all():\n        return \"\"\n    idx = diffs.abs().idxmin()\n    h_row = hol_df.loc[idx]\n    \n    diff_days = (h_row[\"date\"] - current_date).days\n    diff_weeks = diff_days // 7\n    holiday_name = h_row[\"description_clean\"]\n    holiday_day_str = h_row[\"date\"].date().isoformat()\n    \n    if abs(diff_weeks) > 8:\n        return \"\"\n    \n    if diff_weeks > 0:\n        return f\"The holiday {holiday_name} on {holiday_day_str} is {diff_weeks} weeks after this date.\"\n    elif diff_weeks < 0:\n        return f\"The holiday {holiday_name} on {holiday_day_str} was {-diff_weeks} weeks before this date.\"\n    else:\n        return f\"This date is exactly the holiday {holiday_name} on {holiday_day_str}.\"\n\nvalid_df[\"holiday_text\"] = valid_df[\"date\"].apply(lambda d: nearest_holiday_text(d, holidays_small))\n\n\n# 4.2 Weather_text: thời tiết synthetic theo tháng\n\ndef weather_summary(date):\n    m = date.month\n    if m in [12, 1, 2]:\n        return \"Weather is hot and humid during this period.\"\n    if m in [6, 7, 8]:\n        return \"Cooler season with increased rainfall.\"\n    return \"Temperatures are mild with no extreme conditions expected.\"\n\nvalid_df[\"weather_text\"] = valid_df[\"date\"].apply(weather_summary)\n\n\n# 4.3 Promo_text: mô tả onpromotion\n\ndef promo_summary(onpromo):\n    if onpromo > 0:\n        return \"A promotion is active for this product on this date.\"\n    return \"No promotion activity is observed for this product on this date.\"\n\nvalid_df[\"promo_text\"] = valid_df[\"onpromotion\"].fillna(0).astype(int).apply(promo_summary)\n\n\n# 4.4 Product_text: mô tả synthetic cho item\n\ndef product_summary(row):\n    return f\"This item with ID {row['item_nbr']} is a common grocery product frequently purchased in this store.\"\n\nvalid_df[\"product_text\"] = valid_df.apply(product_summary, axis=1)\n\nvalid_df[[\"date\", \"store_nbr\", \"item_nbr\", \"holiday_text\", \"weather_text\", \"promo_text\", \"product_text\"]].head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:31.161742Z","iopub.execute_input":"2025-11-29T07:29:31.162105Z","iopub.status.idle":"2025-11-29T07:29:40.453277Z","shell.execute_reply.started":"2025-11-29T07:29:31.162073Z","shell.execute_reply":"2025-11-29T07:29:40.452298Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final_cols = [\n    \"date\",\n    \"store_nbr\",\n    \"item_nbr\",\n    \"unit_sales\",\n    \"onpromotion\",\n    \"oil_price\",\n    \"baseline_p50\",\n    \"baseline_p90\",\n    \"holiday_text\",\n    \"weather_text\",\n    \"promo_text\",\n    \"product_text\"\n]\n\nfinal_df = valid_df[final_cols].sort_values([\"store_nbr\", \"item_nbr\", \"date\"]).reset_index(drop=True)\n\nprint(final_df.head())\nprint(final_df.info())\n\nfinal_path = \"/kaggle/working/final_favorita_llm_dataset.csv\"\nfinal_df.to_csv(final_path, index=False)\nprint(\"Saved final dataset to:\", final_path)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:40.454472Z","iopub.execute_input":"2025-11-29T07:29:40.454859Z","iopub.status.idle":"2025-11-29T07:29:40.876155Z","shell.execute_reply.started":"2025-11-29T07:29:40.454829Z","shell.execute_reply":"2025-11-29T07:29:40.875231Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\n\nfinal_path = \"/kaggle/working/final_favorita_llm_dataset.csv\"\n\ndf = pd.read_csv(final_path, parse_dates=[\"date\"])\nprint(df)\nprint(df.info())\nprint(df[\"weather_text\"].unique())\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:29:40.877053Z","iopub.execute_input":"2025-11-29T07:29:40.877305Z","iopub.status.idle":"2025-11-29T07:29:40.998440Z","shell.execute_reply.started":"2025-11-29T07:29:40.877285Z","shell.execute_reply":"2025-11-29T07:29:40.997383Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# chỉ dùng những quan sát có baseline và sales dương\ndf = df[(df[\"baseline_p50\"] > 0) & (df[\"unit_sales\"] > 0)].copy()\n\n# lambda = log(y / y_hat_base)\ndf[\"lambda\"] = np.log(df[\"unit_sales\"] / df[\"baseline_p50\"])\n\ndf[[\"date\", \"store_nbr\", \"item_nbr\", \"unit_sales\", \"baseline_p50\", \"lambda\"]].head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:31:21.485508Z","iopub.execute_input":"2025-11-29T07:31:21.486041Z","iopub.status.idle":"2025-11-29T07:31:21.523487Z","shell.execute_reply.started":"2025-11-29T07:31:21.486005Z","shell.execute_reply":"2025-11-29T07:31:21.522367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_prompt(row):\n    return (\n        \"You are a forecasting assistant that corrects retail demand forecasts.\\n\"\n        f\"Baseline forecast: {row['baseline_p50']:.3f} units.\\n\"\n        f\"Holiday info: {row['holiday_text']}\\n\"\n        f\"Weather info: {row['weather_text']}\\n\"\n        f\"Promotion info: {row['promo_text']}\\n\"\n        f\"Product description: {row['product_text']}\\n\"\n        \"Predict the log scaling factor lambda that corrects the baseline forecast towards the true sales.\"\n    )\n\ndf[\"prompt\"] = df.apply(build_prompt, axis=1)\n\ndf[[\"prompt\", \"lambda\"]].head(2)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:31:51.755650Z","iopub.execute_input":"2025-11-29T07:31:51.756501Z","iopub.status.idle":"2025-11-29T07:31:52.287983Z","shell.execute_reply.started":"2025-11-29T07:31:51.756465Z","shell.execute_reply":"2025-11-29T07:31:52.287025Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df, test_df = train_test_split(\n    df,\n    test_size=0.2,\n    random_state=42\n)\n\nprint(\"Train size:\", train_df.shape, \"Test size:\", test_df.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:47:17.488301Z","iopub.execute_input":"2025-11-29T07:47:17.488641Z","iopub.status.idle":"2025-11-29T07:47:17.532658Z","shell.execute_reply.started":"2025-11-29T07:47:17.488615Z","shell.execute_reply":"2025-11-29T07:47:17.531482Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install transformers accelerate -q\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:47:51.440929Z","iopub.execute_input":"2025-11-29T07:47:51.441318Z","iopub.status.idle":"2025-11-29T07:49:32.158522Z","shell.execute_reply.started":"2025-11-29T07:47:51.441293Z","shell.execute_reply":"2025-11-29T07:49:32.156947Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import AutoTokenizer, AutoModelForSequenceClassification\nfrom transformers import Trainer, TrainingArguments\nimport torch\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:55:55.550769Z","iopub.execute_input":"2025-11-29T07:55:55.551823Z","iopub.status.idle":"2025-11-29T07:56:35.834188Z","shell.execute_reply.started":"2025-11-29T07:55:55.551788Z","shell.execute_reply":"2025-11-29T07:56:35.833272Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install \"protobuf==3.20.3\" --force-reinstall -q\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T08:00:44.867439Z","iopub.execute_input":"2025-11-29T08:00:44.868987Z","iopub.status.idle":"2025-11-29T08:00:50.967964Z","shell.execute_reply.started":"2025-11-29T08:00:44.868944Z","shell.execute_reply":"2025-11-29T08:00:50.966623Z"}},"outputs":[],"execution_count":null}]}