{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.13"},"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:28:33.479990Z","iopub.execute_input":"2025-11-17T13:28:33.480337Z","iopub.status.idle":"2025-11-17T13:28:35.755952Z","shell.execute_reply.started":"2025-11-17T13:28:33.480308Z","shell.execute_reply":"2025-11-17T13:28:35.755037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cài đặt giải nén file csv.7z\n!pip -q install py7zr","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:28:40.546369Z","iopub.execute_input":"2025-11-17T13:28:40.546771Z","iopub.status.idle":"2025-11-17T13:28:47.811573Z","shell.execute_reply.started":"2025-11-17T13:28:40.546748Z","shell.execute_reply":"2025-11-17T13:28:47.809899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import thư viện cần thiết\nimport pandas as pd\nimport warnings\nimport py7zr\nimport os, gc\nimport re\nfrom subprocess import check_output\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport polars as pl\nwarnings.filterwarnings('ignore', category=RuntimeWarning)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:28:47.813199Z","iopub.execute_input":"2025-11-17T13:28:47.813525Z","iopub.status.idle":"2025-11-17T13:28:49.831116Z","shell.execute_reply.started":"2025-11-17T13:28:47.813480Z","shell.execute_reply":"2025-11-17T13:28:49.830259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Danh sách các file CSV muốn giải nén\nfiles_to_extract = [\n    'train.csv',\n    'transactions.csv',\n    'stores.csv',\n    'oil.csv',\n    'items.csv',\n    'holidays_events.csv'\n]\n\n# Thư mục đích để giải nén\nextract_path = \"/kaggle/working\"\nos.makedirs(extract_path, exist_ok=True) # Đảm bảo thư mục đích tồn tại\n\nprint(\"Bắt đầu giải nén các file được chọn...\")\n\nfor dirname, _, filenames in os.walk('/kaggle/input/favorita-grocery-sales-forecasting'):\n    for filename in filenames:\n        archive_path = os.path.join(dirname, filename)\n        try:\n            # Mở file 7z\n            with py7zr.SevenZipFile(archive_path, mode='r') as archive:\n                # Lặp qua các file muốn giải nén\n                for file_to_extract in files_to_extract:\n                    print(f\"  Đang giải nén: {file_to_extract}\")\n                    # Sử dụng extract() để chỉ giải nén file mong muốn\n                    archive.extract(path=extract_path, targets=[file_to_extract])\n\n            print(\"Giải nén hoàn tất các file được chọn.\")\n\n        except py7zr.Bad7zFile:\n            print(f\"Lỗi: File {filename} không phải là file 7z hợp lệ hoặc bị hỏng.\")\n        except FileNotFoundError:\n            print(f\"Lỗi: Không tìm thấy file {archive_path}.\")\n        except Exception as e:\n            print(f\"Đã xảy ra lỗi: {e}\")\n\n\n# Kiểm tra lại các file đã được giải nén trong thư mục working\nprint(\"\\n--- Danh sách file trong thư mục 'working' ---\")\n# Sử dụng 'ls -l' để hiển thị chi tiết hơn\nprint(check_output([\"ls\", \"-l\", \"../working\"]).decode(\"utf8\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:29:28.874295Z","iopub.execute_input":"2025-11-17T13:29:28.874752Z","iopub.status.idle":"2025-11-17T13:30:57.956539Z","shell.execute_reply.started":"2025-11-17T13:29:28.874724Z","shell.execute_reply":"2025-11-17T13:30:57.955716Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cắt bỏ dữ liệu chỉ giữ năm 2016-2017 để phân tích","metadata":{}},{"cell_type":"code","source":"# Hàm hỗ trợ để đọc, chuyển đổi ngày và lọc dữ liệu\ndef prune_data(filename):\n    \"\"\"Đọc file CSV, lọc các bản ghi có năm 2016-2017, và trả về DataFrame.\"\"\"\n    \n    # Đọc file CSV\n    df = pd.read_csv(filename, low_memory=False)\n    \n    # Chuyển đổi cột 'date' thành kiểu datetime để dễ dàng lọc\n    df['date'] = pd.to_datetime(df['date'])\n    \n    # 3. Lọc dữ liệu cho năm 2016-2017\n    # Lấy năm từ cột 'date' và so sánh\n    df_pruned = df[df['date'].dt.year.isin([2016, 2017])].copy()\n    \n    # Trả về DataFrame đã được cắt tỉa\n    return df_pruned","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:53:45.925536Z","iopub.execute_input":"2025-11-17T13:53:45.925927Z","iopub.status.idle":"2025-11-17T13:53:45.931451Z","shell.execute_reply.started":"2025-11-17T13:53:45.925899Z","shell.execute_reply":"2025-11-17T13:53:45.930553Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. train.csv\ntrain_pruned = prune_data(\"train.csv\")\nprint(f\"train_pruned: {len(train_pruned)} bản ghi đã được tải (2016-2017).\")\n\n# 2. transactions.csv\ntransactions_pruned = prune_data(\"transactions.csv\")\nprint(f\"transactions_pruned: {len(transactions_pruned)} bản ghi đã được tải (2016-2017).\")\n\n# 3. oil.csv\noil_pruned = prune_data(\"oil.csv\")\nprint(f\"oil_pruned: {len(oil_pruned)} bản ghi đã được tải (2016-2017).\")\n\n# 4. holidays_events.csv\nholidays_pruned = prune_data(\"holidays_events.csv\")\nprint(f\"holidays_pruned: {len(holidays_pruned)} bản ghi đã được tải (2016-2017).\")\n\n\n# --- Kết quả ---\nprint(\"\\nĐã lọc và lưu toàn bộ dữ liệu năm 2016-2017 thành công!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:49:19.507416Z","iopub.execute_input":"2025-11-17T13:49:19.508464Z","iopub.status.idle":"2025-11-17T13:51:54.051204Z","shell.execute_reply.started":"2025-11-17T13:49:19.508428Z","shell.execute_reply":"2025-11-17T13:51:54.049857Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra\nprint(\"Kiểm tra 5 dòng đầu tâp train\")\nprint(train_pruned.head())\n\nprint(\"\\nKiểm tra 5 dòng đầu của transactions\")\nprint(transactions_pruned.head())\n\nprint(\"\\nKiểm tra 5 dòng đầu của oil\")\nprint(oil_pruned.head())\n\nprint(\"\\nKiểm tra 5 dòng đầu của holidays_events\")\nprint(holidays_pruned.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:53:56.657582Z","iopub.execute_input":"2025-11-17T13:53:56.658517Z","iopub.status.idle":"2025-11-17T13:53:56.673934Z","shell.execute_reply.started":"2025-11-17T13:53:56.658484Z","shell.execute_reply":"2025-11-17T13:53:56.672950Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Tiền xử lý dữ liệu","metadata":{}},{"cell_type":"markdown","source":"## Các hàm tiện ích","metadata":{}},{"cell_type":"code","source":"def check_null(df: pd.DataFrame) -> dict:\n    \"\"\"Trả về số lượng giá trị null theo từng cột (Pandas) dưới dạng dict.\"\"\"\n    return df.isnull().sum().to_dict()\n\ndef check_duplicate(df: pd.DataFrame) -> int:\n    \"\"\"\n    Trả về tổng số dòng trùng lặp trong DataFrame (Pandas).\n    \"\"\"\n    dup_count = df.duplicated().sum()\n    print(f\"Số dòng trùng lặp: {dup_count}\")\n    return dup_count\n    \ndef check_negative(df: pd.DataFrame) -> dict:\n    \"\"\"\n    Trả về tổng số giá trị âm trong từng cột số (dưới dạng dict).\n    \"\"\"\n    # Lọc danh sách cột kiểu số (integer/float)\n    numeric_df = df.select_dtypes(include=np.number)\n    return (numeric_df < 0).sum().to_dict()\n\ndef check_strange_char(df: pd.DataFrame, pattern=r\"[^a-zA-Z0-9\\s.,:/\\-_]\") -> dict:\n    \"\"\"\n    Kiểm tra số lượng giá trị chứa ký tự lạ (ngoài chữ,số,khoảng trắng và vài dấu cơ bản) trong các cột chuỗi.\n    (Phiên bản đã sửa lỗi)\n    \"\"\"\n    # Lọc danh sách cột kiểu chuỗi (object)\n    str_cols = df.select_dtypes(include='object').columns.tolist()\n    results = {}\n\n    for c in str_cols:\n        try:\n            count = int(df[c].astype(str).str.contains(pattern).sum())\n            results[c] = count\n        except Exception as e:\n            print(f\"Không thể xử lý cột '{c}': {e}\")\n            results[c] = 0\n\n    return results\n\n# Tạo feature về thời gian (season)- mùa trong năm\ndef season_from_month(m):\n    if m in [12, 1, 2]: return 0  # Winter\n    elif m in [3, 4, 5]: return 1  # Spring\n    elif m in [6, 7, 8]: return 2  # Summer\n    else: return 3  # Fall","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:53:59.977332Z","iopub.execute_input":"2025-11-17T13:53:59.977616Z","iopub.status.idle":"2025-11-17T13:54:00.182783Z","shell.execute_reply.started":"2025-11-17T13:53:59.977597Z","shell.execute_reply":"2025-11-17T13:54:00.181829Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập train_pruned","metadata":{}},{"cell_type":"code","source":"train_pruned.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:54:02.791897Z","iopub.execute_input":"2025-11-17T13:54:02.792216Z","iopub.status.idle":"2025-11-17T13:54:02.798451Z","shell.execute_reply.started":"2025-11-17T13:54:02.792191Z","shell.execute_reply":"2025-11-17T13:54:02.797561Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra null\nprint(\"Tổng số giá trị null: \", check_null(train_pruned))\n\n# Kiểm tra trùng lặp\nprint(\"Tổng số giá trị trùng lặp: \", check_duplicate(train_pruned))\n\n# Kiểm tra giá trị âm\nprint(\"Tổng số giá trị âm: \", check_negative(train_pruned))\n\n# Kiểm tra giá trị có ký tự đặc biệt\nprint(\"Tổng số giá trị có ký tự đăc biệt: \", check_strange_char(train_pruned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:54:04.915655Z","iopub.execute_input":"2025-11-17T13:54:04.915992Z","iopub.status.idle":"2025-11-17T13:55:24.735897Z","shell.execute_reply.started":"2025-11-17T13:54:04.915968Z","shell.execute_reply":"2025-11-17T13:55:24.734844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Xử lý giá trị < 0 trong unit_sales\nnegative_before = (train_pruned['unit_sales'] < 0).sum()\nprint(f\"Số giá trị 'unit_sales' âm (trước khi sửa): {negative_before}\")\n\n# Một số dòng có unit_sales < 0 → thay bằng 0 đại diện cho không bán được\ntrain_pruned['unit_sales'] = train_pruned['unit_sales'].clip(lower=0)\n\n# --- Kiểm tra lại kết quả ---\nnegative_after = (train_pruned['unit_sales'] < 0).sum()\nprint(f\"Số giá trị 'unit_sales' < 0 (sau khi sửa): {negative_after}\")\n\nif negative_after == 0:\n    print(\"Đã cập nhật thành công: Tất cả giá trị < 0 trong 'unit_sales' đã được đổi thành 0\")\nelse:\n    print(\"Lỗi: Vẫn còn giá trị <= 0\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:55:58.810174Z","iopub.execute_input":"2025-11-17T13:55:58.810485Z","iopub.status.idle":"2025-11-17T13:55:59.822529Z","shell.execute_reply.started":"2025-11-17T13:55:58.810463Z","shell.execute_reply":"2025-11-17T13:55:59.821584Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Tổng số giá trị âm: \", check_negative(train_pruned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:56:04.602276Z","iopub.execute_input":"2025-11-17T13:56:04.603388Z","iopub.status.idle":"2025-11-17T13:56:06.105231Z","shell.execute_reply.started":"2025-11-17T13:56:04.603353Z","shell.execute_reply":"2025-11-17T13:56:06.104373Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Trực quan dữ liệu để kiểm tra outlier\nsns.set_theme(style=\"whitegrid\")\n\n# Danh sách các cột kiểm tra outlier\ncols_to_plot = ['id', 'store_nbr', 'item_nbr', 'unit_sales']\n\nn_features = len(cols_to_plot)\n\n# Vẽ Boxplot\nfig, axes = plt.subplots(nrows=1, ncols=n_features, figsize=(7 * n_features, 6))\n\n# Đặt tiêu đề chung cho tất cả biểu đồ\nfig.suptitle('Phân tích Ngoại lai (Outliers) bằng Boxplot', fontsize=16, y=1.02)\n\nif n_features == 1:\n    axes = [axes]\n\nfor i, col in enumerate(cols_to_plot):\n    print(f\"Đang tạo boxplot cho cột: {col}...\")\n    \n    sns.boxplot(data=train_pruned, y=col, ax=axes[i])\n    \n    axes[i].set_title(f\"Boxplot của {col}\", fontsize=12)\n    axes[i].set_ylabel('')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T13:59:05.513023Z","iopub.execute_input":"2025-11-17T13:59:05.513393Z","iopub.status.idle":"2025-11-17T13:59:29.750793Z","shell.execute_reply.started":"2025-11-17T13:59:05.513356Z","shell.execute_reply":"2025-11-17T13:59:29.749761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Danh sách các cột cần kiểm tra outlier\ncols_to_check = ['unit_sales']\ntotal_rows = len(train_pruned)\nprint(f\"Tổng số dòng trong DataFrame: {total_rows}\\n\")\n\nprint(\"--- Thống kê Outlier bằng phương pháp IQR ---\")\n\nfor col in cols_to_check:\n    # 1. Tính Q1 (Quartile 1) và Q3 (Quartile 3)\n    Q1 = train_pruned[col].quantile(0.25)\n    Q3 = train_pruned[col].quantile(0.75)\n    \n    # 2. Tính IQR (Interquartile Range)\n    IQR = Q3 - Q1\n    \n    # 3. Xác định Ngưỡng Outlier\n    # Ngưỡng dưới: Q1 - 1.5 * IQR\n    # Ngưỡng trên: Q3 + 1.5 * IQR\n    lower_bound = Q1 - 1.5 * IQR\n    upper_bound = Q3 + 1.5 * IQR\n    \n    # 4. Đếm số lượng Outlier\n    outliers = train_pruned[\n        (train_pruned[col] < lower_bound) | (train_pruned[col] > upper_bound)\n    ]\n    \n    num_outliers = len(outliers)\n    \n    # 5. Tính Tỷ lệ\n    outlier_ratio = (num_outliers / total_rows) * 100\n    \n    print(f\"Cột: {col}\")\n    print(f\"Số lượng Outlier: {num_outliers}\")\n    print(f\"Tỷ lệ Outlier: {outlier_ratio:.4f}%\")\n    \n    if col == 'unit_sales':\n        print(f\"  --> Q1: {Q1:.2f}, Q3: {Q3:.2f}, Ngưỡng Trên: {upper_bound:.2f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:01:54.549365Z","iopub.execute_input":"2025-11-17T14:01:54.550594Z","iopub.status.idle":"2025-11-17T14:01:58.737000Z","shell.execute_reply.started":"2025-11-17T14:01:54.550558Z","shell.execute_reply":"2025-11-17T14:01:58.735827Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Nhận xét: Khoảng 9.88% số lượng bán hàng được xác định là outlier theo phương pháp IQR (ngưỡng trên là $\\mathbf{17}$ đơn vị). Tỷ lệ này là đáng kể nhưng thường thấy trong dữ liệu bán lẻ. Thay vì xóa các outlier này, chúng ta sẽ giữ lại chúng. Các giá trị unit_sales lớn này có thể đại diện cho các sự kiện bán hàng có ý nghĩa thực tế như các đợt khuyến mãi lớn, mùa lễ hội, hoặc tích trữ hàng hóa. Việc xóa chúng sẽ làm mất đi thông tin quan trọng mà mô hình cần học để dự báo các sự kiện đột biến trong tương lai.","metadata":{}},{"cell_type":"code","source":"# Chuyển cột onpromotion từ bool sang binary (0 và 1)\ntrain_pruned['onpromotion'] = train_pruned['onpromotion'].astype('int8')\ntrain_pruned.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:04:55.009601Z","iopub.execute_input":"2025-11-17T14:04:55.011086Z","iopub.status.idle":"2025-11-17T14:04:57.423503Z","shell.execute_reply.started":"2025-11-17T14:04:55.011046Z","shell.execute_reply":"2025-11-17T14:04:57.422685Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Thêm các cột thời gian\n\ntrain_pruned['year'] = train_pruned['date'].dt.year\ntrain_pruned['month'] = train_pruned['date'].dt.month\ntrain_pruned['day'] = train_pruned['date'].dt.day\ntrain_pruned['dayofweek'] = train_pruned['date'].dt.dayofweek\n\ntrain_pruned[\"weekofyear\"] = train_pruned[\"date\"].dt.isocalendar().week.astype(int)\ntrain_pruned[\"is_weekend\"] = train_pruned[\"dayofweek\"].isin([5, 6]).astype(int)\ntrain_pruned[\"quarter\"] = train_pruned[\"date\"].dt.quarter\n\n# Đánh dấu đầu tháng/cuối tháng\ntrain_pruned[\"is_month_start\"] = train_pruned[\"date\"].dt.is_month_start.astype(int)\ntrain_pruned[\"is_month_end\"] = train_pruned[\"date\"].dt.is_month_end.astype(int)\n\ntrain_pruned[\"season\"] = train_pruned[\"month\"].apply(season_from_month)\n\n\n# --- Kiểm tra kết quả ---\nprint(\"\\nĐã thêm các cột thành công!\")\nprint(\"Kiểm tra 5 dòng đầu của train_pruned (với cột mới):\")\nprint(train_pruned.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:05:02.469398Z","iopub.execute_input":"2025-11-17T14:05:02.469704Z","iopub.status.idle":"2025-11-17T14:05:49.942174Z","shell.execute_reply.started":"2025-11-17T14:05:02.469684Z","shell.execute_reply":"2025-11-17T14:05:49.941028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Bỏ cột id\ntrain_pruned = train_pruned.drop('id', axis=1)\nprint(\"Các cột hiện tại của train_pruned:\")\nprint(train_pruned.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:20:18.745883Z","iopub.execute_input":"2025-11-17T14:20:18.746699Z","iopub.status.idle":"2025-11-17T14:20:25.527864Z","shell.execute_reply.started":"2025-11-17T14:20:18.746663Z","shell.execute_reply":"2025-11-17T14:20:25.526798Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_pruned.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:20:33.811058Z","iopub.execute_input":"2025-11-17T14:20:33.811495Z","iopub.status.idle":"2025-11-17T14:20:33.819188Z","shell.execute_reply.started":"2025-11-17T14:20:33.811455Z","shell.execute_reply":"2025-11-17T14:20:33.818282Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Ghi file csv\ntrain_pruned.to_csv(\"train_final.csv\", index=False)\nprint(\"Đã xuất file thành công!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:05:49.952278Z","iopub.execute_input":"2025-11-17T14:05:49.952626Z","iopub.status.idle":"2025-11-17T14:11:54.880006Z","shell.execute_reply.started":"2025-11-17T14:05:49.952594Z","shell.execute_reply":"2025-11-17T14:11:54.878362Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập transactions_pruned","metadata":{}},{"cell_type":"code","source":"transactions_pruned.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:12:57.024106Z","iopub.execute_input":"2025-11-17T14:12:57.024467Z","iopub.status.idle":"2025-11-17T14:12:57.030505Z","shell.execute_reply.started":"2025-11-17T14:12:57.024442Z","shell.execute_reply":"2025-11-17T14:12:57.029633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transactions_pruned.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:01.664064Z","iopub.execute_input":"2025-11-17T14:13:01.664385Z","iopub.status.idle":"2025-11-17T14:13:01.674435Z","shell.execute_reply.started":"2025-11-17T14:13:01.664360Z","shell.execute_reply":"2025-11-17T14:13:01.673434Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra null\nprint(\"Tổng số giá trị null: \", check_null(transactions_pruned))\n\n# Kiểm tra trùng lặp\nprint(\"Tổng số giá trị trùng lặp: \", check_duplicate(transactions_pruned))\n\n# Kiểm tra giá trị âm\nprint(\"Tổng số giá trị âm: \", check_negative(transactions_pruned))\n\n# Kiểm tra giá trị có ký tự đặc biệt\nprint(\"Tổng số giá trị có ký tự đặc biệt: \", check_strange_char(transactions_pruned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:07.534838Z","iopub.execute_input":"2025-11-17T14:13:07.535592Z","iopub.status.idle":"2025-11-17T14:13:07.553235Z","shell.execute_reply.started":"2025-11-17T14:13:07.535561Z","shell.execute_reply":"2025-11-17T14:13:07.552165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Trực quan dữ liệu để kiểm tra outlier\nsns.set_theme(style=\"whitegrid\")\n\n# Danh sách các cột kiểm tra outlier\ncols_to_plot = ['store_nbr', 'transactions']\n\nn_features = len(cols_to_plot)\n\n# Vẽ Boxplot\nfig, axes = plt.subplots(nrows=1, ncols=n_features, figsize=(7 * n_features, 6))\n\n# Đặt tiêu đề chung cho tất cả biểu đồ\nfig.suptitle('Phân tích Ngoại lai (Outliers) bằng Boxplot', fontsize=16, y=1.02)\n\nif n_features == 1:\n    axes = [axes]\n\nfor i, col in enumerate(cols_to_plot):\n    print(f\"Đang tạo boxplot cho cột: {col}...\")\n    \n    sns.boxplot(data=transactions_pruned, y=col, ax=axes[i])\n    \n    axes[i].set_title(f\"Boxplot của {col}\", fontsize=12)\n    axes[i].set_ylabel('')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:13.378759Z","iopub.execute_input":"2025-11-17T14:13:13.379138Z","iopub.status.idle":"2025-11-17T14:13:13.722043Z","shell.execute_reply.started":"2025-11-17T14:13:13.379112Z","shell.execute_reply":"2025-11-17T14:13:13.721292Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Xác định ngưỡng Outlier cho 'transactions' (dựa trên phương pháp IQR)\nQ1 = transactions_pruned['transactions'].quantile(0.25)\nQ3 = transactions_pruned['transactions'].quantile(0.75)\nIQR = Q3 - Q1\nupper_bound = Q3 + 1.5 * IQR\n\nprint(f\"Ngưỡng Q1: {Q1}\")\nprint(f\"Ngưỡng Q3: {Q3}\")\nprint(f\"Ngưỡng Outlier (Giới hạn trên = Q3 + 1.5*IQR): {upper_bound:.2f}\")\n\n# Lọc tất cả các ngày có transactions được coi là ngoại lai\noutlier_trans_df = transactions_pruned[\n    transactions_pruned['transactions'] > upper_bound\n].copy()\n\nprint(f\"\\nTìm thấy {len(outlier_trans_df)} ngày có transactions là ngoại lai (cao hơn {upper_bound:.2f}).\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:31.146901Z","iopub.execute_input":"2025-11-17T14:13:31.147251Z","iopub.status.idle":"2025-11-17T14:13:31.160993Z","shell.execute_reply.started":"2025-11-17T14:13:31.147230Z","shell.execute_reply":"2025-11-17T14:13:31.159974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"outlier_trans_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:34.421821Z","iopub.execute_input":"2025-11-17T14:13:34.422261Z","iopub.status.idle":"2025-11-17T14:13:34.433752Z","shell.execute_reply.started":"2025-11-17T14:13:34.422234Z","shell.execute_reply":"2025-11-17T14:13:34.432462Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Ghi chú: Các giá trị transactions tăng đột biến vào các ngày như 2016-01-02, 2016-12-23, 2017-08-14 và 2017-08-15 được xác định là outlier về mặt thống kê, tuy nhiên đều là outlier hợp lý. Cụ thể, những thời điểm này trùng với mùa mua sắm lễ Tết Dương lịch, cao điểm Giáng sinh, và lễ Đức Mẹ Lên Trời – các dịp người dân Ecuador thường mua sắm, du lịch và tổ chức lễ hội nhiều hơn bình thường. Vì vậy, các outlier này phản ánh biến động thực tế trong hành vi tiêu dùng, không phải dữ liệu bất thường hay lỗi, nên được giữ lại trong phân tích.","metadata":{}},{"cell_type":"code","source":"# Thêm các cột thời gian\n\ntransactions_pruned['year'] = transactions_pruned['date'].dt.year\ntransactions_pruned['month'] = transactions_pruned['date'].dt.month\ntransactions_pruned['day'] = transactions_pruned['date'].dt.day\ntransactions_pruned['dayofweek'] = transactions_pruned['date'].dt.dayofweek\n\ntransactions_pruned[\"weekofyear\"] = transactions_pruned[\"date\"].dt.isocalendar().week.astype(int)\ntransactions_pruned[\"is_weekend\"] = transactions_pruned[\"dayofweek\"].isin([5, 6]).astype(int)\ntransactions_pruned[\"quarter\"] = transactions_pruned[\"date\"].dt.quarter\n\n# Đánh dấu đầu tháng/cuối tháng\ntransactions_pruned[\"is_month_start\"] = transactions_pruned[\"date\"].dt.is_month_start.astype(int)\ntransactions_pruned[\"is_month_end\"] = transactions_pruned[\"date\"].dt.is_month_end.astype(int)\n\ntransactions_pruned[\"season\"] = transactions_pruned[\"month\"].apply(season_from_month)\n\n# --- Kiểm tra kết quả ---\nprint(\"\\nĐã thêm cột thành công!\")\nprint(\"Kiểm tra 5 dòng đầu của transactions_pruned (với cột mới):\")\nprint(transactions_pruned.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:41.749433Z","iopub.execute_input":"2025-11-17T14:13:41.749769Z","iopub.status.idle":"2025-11-17T14:13:41.792887Z","shell.execute_reply.started":"2025-11-17T14:13:41.749738Z","shell.execute_reply":"2025-11-17T14:13:41.792030Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Ghi file csv\ntransactions_pruned.to_csv(\"transactions_final.csv\", index=False)\nprint(\"Đã xuất file thành công!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:45.220731Z","iopub.execute_input":"2025-11-17T14:13:45.221101Z","iopub.status.idle":"2025-11-17T14:13:45.373002Z","shell.execute_reply.started":"2025-11-17T14:13:45.221073Z","shell.execute_reply":"2025-11-17T14:13:45.371845Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập oil_pruned","metadata":{}},{"cell_type":"code","source":"oil_pruned.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:47.967663Z","iopub.execute_input":"2025-11-17T14:13:47.968144Z","iopub.status.idle":"2025-11-17T14:13:47.974293Z","shell.execute_reply.started":"2025-11-17T14:13:47.968108Z","shell.execute_reply":"2025-11-17T14:13:47.973344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"oil_pruned.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:49.940835Z","iopub.execute_input":"2025-11-17T14:13:49.941587Z","iopub.status.idle":"2025-11-17T14:13:49.952310Z","shell.execute_reply.started":"2025-11-17T14:13:49.941556Z","shell.execute_reply":"2025-11-17T14:13:49.950980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra null\nprint(\"Tổng số giá trị null: \", check_null(oil_pruned))\n\n# Kiểm tra trùng lặp\nprint(\"Tổng số giá trị trùng lặp: \", check_duplicate(oil_pruned))\n\n# Kiểm tra giá trị âm\nprint(\"Tổng số giá trị âm: \", check_negative(oil_pruned))\n\n# Kiểm tra giá trị có ký tự đặc biệt\nprint(\"Tổng số giá trị có ký tự đặc biệt: \", check_strange_char(oil_pruned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:13:52.633351Z","iopub.execute_input":"2025-11-17T14:13:52.633857Z","iopub.status.idle":"2025-11-17T14:13:52.643699Z","shell.execute_reply.started":"2025-11-17T14:13:52.633778Z","shell.execute_reply":"2025-11-17T14:13:52.642483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra thông tin dữ liệu có dcoilwtico bị null\noil_pruned[oil_pruned['dcoilwtico'].isnull()]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:14:02.923181Z","iopub.execute_input":"2025-11-17T14:14:02.923497Z","iopub.status.idle":"2025-11-17T14:14:02.934923Z","shell.execute_reply.started":"2025-11-17T14:14:02.923474Z","shell.execute_reply":"2025-11-17T14:14:02.933629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Xử lý null\nprint(f\"Số lượng giá trị null trong 'dcoilwtico' ban đầu: {oil_pruned['dcoilwtico'].isnull().sum()}\")\n\n# Sử dụng 'ffill' (forward fill) để fill giá trị null bằng giá trị của ngày trước đó\noil_pruned['dcoilwtico'] = oil_pruned['dcoilwtico'].fillna(method='ffill')\n\n# Xử lý trường hợp đặc biệt nếu phía trước dữ liệu null không có dữ liệu nào trước đó -> Dùng 'bfill' (backward fill)\noil_pruned['dcoilwtico'] = oil_pruned['dcoilwtico'].fillna(method='bfill')\n\n# Kiểm tra lại\nfinal_null_count = oil_pruned['dcoilwtico'].isnull().sum()\nprint(f\"Số lượng giá trị null trong 'dcoilwtico' sau khi fill: {final_null_count}\")\n\nif final_null_count == 0:\n    print(\"Đã fill thành công!\")\nelse:\n    print(\"Vẫn còn giá trị null, vui lòng kiểm tra lại.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:14:43.879173Z","iopub.execute_input":"2025-11-17T14:14:43.879545Z","iopub.status.idle":"2025-11-17T14:14:43.896069Z","shell.execute_reply.started":"2025-11-17T14:14:43.879515Z","shell.execute_reply":"2025-11-17T14:14:43.894957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Thêm các cột thời gian\n\noil_pruned['year'] = oil_pruned['date'].dt.year\noil_pruned['month'] = oil_pruned['date'].dt.month\noil_pruned['day'] = oil_pruned['date'].dt.day\noil_pruned['dayofweek'] = oil_pruned['date'].dt.dayofweek\n\noil_pruned[\"weekofyear\"] = oil_pruned[\"date\"].dt.isocalendar().week.astype(int)\noil_pruned[\"is_weekend\"] = oil_pruned[\"dayofweek\"].isin([5, 6]).astype(int)\noil_pruned[\"quarter\"] = oil_pruned[\"date\"].dt.quarter\n\n# Đánh dấu đầu tháng/cuối tháng\noil_pruned[\"is_month_start\"] = oil_pruned[\"date\"].dt.is_month_start.astype(int)\noil_pruned[\"is_month_end\"] = oil_pruned[\"date\"].dt.is_month_end.astype(int)\n\noil_pruned[\"season\"] = oil_pruned[\"month\"].apply(season_from_month)\n\n# --- Kiểm tra kết quả ---\nprint(\"\\nĐã thêm cột thành công!\")\nprint(\"Kiểm tra 5 dòng đầu của oil_pruned (với cột mới):\")\nprint(oil_pruned.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:14:47.750424Z","iopub.execute_input":"2025-11-17T14:14:47.750742Z","iopub.status.idle":"2025-11-17T14:14:47.776859Z","shell.execute_reply.started":"2025-11-17T14:14:47.750715Z","shell.execute_reply":"2025-11-17T14:14:47.775720Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Trực quan dữ liệu để kiểm tra outlier\nsns.set_theme(style=\"whitegrid\")\n\n# Danh sách các cột kiểm tra outlier\ncols_to_plot = ['dcoilwtico']\n\nn_features = len(cols_to_plot)\n\n# Vẽ Boxplot\nfig, axes = plt.subplots(nrows=1, ncols=n_features, figsize=(7 * n_features, 6))\n\n# Đặt tiêu đề chung cho tất cả biểu đồ\nfig.suptitle('Phân tích Ngoại lai (Outliers) bằng Boxplot', fontsize=16, y=1.02)\n\nif n_features == 1:\n    axes = [axes]\n\nfor i, col in enumerate(cols_to_plot):\n    print(f\"Đang tạo boxplot cho cột: {col}...\")\n    \n    sns.boxplot(data=oil_pruned, y=col, ax=axes[i])\n    \n    axes[i].set_title(f\"Boxplot của {col}\", fontsize=12)\n    axes[i].set_ylabel('')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:14:52.187710Z","iopub.execute_input":"2025-11-17T14:14:52.188348Z","iopub.status.idle":"2025-11-17T14:14:52.385417Z","shell.execute_reply.started":"2025-11-17T14:14:52.188318Z","shell.execute_reply":"2025-11-17T14:14:52.384515Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Xác định ngưỡng Outlier cho 'dcoilwtico' (dựa trên phương pháp IQR)\nQ1 = oil_pruned['dcoilwtico'].quantile(0.25)\nQ3 = oil_pruned['dcoilwtico'].quantile(0.75)\nIQR = Q3 - Q1\nlower_bound = Q1 - 1.5 * IQR\n\nprint(f\"Ngưỡng Q1: {Q1}\")\nprint(f\"Ngưỡng Q3: {Q3}\")\nprint(f\"Ngưỡng Outlier (Giới hạn dưới = Q1 - 1.5*IQR): {lower_bound:.2f}\")\n\n# Lọc tất cả các ngày có dcoilwtico được coi là ngoại lai\noutlier_oil_df = oil_pruned[\n    oil_pruned['dcoilwtico'] < lower_bound\n].copy()\n\nprint(f\"\\nTìm thấy {len(outlier_oil_df)} ngày có dcoilwtico là ngoại lai (thấp hơn {lower_bound:.2f}).\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:03.459382Z","iopub.execute_input":"2025-11-17T14:15:03.459704Z","iopub.status.idle":"2025-11-17T14:15:03.473306Z","shell.execute_reply.started":"2025-11-17T14:15:03.459684Z","shell.execute_reply":"2025-11-17T14:15:03.472153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"outlier_oil_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-07T17:21:06.034599Z","iopub.execute_input":"2025-11-07T17:21:06.035004Z","iopub.status.idle":"2025-11-07T17:21:06.058548Z","shell.execute_reply.started":"2025-11-07T17:21:06.034975Z","shell.execute_reply":"2025-11-07T17:21:06.057536Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Nhận xét: Trong đầu năm 2016, giá dầu thế giới thực sự giảm rất mạnh, có lúc chạm ~26–30 USD/thùng do: Khủng hoảng cung – cầu dầu mỏ, OPEC không cắt giảm sản lượng, nhu cầu từ Trung Quốc giảm mạnh, và tồn kho dầu ở Mỹ cao kỷ lục -> Các giá trị outlier phản ánh biến động thật của giá dầu, không phải thiếu hoặc lỗi dữ liệu -> Giữ lại các outlier","metadata":{}},{"cell_type":"code","source":"# Ghi file csv\noil_pruned.to_csv(\"oil_final.csv\", index=False)\nprint(\"Đã xuất file thành công!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:07.061620Z","iopub.execute_input":"2025-11-17T14:15:07.061985Z","iopub.status.idle":"2025-11-17T14:15:07.071379Z","shell.execute_reply.started":"2025-11-17T14:15:07.061961Z","shell.execute_reply":"2025-11-17T14:15:07.070303Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập holidays_pruned","metadata":{}},{"cell_type":"code","source":"holidays_pruned.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:10.147400Z","iopub.execute_input":"2025-11-17T14:15:10.147715Z","iopub.status.idle":"2025-11-17T14:15:10.154332Z","shell.execute_reply.started":"2025-11-17T14:15:10.147690Z","shell.execute_reply":"2025-11-17T14:15:10.153593Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"holidays_pruned.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:13.141185Z","iopub.execute_input":"2025-11-17T14:15:13.141495Z","iopub.status.idle":"2025-11-17T14:15:13.153570Z","shell.execute_reply.started":"2025-11-17T14:15:13.141473Z","shell.execute_reply":"2025-11-17T14:15:13.152634Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra null\nprint(\"Tổng số giá trị null: \", check_null(holidays_pruned))\n\n# Kiểm tra trùng lặp\nprint(\"Tổng số giá trị trùng lặp: \", check_duplicate(holidays_pruned))\n\n# Kiểm tra giá trị âm\nprint(\"Tổng số giá trị âm: \", check_negative(holidays_pruned))\n\n# Kiểm tra giá trị có ký tự đặc biệt\nprint(\"Tổng số giá trị có ký tự đặc biệt: \", check_strange_char(holidays_pruned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:18.986535Z","iopub.execute_input":"2025-11-17T14:15:18.987093Z","iopub.status.idle":"2025-11-17T14:15:19.001938Z","shell.execute_reply.started":"2025-11-17T14:15:18.987058Z","shell.execute_reply":"2025-11-17T14:15:19.000636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Định nghĩa pattern ký tự đặc biệt (có thể tinh chỉnh)\npattern = r\"[^a-zA-Z0-9\\s.,:/\\-_]\"\n\ncondition = holidays_pruned[\"description\"].str.contains(pattern, na=False)\n\nrows_with_special = holidays_pruned[condition]\n\n# In kết quả\nprint(f\"Số dòng chứa ký tự đặc biệt: {len(rows_with_special)}\")\n\nprint(\"\\n--- Hiển thị các dòng chứa ký tự đặc biệt ---\")\n\nprint(rows_with_special)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:24.452443Z","iopub.execute_input":"2025-11-17T14:15:24.452782Z","iopub.status.idle":"2025-11-17T14:15:24.465419Z","shell.execute_reply.started":"2025-11-17T14:15:24.452736Z","shell.execute_reply":"2025-11-17T14:15:24.464470Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Giải thích: Dấu + (cộng) không phải là dữ liệu lỗi hay ký tự đặc biệt, mà là một ký hiệu ghi chú (notation) có chủ đích. Nó được sử dụng trong cột description để đánh dấu những ngày nghỉ bổ sung hoặc ngày liên quan ngay lập tức sau một sự kiện hoặc ngày lễ chính. Các ngày này thường là \"ngày nghỉ bù\" (bridge days) hoặc các ngày quan sát/phục hồi sau một sự kiện lớn.","metadata":{}},{"cell_type":"code","source":"# Chuyển cột transferred từ bool sang binary (0 và 1)\nholidays_pruned['transferred'] = holidays_pruned['transferred'].astype('int8')\nholidays_pruned.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:31.238051Z","iopub.execute_input":"2025-11-17T14:15:31.238368Z","iopub.status.idle":"2025-11-17T14:15:31.251618Z","shell.execute_reply.started":"2025-11-17T14:15:31.238343Z","shell.execute_reply":"2025-11-17T14:15:31.250598Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Thêm các cột thời gian\n\nholidays_pruned['year'] = holidays_pruned['date'].dt.year\nholidays_pruned['month'] = holidays_pruned['date'].dt.month\nholidays_pruned['day'] = holidays_pruned['date'].dt.day\nholidays_pruned['dayofweek'] = holidays_pruned['date'].dt.dayofweek\n\nholidays_pruned[\"weekofyear\"] = holidays_pruned[\"date\"].dt.isocalendar().week.astype(int)\nholidays_pruned[\"is_weekend\"] = holidays_pruned[\"dayofweek\"].isin([5, 6]).astype(int)\nholidays_pruned[\"quarter\"] = holidays_pruned[\"date\"].dt.quarter\n\n# Đánh dấu đầu tháng/cuối tháng\nholidays_pruned[\"is_month_start\"] = holidays_pruned[\"date\"].dt.is_month_start.astype(int)\nholidays_pruned[\"is_month_end\"] = holidays_pruned[\"date\"].dt.is_month_end.astype(int)\n\nholidays_pruned[\"season\"] = holidays_pruned[\"month\"].apply(season_from_month)\n\n# --- Kiểm tra kết quả ---\nprint(\"\\nĐã thêm cột thành công!\")\nprint(\"Kiểm tra 5 dòng đầu của holidays_pruned (với cột mới):\")\nprint(holidays_pruned.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:34.012738Z","iopub.execute_input":"2025-11-17T14:15:34.013575Z","iopub.status.idle":"2025-11-17T14:15:34.033404Z","shell.execute_reply.started":"2025-11-17T14:15:34.013541Z","shell.execute_reply":"2025-11-17T14:15:34.032338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Ghi file csv\nholidays_pruned.to_csv(\"holidays_final.csv\", index=False)\nprint(\"Đã xuất file thành công!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:42.521657Z","iopub.execute_input":"2025-11-17T14:15:42.522078Z","iopub.status.idle":"2025-11-17T14:15:42.531222Z","shell.execute_reply.started":"2025-11-17T14:15:42.522048Z","shell.execute_reply":"2025-11-17T14:15:42.530192Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập items","metadata":{}},{"cell_type":"code","source":"items = pd.read_csv(\"/kaggle/working/items.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:46.621398Z","iopub.execute_input":"2025-11-17T14:15:46.621723Z","iopub.status.idle":"2025-11-17T14:15:46.635854Z","shell.execute_reply.started":"2025-11-17T14:15:46.621699Z","shell.execute_reply":"2025-11-17T14:15:46.634746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"items.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:49.297523Z","iopub.execute_input":"2025-11-17T14:15:49.297828Z","iopub.status.idle":"2025-11-17T14:15:49.303567Z","shell.execute_reply.started":"2025-11-17T14:15:49.297800Z","shell.execute_reply":"2025-11-17T14:15:49.302741Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"items.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:50.928987Z","iopub.execute_input":"2025-11-17T14:15:50.929348Z","iopub.status.idle":"2025-11-17T14:15:50.939003Z","shell.execute_reply.started":"2025-11-17T14:15:50.929323Z","shell.execute_reply":"2025-11-17T14:15:50.937925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra giá trị null\nprint(\"Tổng số giá trị null:\\n\", items.isnull().sum(), \"\\n\")\n\n# Kiểm tra trùng lặp\nprint(\"Số dòng trùng lặp hoàn toàn:\", items.duplicated().sum())\nprint(\"Số item_nbr trùng:\", items.duplicated(subset=['item_nbr']).sum(), \"\\n\")\n\n# Kiểm tra giá trị âm (chỉ các cột numeric)\nnum_cols = items.select_dtypes(include='number').columns\nprint(\"Số giá trị âm theo cột:\\n\", (items[num_cols] < 0).sum(), \"\\n\")\n\n# Kiểm tra ký tự đặc biệt (chỉ cột string)\npattern = r\"[^a-zA-Z0-9\\s.,:/\\-_+]\"\nstr_cols = items.select_dtypes(include='object').columns\nprint(\"Số giá trị có ký tự đặc biệt theo cột:\\n\",\n      items[str_cols].apply(lambda s: s.astype(str).str.contains(pattern, regex=True, na=False).sum()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:15:55.630227Z","iopub.execute_input":"2025-11-17T14:15:55.630537Z","iopub.status.idle":"2025-11-17T14:15:55.648413Z","shell.execute_reply.started":"2025-11-17T14:15:55.630513Z","shell.execute_reply":"2025-11-17T14:15:55.647243Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý tập stores","metadata":{}},{"cell_type":"code","source":"stores = pd.read_csv(\"/kaggle/working/stores.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:16:00.165650Z","iopub.execute_input":"2025-11-17T14:16:00.167155Z","iopub.status.idle":"2025-11-17T14:16:00.175259Z","shell.execute_reply.started":"2025-11-17T14:16:00.167107Z","shell.execute_reply":"2025-11-17T14:16:00.174277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"stores.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:16:01.914668Z","iopub.execute_input":"2025-11-17T14:16:01.915024Z","iopub.status.idle":"2025-11-17T14:16:01.921197Z","shell.execute_reply.started":"2025-11-17T14:16:01.914987Z","shell.execute_reply":"2025-11-17T14:16:01.920077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"stores.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:16:04.049260Z","iopub.execute_input":"2025-11-17T14:16:04.050273Z","iopub.status.idle":"2025-11-17T14:16:04.059810Z","shell.execute_reply.started":"2025-11-17T14:16:04.050238Z","shell.execute_reply":"2025-11-17T14:16:04.058938Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra giá trị null\nprint(\"Tổng số giá trị null:\\n\", stores.isnull().sum(), \"\\n\")\n\n# Kiểm tra trùng lặp\nprint(\"Số dòng trùng lặp hoàn toàn:\", stores.duplicated().sum())\nprint(\"Số store_nbr trùng:\", stores.duplicated(subset=['store_nbr']).sum(), \"\\n\")\n\n# Kiểm tra giá trị âm (chỉ các cột numeric)\nnum_cols = stores.select_dtypes(include='number').columns\nprint(\"Số giá trị âm theo cột:\\n\", (stores[num_cols] < 0).sum(), \"\\n\")\n\n# Kiểm tra ký tự đặc biệt (chỉ cột string)\npattern = r\"[^a-zA-Z0-9\\s.,:/\\-_+]\"\nstr_cols = stores.select_dtypes(include='object').columns\nprint(\"Số giá trị có ký tự đặc biệt theo cột:\\n\",\n      stores[str_cols].apply(lambda s: s.astype(str).str.contains(pattern, regex=True, na=False).sum()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:16:09.286031Z","iopub.execute_input":"2025-11-17T14:16:09.286357Z","iopub.status.idle":"2025-11-17T14:16:09.301490Z","shell.execute_reply.started":"2025-11-17T14:16:09.286333Z","shell.execute_reply":"2025-11-17T14:16:09.300576Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# EDA thống kê cơ bản","metadata":{}},{"cell_type":"code","source":"# Train\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(train_pruned.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả unit_sales:\")\nprint(\"Min: \", train_pruned['unit_sales'].min())\nprint(\"Max: \", train_pruned['unit_sales'].max())\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả onpromotion:\")\n# Tính số lượng và tỷ lệ phần trăm cho mỗi giá trị unique\npromotion_counts = train_pruned['onpromotion'].value_counts()\npromotion_ratios = train_pruned['onpromotion'].value_counts(normalize=True) * 100\n\n# In kết quả cho 0 (Không Khuyến mãi)\ncount_0 = promotion_counts.get(0, 0)\nratio_0 = promotion_ratios.get(0, 0)\nprint(f\"0 (Không Khuyến mãi)         | {count_0:<17} | {ratio_0:.4f}%\")\n\n# In kết quả cho 1 (Có Khuyến mãi)\ncount_1 = promotion_counts.get(1, 0)\nratio_1 = promotion_ratios.get(1, 0)\nprint(f\"1 (Có Khuyến mãi)            | {count_1:<17} | {ratio_1:.4f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:33:49.953256Z","iopub.execute_input":"2025-11-17T14:33:49.953643Z","iopub.status.idle":"2025-11-17T14:33:50.689805Z","shell.execute_reply.started":"2025-11-17T14:33:49.953619Z","shell.execute_reply":"2025-11-17T14:33:50.688946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Transactions\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(transactions_pruned.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả transactions:\")\ntransactions_pruned['transactions'].describe().round()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:40:31.964599Z","iopub.execute_input":"2025-11-17T14:40:31.964987Z","iopub.status.idle":"2025-11-17T14:40:31.979348Z","shell.execute_reply.started":"2025-11-17T14:40:31.964961Z","shell.execute_reply":"2025-11-17T14:40:31.978445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Oil\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(oil_pruned.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả oil:\")\noil_pruned['dcoilwtico'].describe().round(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:41:44.947902Z","iopub.execute_input":"2025-11-17T14:41:44.948908Z","iopub.status.idle":"2025-11-17T14:41:44.962631Z","shell.execute_reply.started":"2025-11-17T14:41:44.948848Z","shell.execute_reply":"2025-11-17T14:41:44.960840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Holidays\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(holidays_pruned.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả transferred:\")\n# Tính số lượng và tỷ lệ phần trăm cho mỗi giá trị unique\ntransferred_counts = holidays_pruned['transferred'].value_counts()\ntransferred_ratios = holidays_pruned['transferred'].value_counts(normalize=True) * 100\n\n# In kết quả cho 0 (Ngày tổ chức lễ gốc)\ncount_0 = transferred_counts.get(0, 0)\nratio_0 = transferred_ratios.get(0, 0)\nprint(f\"0 (Ngày tổ chức lễ gốc)                   | {count_0:<17} | {ratio_0:.4f}%\")\n\n# In kết quả cho 1 (Ngày tổ chức lễ bị dời sang ngày khác)\ncount_1 = transferred_counts.get(1, 0)\nratio_1 = transferred_ratios.get(1, 0)\nprint(f\"1 (Ngày tổ chức lễ bị dời sang ngày khác) | {count_1:<17} | {ratio_1:.4f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:45:40.952461Z","iopub.execute_input":"2025-11-17T14:45:40.952851Z","iopub.status.idle":"2025-11-17T14:45:40.963593Z","shell.execute_reply.started":"2025-11-17T14:45:40.952824Z","shell.execute_reply":"2025-11-17T14:45:40.962604Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Items\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(items.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê mô tả perishable:\")\n# Tính số lượng và tỷ lệ phần trăm cho mỗi giá trị unique\nperishable_counts = items['perishable'].value_counts()\nperishable_ratios = items['perishable'].value_counts(normalize=True) * 100\n\n# In kết quả cho 0 (Sản phẩm bền)\ncount_0 = perishable_counts.get(0, 0)\nratio_0 = perishable_ratios.get(0, 0)\nprint(f\"0 (Sản phẩm bền)         | {count_0:<17} | {ratio_0:.4f}%\")\n\n# In kết quả cho 1 (Sản phẩm dễ hư hỏng)\ncount_1 = perishable_counts.get(1, 0)\nratio_1 = perishable_ratios.get(1, 0)\nprint(f\"1 (Sản phẩm dễ hư hỏng)  | {count_1:<17} | {ratio_1:.4f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:51:51.776715Z","iopub.execute_input":"2025-11-17T14:51:51.777075Z","iopub.status.idle":"2025-11-17T14:51:51.787498Z","shell.execute_reply.started":"2025-11-17T14:51:51.777048Z","shell.execute_reply":"2025-11-17T14:51:51.786457Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Stores\nprint(\"Thống kê kiểu dữ liệu: \")\nprint(stores.dtypes)\n\nprint(\"\\n=====================\\n\")\nprint(\"Thống kê giá trị unique của city/state/type/cluster:\")\n# Danh sách các cột cần thống kê\ncols_to_check = ['city', 'state', 'type', 'cluster']\n\nfor col in cols_to_check:\n    print(f\"\\nCột: {col} ({len(stores[col].unique())} giá trị duy nhất)\")\n    # In ra tất cả các giá trị unique\n    print(stores[col].unique())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T14:55:16.933645Z","iopub.execute_input":"2025-11-17T14:55:16.934074Z","iopub.status.idle":"2025-11-17T14:55:16.947096Z","shell.execute_reply.started":"2025-11-17T14:55:16.934043Z","shell.execute_reply":"2025-11-17T14:55:16.945557Z"}},"outputs":[],"execution_count":null}]}