{"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":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Explore and prepare the data","metadata":{}},{"cell_type":"markdown","source":"## Preprocessing","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\nimport os\n\n# Duyệt toàn bộ thư mục con bên trong thư mục gốc (file zip)\nfor dirname, _, filenames in os.walk('/kaggle/input/favorita-grocery-sales-forecasting'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:11:19.749182Z","iopub.execute_input":"2025-11-04T09:11:19.749404Z","iopub.status.idle":"2025-11-04T09:11:20.155063Z","shell.execute_reply.started":"2025-11-04T09:11:19.749374Z","shell.execute_reply":"2025-11-04T09:11:20.153891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install py7zr","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:11:20.156117Z","iopub.execute_input":"2025-11-04T09:11:20.156641Z","iopub.status.idle":"2025-11-04T09:11:27.009466Z","shell.execute_reply.started":"2025-11-04T09:11:20.156615Z","shell.execute_reply":"2025-11-04T09:11:27.008443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import py7zr\nfrom subprocess import check_output\n\nfor dirname, _, filenames in os.walk('/kaggle/input/favorita-grocery-sales-forecasting'):\n    for filename in filenames:\n        # Mở lần lượt từng file zip\n        archive = py7zr.SevenZipFile(os.path.join(dirname, filename), mode='r')\n        # Giải nén lần lượt từng file zip rồi xuất ra output\n        archive.extractall(path=\"/kaggle/working\")\n        archive.close()\n\n# Giải nén thành công thì in ra màn hình để thông báo\nprint(check_output([\"ls\", \"../working\"]).decode(\"utf8\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:11:27.011076Z","iopub.execute_input":"2025-11-04T09:11:27.012268Z","iopub.status.idle":"2025-11-04T09:12:53.508676Z","shell.execute_reply.started":"2025-11-04T09:11:27.012223Z","shell.execute_reply":"2025-11-04T09:12:53.507623Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Đọc từng file csv\ntrain = pd.read_csv('../working/train.csv', parse_dates=['date'], low_memory = False)\ntest = pd.read_csv('../working/test.csv', parse_dates=['date'])\noil = pd.read_csv('../working/oil.csv', parse_dates=['date'])\nstores = pd.read_csv('../working/stores.csv')\nitems = pd.read_csv('../working/items.csv')\ntransactions = pd.read_csv('../working/transactions.csv', parse_dates=['date'])\nholidays = pd.read_csv('../working/holidays_events.csv', parse_dates=['date'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:12:53.509793Z","iopub.execute_input":"2025-11-04T09:12:53.510462Z","iopub.status.idle":"2025-11-04T09:14:43.664912Z","shell.execute_reply.started":"2025-11-04T09:12:53.510431Z","shell.execute_reply":"2025-11-04T09:14:43.663627Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"oil['dcoilwtico'] = oil['dcoilwtico'].interpolate()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:14:43.666030Z","iopub.execute_input":"2025-11-04T09:14:43.666371Z","iopub.status.idle":"2025-11-04T09:14:43.674836Z","shell.execute_reply.started":"2025-11-04T09:14:43.666336Z","shell.execute_reply":"2025-11-04T09:14:43.673906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"oil['dcoilwtico'] = oil['dcoilwtico'].bfill()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:14:43.677930Z","iopub.execute_input":"2025-11-04T09:14:43.678181Z","iopub.status.idle":"2025-11-04T09:14:43.702740Z","shell.execute_reply.started":"2025-11-04T09:14:43.678161Z","shell.execute_reply":"2025-11-04T09:14:43.701728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"oil.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:14:43.703577Z","iopub.execute_input":"2025-11-04T09:14:43.703884Z","iopub.status.idle":"2025-11-04T09:14:43.727870Z","shell.execute_reply.started":"2025-11-04T09:14:43.703845Z","shell.execute_reply":"2025-11-04T09:14:43.726981Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:14:43.728901Z","iopub.execute_input":"2025-11-04T09:14:43.729227Z","iopub.status.idle":"2025-11-04T09:14:51.011731Z","shell.execute_reply.started":"2025-11-04T09:14:43.729196Z","shell.execute_reply":"2025-11-04T09:14:51.010845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['onpromotion'] = train['onpromotion'].fillna('False')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:14:51.012699Z","iopub.execute_input":"2025-11-04T09:14:51.013021Z","iopub.status.idle":"2025-11-04T09:15:07.836355Z","shell.execute_reply.started":"2025-11-04T09:14:51.012991Z","shell.execute_reply":"2025-11-04T09:15:07.834610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:07.838103Z","iopub.execute_input":"2025-11-04T09:15:07.838447Z","iopub.status.idle":"2025-11-04T09:15:15.550015Z","shell.execute_reply.started":"2025-11-04T09:15:07.838425Z","shell.execute_reply":"2025-11-04T09:15:15.549069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:15.550906Z","iopub.execute_input":"2025-11-04T09:15:15.551166Z","iopub.status.idle":"2025-11-04T09:15:15.573536Z","shell.execute_reply.started":"2025-11-04T09:15:15.551146Z","shell.execute_reply":"2025-11-04T09:15:15.572675Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom statsmodels.tsa.statespace.sarimax import SARIMAX\nfrom sklearn.metrics import mean_absolute_error, mean_squared_error\nfrom scipy.stats import boxcox\nfrom scipy.special import inv_boxcox\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:15.574436Z","iopub.execute_input":"2025-11-04T09:15:15.574746Z","iopub.status.idle":"2025-11-04T09:15:16.779746Z","shell.execute_reply.started":"2025-11-04T09:15:15.574724Z","shell.execute_reply":"2025-11-04T09:15:16.778988Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature Engineering","metadata":{}},{"cell_type":"code","source":"# Kiểm tra số lượng sản phẩm\nnum_items = train['item_nbr'].nunique()\nprint(\"Số lượng sản phẩm khác nhau:\", num_items)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:16.780767Z","iopub.execute_input":"2025-11-04T09:15:16.781273Z","iopub.status.idle":"2025-11-04T09:15:17.784432Z","shell.execute_reply.started":"2025-11-04T09:15:16.781245Z","shell.execute_reply":"2025-11-04T09:15:17.783409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tính tổng doanh thu theo item_nbr\nitem_sales = train.groupby('item_nbr')['unit_sales'].sum().sort_values(ascending=False)\n\n# Lấy top 100 sản phẩm có doanh thu cao nhất\ntop_100_items = item_sales.head(100).index.tolist()\n\n# Tạo tập dữ liệu train_top chỉ chứa các sản phẩm này\ntrain_top = train[train['item_nbr'].isin(top_100_items)].copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:17.785558Z","iopub.execute_input":"2025-11-04T09:15:17.785931Z","iopub.status.idle":"2025-11-04T09:15:24.467485Z","shell.execute_reply.started":"2025-11-04T09:15:17.785911Z","shell.execute_reply":"2025-11-04T09:15:24.466430Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Chuyển kiểu dữ liệu ngày\ntrain_top['date'] = pd.to_datetime(train_top['date'])\n\n# Loại bỏ giá trị âm hoặc bất thường trong unit_sales\ntrain_top = train_top[train_top['unit_sales'] >= 0]\n\n# Chuyển onpromotion về kiểu bool\ntrain_top['onpromotion'] = train_top['onpromotion'].map({'False': False, 'True': True}).fillna(False).astype('bool')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:24.468568Z","iopub.execute_input":"2025-11-04T09:15:24.468829Z","iopub.status.idle":"2025-11-04T09:15:25.832878Z","shell.execute_reply.started":"2025-11-04T09:15:24.468809Z","shell.execute_reply":"2025-11-04T09:15:25.832015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_top)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:25.833883Z","iopub.execute_input":"2025-11-04T09:15:25.834169Z","iopub.status.idle":"2025-11-04T09:15:25.845862Z","shell.execute_reply.started":"2025-11-04T09:15:25.834147Z","shell.execute_reply":"2025-11-04T09:15:25.844863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tạo danh sách ngày đầy đủ từ min đến max\nfull_dates = pd.date_range(start=train_top['date'].min(), end=train_top['date'].max())\n\nactual_dates = train_top['date'].drop_duplicates().sort_values()\n\nmissing_dates = full_dates.difference(actual_dates)\n\n# In ra các ngày bị thiếu (nếu có)\nprint(\"Số ngày bị thiếu:\", len(missing_dates))\nprint(\"Các ngày bị thiếu:\", missing_dates)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:25.847096Z","iopub.execute_input":"2025-11-04T09:15:25.847378Z","iopub.status.idle":"2025-11-04T09:15:25.977862Z","shell.execute_reply.started":"2025-11-04T09:15:25.847355Z","shell.execute_reply":"2025-11-04T09:15:25.976805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Danh sách ngày thiếu\nmissing_dates = pd.to_datetime(['2013-12-25', '2014-12-25', '2015-12-25', '2016-12-25'])\n\n# Lấy danh sách store và item đã có\nstores = train_top['store_nbr'].unique()\nitems = train_top['item_nbr'].unique()\n\n# Tạo tổ hợp đầy đủ cho các ngày thiếu\nfrom itertools import product\nmissing_index = pd.DataFrame(product(missing_dates, stores, items), columns=['date', 'store_nbr', 'item_nbr'])\n\n# Gán giá trị mặc định\nmissing_index['unit_sales'] = 0.0\nmissing_index['onpromotion'] = False\n\n# Gộp dữ liệu bị thiếu vào train_top\ntrain_top = pd.concat([train_top, missing_index], ignore_index=True)\n\n# Sắp xếp lại theo thời gian để đảm bảo đúng thứ tự chuỗi\ntrain_top = train_top.sort_values(['date', 'store_nbr', 'item_nbr']).reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:25.979068Z","iopub.execute_input":"2025-11-04T09:15:25.979426Z","iopub.status.idle":"2025-11-04T09:15:27.407116Z","shell.execute_reply.started":"2025-11-04T09:15:25.979396Z","shell.execute_reply":"2025-11-04T09:15:27.406111Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tạo đặc trưng thời gian cho toàn bộ dữ liệu\ntrain_top['day_of_week'] = train_top['date'].dt.dayofweek.astype('int8')\ntrain_top['week_of_year'] = train_top['date'].dt.isocalendar().week.astype('int8')\ntrain_top['month'] = train_top['date'].dt.month.astype('int8')\ntrain_top['year'] = train_top['date'].dt.year.astype('int16')\ntrain_top['is_weekend'] = train_top['day_of_week'].isin([5, 6])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:27.408108Z","iopub.execute_input":"2025-11-04T09:15:27.408384Z","iopub.status.idle":"2025-11-04T09:15:28.316471Z","shell.execute_reply.started":"2025-11-04T09:15:27.408361Z","shell.execute_reply":"2025-11-04T09:15:28.315770Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_top)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:28.317420Z","iopub.execute_input":"2025-11-04T09:15:28.317672Z","iopub.status.idle":"2025-11-04T09:15:28.329287Z","shell.execute_reply.started":"2025-11-04T09:15:28.317653Z","shell.execute_reply":"2025-11-04T09:15:28.328562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_top.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:28.333642Z","iopub.execute_input":"2025-11-04T09:15:28.333938Z","iopub.status.idle":"2025-11-04T09:15:28.498483Z","shell.execute_reply.started":"2025-11-04T09:15:28.333918Z","shell.execute_reply":"2025-11-04T09:15:28.497703Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_top.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:28.499472Z","iopub.execute_input":"2025-11-04T09:15:28.499797Z","iopub.status.idle":"2025-11-04T09:15:28.658983Z","shell.execute_reply.started":"2025-11-04T09:15:28.499776Z","shell.execute_reply":"2025-11-04T09:15:28.658251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tạo danh sách ngày đầy đủ từ min đến max\nfull_dates = pd.date_range(start=train_top['date'].min(), end=train_top['date'].max())\n\nactual_dates = train_top['date'].drop_duplicates().sort_values()\n\nmissing_dates = full_dates.difference(actual_dates)\n\n# In ra các ngày bị thiếu (nếu có)\nprint(\"Số ngày bị thiếu:\", len(missing_dates))\nprint(\"Các ngày bị thiếu:\", missing_dates)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:28.659902Z","iopub.execute_input":"2025-11-04T09:15:28.660195Z","iopub.status.idle":"2025-11-04T09:15:28.778083Z","shell.execute_reply.started":"2025-11-04T09:15:28.660174Z","shell.execute_reply":"2025-11-04T09:15:28.776989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Ép kiểu để giảm bộ nhớ trước khi merge\noil['dcoilwtico'] = oil['dcoilwtico'].astype('float32')\n\n# Merge giá dầu vào train\ntrain_top = train_top.merge(oil[['date', 'dcoilwtico']], on='date', how='left')\n\n# Xử lý thiếu giá dầu sau khi merge (do train có ngày không khớp với oil)\ntrain_top['dcoilwtico'] = train_top['dcoilwtico'].interpolate(method='linear')\ntrain_top['dcoilwtico'] = train_top['dcoilwtico'].fillna(method='ffill').fillna(method='bfill')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:28.779235Z","iopub.execute_input":"2025-11-04T09:15:28.779595Z","iopub.status.idle":"2025-11-04T09:15:29.853869Z","shell.execute_reply.started":"2025-11-04T09:15:28.779565Z","shell.execute_reply":"2025-11-04T09:15:29.853002Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Merge ngày lễ\nholidays['date'] = pd.to_datetime(holidays['date'])\nholidays['transferred'] = holidays['transferred'].astype('bool')\nholidays['type'] = holidays['type'].astype('category')\nholidays['locale'] = holidays['locale'].astype('category')\nholidays['locale_name'] = holidays['locale_name'].astype('category')\n\nregular = holidays[holidays['transferred'] == False]\ntransfer_map = holidays[holidays['type'] == 'Transfer'][['description', 'date']]\ntransferred = holidays[holidays['transferred'] == True][['description', 'locale', 'locale_name']]\ntransferred = transferred.merge(transfer_map, on='description', how='left')\nholiday_actual = pd.concat([\n    regular[['date', 'locale', 'locale_name']],\n    transferred[['date', 'locale', 'locale_name']]\n], ignore_index=True)\nholiday_actual['is_holiday'] = True\n\n# Đọc lại stores.csv để đảm bảo là DataFrame\nstores = pd.read_csv('../working/stores.csv')\n\n# Merge city từ stores\nstores['store_nbr'] = stores['store_nbr'].astype('int8')\nstores['city'] = stores['city'].astype('category')\ntrain_top = train_top.merge(stores[['store_nbr', 'city']], on='store_nbr', how='left')\n\n# Merge ngày lễ theo city\ntrain_top = train_top.merge(\n    holiday_actual,\n    left_on=['date', 'city'],\n    right_on=['date', 'locale_name'],\n    how='left'\n)\ntrain_top['is_holiday'] = train_top['is_holiday'].fillna(False).astype('bool')\ntrain_top.drop(columns=['locale', 'locale_name'], inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:29.854766Z","iopub.execute_input":"2025-11-04T09:15:29.855017Z","iopub.status.idle":"2025-11-04T09:15:33.091416Z","shell.execute_reply.started":"2025-11-04T09:15:29.854998Z","shell.execute_reply":"2025-11-04T09:15:33.090632Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_top)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:33.092355Z","iopub.execute_input":"2025-11-04T09:15:33.092621Z","iopub.status.idle":"2025-11-04T09:15:33.103925Z","shell.execute_reply.started":"2025-11-04T09:15:33.092601Z","shell.execute_reply":"2025-11-04T09:15:33.102790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_top.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:33.104819Z","iopub.execute_input":"2025-11-04T09:15:33.105148Z","iopub.status.idle":"2025-11-04T09:15:33.570963Z","shell.execute_reply.started":"2025-11-04T09:15:33.105084Z","shell.execute_reply":"2025-11-04T09:15:33.569774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_top.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:33.572002Z","iopub.execute_input":"2025-11-04T09:15:33.572319Z","iopub.status.idle":"2025-11-04T09:15:34.032221Z","shell.execute_reply.started":"2025-11-04T09:15:33.572299Z","shell.execute_reply":"2025-11-04T09:15:34.031266Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Chia tập train-test","metadata":{}},{"cell_type":"code","source":"# Sắp xếp theo thời gian\ntrain_top = train_top.sort_values('date')\n\n# Chia theo thời gian (không shuffle)\nsplit_date = train_top['date'].quantile(0.8)\ntrain_set = train_top[train_top['date'] <= split_date]\ntest_set = train_top[train_top['date'] > split_date]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:34.033139Z","iopub.execute_input":"2025-11-04T09:15:34.033387Z","iopub.status.idle":"2025-11-04T09:15:35.273697Z","shell.execute_reply.started":"2025-11-04T09:15:34.033369Z","shell.execute_reply":"2025-11-04T09:15:35.272625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_set)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:35.274816Z","iopub.execute_input":"2025-11-04T09:15:35.275168Z","iopub.status.idle":"2025-11-04T09:15:35.286186Z","shell.execute_reply.started":"2025-11-04T09:15:35.275140Z","shell.execute_reply":"2025-11-04T09:15:35.285259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(test_set)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:35.287064Z","iopub.execute_input":"2025-11-04T09:15:35.287383Z","iopub.status.idle":"2025-11-04T09:15:35.309686Z","shell.execute_reply.started":"2025-11-04T09:15:35.287356Z","shell.execute_reply":"2025-11-04T09:15:35.308878Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Chuẩn hóa giá trị đầu vào","metadata":{}},{"cell_type":"code","source":"# Chuẩn hóa giá trị đầu vào\nfrom sklearn.preprocessing import MinMaxScaler\n\nscaler = MinMaxScaler()\ncols_to_scale = ['unit_sales', 'dcoilwtico']\n\n# Fit trên tập train để học min/max\nscaler.fit(train_set[cols_to_scale])\n\n# Tạo biến mới chứa dữ liệu đã chuẩn hóa\ntrain_scaled = train_set.copy()\ntest_scaled = test_set.copy()\n\ntrain_scaled[cols_to_scale] = scaler.transform(train_set[cols_to_scale])\ntest_scaled[cols_to_scale] = scaler.transform(test_set[cols_to_scale])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:35.310692Z","iopub.execute_input":"2025-11-04T09:15:35.310978Z","iopub.status.idle":"2025-11-04T09:15:35.776213Z","shell.execute_reply.started":"2025-11-04T09:15:35.310948Z","shell.execute_reply":"2025-11-04T09:15:35.775468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_scaled)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:35.777168Z","iopub.execute_input":"2025-11-04T09:15:35.777448Z","iopub.status.idle":"2025-11-04T09:15:35.788432Z","shell.execute_reply.started":"2025-11-04T09:15:35.777428Z","shell.execute_reply":"2025-11-04T09:15:35.787344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(test_scaled)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:15:35.789527Z","iopub.execute_input":"2025-11-04T09:15:35.789848Z","iopub.status.idle":"2025-11-04T09:15:35.811649Z","shell.execute_reply.started":"2025-11-04T09:15:35.789821Z","shell.execute_reply":"2025-11-04T09:15:35.810720Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## SARIMAX","metadata":{}},{"cell_type":"code","source":"from statsmodels.tsa.statespace.sarimax import SARIMAX\nfrom sklearn.metrics import mean_absolute_error, mean_squared_error\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\n\n# Tổng hợp theo tuần\ntrain_set['week'] = train_set['date'] - pd.to_timedelta(train_set['date'].dt.dayofweek, unit='d')\ntest_set['week'] = test_set['date'] - pd.to_timedelta(test_set['date'].dt.dayofweek, unit='d')\n\n# Khởi tạo danh sách lưu kết quả\nmae_list, rmse_list, mape_list = [], [], []\nitem_ids = []\n\n# Lặp qua từng sản phẩm\nfor item in tqdm(train_set['item_nbr'].unique()):\n    try:\n        # Lọc dữ liệu theo item\n        train_grp = train_set[train_set['item_nbr'] == item]\n        test_grp = test_set[test_set['item_nbr'] == item]\n\n        # Tổng hợp theo tuần\n        train_weekly = train_grp.groupby('week').agg({\n            'unit_sales': 'sum',\n            'onpromotion': 'mean',\n            'dcoilwtico': 'mean',\n            'is_holiday': 'mean'\n        }).reset_index()\n\n        test_weekly = test_grp.groupby('week').agg({\n            'unit_sales': 'sum',\n            'onpromotion': 'mean',\n            'dcoilwtico': 'mean',\n            'is_holiday': 'mean'\n        }).reset_index()\n\n        # Kiểm tra đủ dữ liệu\n        if len(train_weekly) < 10 or len(test_weekly) < 5:\n            continue\n\n        # Biến đầu ra và biến ngoại sinh\n        train_y = train_weekly['unit_sales']\n        train_exog = train_weekly[['onpromotion', 'dcoilwtico', 'is_holiday']]\n        test_exog = test_weekly[['onpromotion', 'dcoilwtico', 'is_holiday']]\n\n        # Huấn luyện mô hình\n        model = SARIMAX(train_y, exog=train_exog, order=(1,1,1), seasonal_order=(1,1,1,52),\n                        enforce_stationarity=False, enforce_invertibility=False)\n        results = model.fit(disp=False)\n\n        # Dự báo\n        forecast = results.predict(start=len(train_y), end=len(train_y)+len(test_exog)-1, exog=test_exog)\n\n        # Đánh giá\n        y_true = test_weekly['unit_sales'].values\n        y_pred = forecast.values\n\n        mae = mean_absolute_error(y_true, y_pred)\n        rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n        mape = np.mean(np.abs((y_true - y_pred) / (y_true + 1e-5))) * 100\n\n        mae_list.append(mae)\n        rmse_list.append(rmse)\n        mape_list.append(mape)\n        item_ids.append(item)\n\n    except Exception as e:\n        print(\"Lỗi\")\n        continue  # Bỏ qua nếu lỗi\n\n# Tổng hợp kết quả\nresults_df = pd.DataFrame({\n    'item_nbr': item_ids,\n    'MAE': mae_list,\n    'RMSE': rmse_list,\n    'MAPE': mape_list\n})\n\n# In chỉ số trung bình\nprint(\"MAE trung bình:\", results_df['MAE'].mean())\nprint(\"RMSE trung bình:\", results_df['RMSE'].mean())\nprint(\"MAPE trung bình:\", results_df['MAPE'].mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:16:45.322174Z","iopub.execute_input":"2025-11-04T09:16:45.322702Z","iopub.status.idle":"2025-11-04T09:48:53.658722Z","shell.execute_reply.started":"2025-11-04T09:16:45.322677Z","shell.execute_reply":"2025-11-04T09:48:53.656616Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(results_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:51:16.858470Z","iopub.execute_input":"2025-11-04T09:51:16.859089Z","iopub.status.idle":"2025-11-04T09:51:16.875165Z","shell.execute_reply.started":"2025-11-04T09:51:16.859057Z","shell.execute_reply":"2025-11-04T09:51:16.873895Z"}},"outputs":[],"execution_count":null}]}