{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":103436,"databundleVersionId":12476472,"sourceType":"competition"},{"sourceId":12200466,"sourceType":"datasetVersion","datasetId":7685180}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.ensemble import RandomForestClassifier\nfrom xgboost import XGBClassifier\nfrom lightgbm import LGBMClassifier\nfrom sklearn.metrics import classification_report\nfrom category_encoders.hashing import HashingEncoder\nimport xgboost as xgb\nfrom imblearn.over_sampling import SMOTE\nfrom imblearn.under_sampling import RandomUnderSampler\nimport lightgbm as lgb","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install -U scikit-learn imbalanced-learn xgboost lightgbm category_encoders\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Đồng nhất và gộp các tập dữ liệu ##","metadata":{}},{"cell_type":"code","source":"# Đọc file cụ thể\ndelay_4_6 = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv\")\nnot_delay_4_6 = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/not_delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv\", dtype={10: str, 42: str})\ndelay_7_9 = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv\")\nnot_delay_7_9 = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/not_delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv\", dtype={34: str})","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pilot = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/PILOT_10.csv\")\nsample_solution = pd.read_csv(f\"/kaggle/input/ds-108-p-21-assigment-06/sample_Solution.csv\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(delay_4_6.dtypes == not_delay_4_6.dtypes).all()  # True nếu kiểu dữ liệu khớp","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra xem ko khớp ở đâu\ndelay_4_6.dtypes\nnot_delay_4_6.dtypes","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(delay_7_9.dtypes == not_delay_7_9.dtypes).all()  # True nếu kiểu dữ liệu khớp","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"not_delay_4_6[\"Consider count hodiday Saturday\"] = pd.to_numeric(not_delay_4_6[\"Consider count hodiday Saturday\"], errors=\"coerce\").fillna(0).astype(\"int64\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(delay_4_6.dtypes == not_delay_4_6.dtypes).all()  # True nếu kiểu dữ liệu khớp","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Bỏ các dữ liệu trùng lặp\ndelay_4_6 = delay_4_6.drop_duplicates()\nnot_delay_4_6 = not_delay_4_6.drop_duplicates()\ndelay_7_9 = delay_7_9.drop_duplicates()\nnot_delay_7_9 = not_delay_7_9.drop_duplicates()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Cột có ở delay_4_6 nhưng không có ở delay_7_9:\", set(delay_4_6.columns) - set(delay_7_9.columns))\nprint(\"Cột có ở delay_7_9 nhưng không có ở delay_4_6:\", set(delay_7_9.columns) - set(delay_4_6.columns))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cols_to_drop = [\n    'SPECIFY_PRODUCTION_DAYS', 'EXPENSIVE_FLG', 'IO_UNFIT_FLG',\n    'ACTUAL_SHIP_DAYS', 'PRODUCT_ASSORT', 'SUPPLIER_CATEGORY_CD',\n    'SPECIFY_SHIP_DAYS', 'HAZARD_FLG', 'WEIGHT_UNIT', 'HEAVY_FLG'\n]\n\ndelay_4_6 = delay_4_6.drop(columns=cols_to_drop)\nnot_delay_4_6 = not_delay_4_6.drop(columns=cols_to_drop)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"set(delay_4_6.columns) == set(delay_7_9.columns)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Gộp df_train và df_test thành df_A\ndf_A = pd.concat([delay_4_6, not_delay_4_6], ignore_index=True)\n# Gộp df_train_7_9 và df_test_7_9 thành df_B\ndf_B = pd.concat([delay_7_9, not_delay_7_9], ignore_index=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Số dữ liệu từ tháng 7 - 9: {df_B.shape} \")\nprint(f\"Số dữ liệu từ tháng 4 - 6: {df_A.shape} \")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_A = df_A.sample(frac=1, random_state=42)  # Lấy toàn bộ dữ liệu\ndf_B = df_B.sample(frac=0.75, random_state=42) #Lấy 75% dữ liệu\n#Giảm dữ liệu để khi train không bị crash out","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kết hợp A và B\ndf = pd.concat([df_A, df_B], ignore_index=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Cột có ở pilot nhưng không có ở df:\", set(pilot.columns) - set(df.columns))\nprint(\"Cột có ở df nhưng không có ở pilot:\", set(df.columns) - set(pilot.columns))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý ##","metadata":{}},{"cell_type":"code","source":"# Chỉnh dữ liệu lại các kiểu dữ liệu thời gian\ndf[\"VSD\"] = pd.to_datetime(df[\"VSD\"], format='ISO8601')\ndf[\"Order date\"] = pd.to_datetime(df[\"Order date\"], format='ISO8601')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[['VSD', \"Order date\"]]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pilot[['VSD', \"Order date\"]]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Chỉnh dữ liệu lại các kiểu dữ liệu thời gian\npilot[\"VSD\"] = pd.to_datetime(pilot[\"VSD\"], format='%m/%d/%Y')\npilot[\"Order date\"] = pd.to_datetime(pilot[\"Order date\"], format='%m/%d/%Y')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(df.dtypes[[\"VSD\", \"Order date\"]])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(pilot.dtypes[[\"VSD\", \"Order date\"]])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_missing_values(df, threshold=0.2, show_top=None):\n    \"\"\"\n    Hiển thị biểu đồ tỷ lệ missing value của các cột trong DataFrame.\n\n    Parameters:\n        df (pd.DataFrame): Dataset cần kiểm tra.\n        threshold (float): Ngưỡng tỷ lệ missing để lọc (default = 0.2).\n        show_top (int): Nếu muốn chỉ hiển thị top N cột missing cao nhất.\n\n    Returns:\n        missing_df (pd.DataFrame): Thống kê missing.\n        high_missing_cols (List[str]): Các cột có missing > threshold.\n    \"\"\"\n    cols = df.columns\n    count = [df[col].isnull().sum() for col in cols]\n    percent = [i / len(df) for i in count]\n\n    missing = pd.DataFrame({'column': cols, 'proportion': percent})\n    missing = missing.sort_values(by='proportion', ascending=False)\n\n    if show_top is not None:\n        missing = missing.head(show_top)\n\n    plt.figure(figsize=(20, max(10, len(missing) * 0.3)))\n    plt.title('Missing values in each column', fontsize=16)\n    ax = sns.barplot(x='proportion', y='column', data=missing, palette='viridis')\n\n    for p in ax.patches:\n        value = p.get_width() * 100\n        ax.text(p.get_width() + 0.005, p.get_y() + p.get_height() / 2,\n                f\"{value:.2f}%\", va='center')\n\n    mean = np.mean(missing['proportion'])\n    std = np.std(missing['proportion'])\n    plt.xlabel('Proportion')\n    plt.ylabel('Columns')\n    plt.plot([], [], ' ', label=f'Average missing: {mean:.2%} \\u00B1 {std:.2%}')\n    plt.legend()\n    plt.tight_layout()\n    plt.show()\n\n    high_missing_cols = missing[missing['proportion'] > threshold]['column'].tolist()\n    return missing, high_missing_cols","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing_df, high_missing_cols = plot_missing_values(df, threshold=0.2)\nprint(\"Cột có missing > 20%:\", high_missing_cols)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cols_to_check = ['QTUF_RCV_NO', 'SOUF_RCV_NO', 'REASON_CD', \"OTHER AREA SHIP DIV\"]\n\nfor col in cols_to_check:\n    print(f\"\\n--- {col} ---\")\n    print(df[col].value_counts(dropna=False))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Đầu tiên chuyển tất cả sang string (để xử lý 1.0 và 1 giống nhau)\ndf['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].astype(str).replace('1.0', '1')\n\n# Nếu muốn giữ là số nguyên:\ndf['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].replace({'1.0': 1, '1': 1})","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Đầu tiên chuyển tất cả sang string (để xử lý 1.0 và 1 giống nhau)\npilot['OTHER AREA SHIP DIV'] = pilot['OTHER AREA SHIP DIV'].astype(str).replace('1.0', '1')\n\n# Nếu muốn giữ là số nguyên:\npilot['OTHER AREA SHIP DIV'] = pilot['OTHER AREA SHIP DIV'].replace({'1.0': 1, '1': 1})","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(df[\"OTHER AREA SHIP DIV\"].value_counts(dropna=False))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].replace(['nan', 'NaN'], '0')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pilot['OTHER AREA SHIP DIV'] = pilot['OTHER AREA SHIP DIV'].replace(['nan', 'NaN'], '0')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(df[\"OTHER AREA SHIP DIV\"].value_counts(dropna=False))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].astype(str)\npilot['OTHER AREA SHIP DIV'] = pilot['OTHER AREA SHIP DIV'].astype(str)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Impute cột categorical bằng mode (most frequent)\ncat_imputer = SimpleImputer(strategy=\"most_frequent\")\ndf[[\"Ship Mode\", \"SHIP DECISION NO\"]] = cat_imputer.fit_transform(df[[\"Ship Mode\", \"SHIP DECISION NO\"]])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Impute cột categorical bằng mode (most frequent)\ncat_imputer = SimpleImputer(strategy=\"most_frequent\")\npilot[[\"Ship Mode\", \"SHIP DECISION NO\"]] = cat_imputer.fit_transform(pilot[[\"Ship Mode\", \"SHIP DECISION NO\"]])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.drop(columns = ['QTUF_RCV_NO', 'SOUF_RCV_NO', 'REASON_CD'], inplace = True)\n# drop luôn REASON_CD do tỉ lệ thiếu cao và có thể là biến được tạo ra khi đã giao hàng rồi","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pilot.drop(columns = ['QTUF_RCV_NO', 'SOUF_RCV_NO', 'REASON_CD'], inplace = True)\n# # drop luôn REASON_CD do tỉ lệ thiếu cao và có thể là biến được tạo ra khi đã giao hàng rồi","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def winsorize_column(data, column):\n    Q1 = data[column].quantile(0.25)\n    Q3 = data[column].quantile(0.75)\n    IQR = Q3 - Q1\n    lower = Q1 - 1.5 * IQR\n    upper = Q3 + 1.5 * IQR\n    original_outliers = ((data[column] < lower) | (data[column] > upper)).sum()\n\n    # Clip lại\n    data[column] = data[column].clip(lower, upper)\n\n    print(f\"[{column}] Winsorized: {original_outliers} outliers clipped.\")\n    return data","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_cols = [\"Sales order line number\",\"SO QTY\",\"ALLOCATION QTY\",\"SUPPLIER INV AMOUNT\",\"PURCHASE AMOUNT\",\"PACK QTY\",\n            \"WEIGHT PER PIECE\"]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for col in num_cols:\n    df = winsorize_column(df, col)\n    pilot = winsorize_column(pilot,col)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.describe()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Loại các cột bị toàn NaN hoặc 1 giá trị\nnum_filtered_cols = [col for col in num_cols if df[col].nunique(dropna=True) > 1 and df[col].notnull().sum() > 0]\ncorr = df[num_filtered_cols].corr(numeric_only=True)  # Tính hệ số tương quan\nplt.figure(figsize=(20, 16))\nsns.heatmap(corr, annot=True, fmt=\".2f\", cmap='coolwarm', linewidths=0.5)\nplt.title(\"Ma trận tương quan giữa các biến số\")\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.linear_model import LinearRegression\nfrom sklearn.feature_selection import f_regression\n\n# Thử xem biến nào dự đoán label tốt hơn trong các biến có tương quan cao\nX = df[['SO QTY', 'ALLOCATION QTY', 'SUPPLIER INV AMOUNT', 'PURCHASE AMOUNT']].copy()\ny = df['label'].copy()\n\nmodel = LinearRegression()\nmodel.fit(X, y)\nprint(model.coef_)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = df.drop(columns = ['SO QTY','PURCHASE AMOUNT'])\npilot = pilot.drop(columns = ['SO QTY','PURCHASE AMOUNT'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_cols = ['SUBSIDIARY_CD', 'GLOBAL_NO', 'CLASSIFY_CD', 'CUST_CD',\n       'BRAND_CD', 'INNER_CD', 'SUPPLIER_CD',\n       'Stock class', 'Consider count hodiday Saturday', 'OTHER AREA SHIP DIV',\n       'PACKING RANK', 'PRODUCT_CD', 'PRODUCT ATTRIBUTION', 'SPECIAL DIV',\n       'LOGICAL PLANT', 'DIRECT SHIP FLG', 'DELI_DIV', 'Ship Mode',\n        'SHIP DECISION NO', 'SUPPLIER_DIV', 'SPECIAL_DIV', 'SO_DAY_OF_MONTH',\n       'SO_DAY_OF_WEEK', 'SO_TIME']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for col in cat_cols:\n    print(f\"Biến: {col} có {df[col].nunique()} giá trị khác nhau\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* GLOBAL_NO, INNER_CD, PRODUCT_CD, SHIP DECISION NO có giá trị khác nhau rất nhiều, ý nghĩa cho mô hình không cao, giống như số ID => Xóa các biến này\n* CLASSIFY_CD, CUST_CD, BRAND_CD', 'SUPPLIER_CD' cũng có giá trị khác nhau nhiều, nhưng ít hơn các biến kể trên, có thể thực hiện target encode/ hash_encode\n* Các biến còn lại sử dụng One-hot","metadata":{}},{"cell_type":"markdown","source":"## Feature Engineering ##","metadata":{}},{"cell_type":"code","source":"# Do em nhận thấy 'SO_TIME' có dạng hh:mm:ss, nên sẽ chuyển thành chuỗi và chỉ lấy 2 kí tự đầu trích xuất thành 'hour'\ndf['SO_TIME_str'] = df['SO_TIME'].astype(str).str.zfill(6)\ndf['hour'] = pd.to_datetime(df['SO_TIME_str'], format='%H%M%S').dt.hour","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Chuyển sang int trước để loại bỏ phần thập phân, sau đó về chuỗi\npilot['SO_TIME_str'] = pilot['SO_TIME'].astype(float).astype(int).astype(str)\n# Bổ sung số 0 đầu nếu cần (đảm bảo độ dài 6 ký tự)\npilot['SO_TIME_str'] = pilot['SO_TIME_str'].str.zfill(6)\n\npilot['hour'] = pd.to_datetime(pilot['SO_TIME_str'], format='%H%M%S').dt.hour","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_time_period(hour):\n    if 5 <= hour < 12:\n        return 'Sáng'\n    elif 12 <= hour < 18:\n        return 'Chiều'\n    elif 18 <= hour < 23:\n        return 'Tối'\n    else:\n        return 'Khuya'\n\ndf['time_period'] = df['hour'].apply(get_time_period)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\nplt.figure(figsize=(12,6))\nsns.countplot(data=df, x='time_period', order=df['time_period'].value_counts().index)\nplt.xticks(rotation=45)\nplt.title('Phân bố giá trị của biến time_period')\nplt.ylabel('Số lượng')\nplt.xlabel('time_period')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Gom nhóm cuối tuần hay không\ndf['IS_WEEKEND'] = df['SO_DAY_OF_WEEK'].isin([5, 6]).astype(int)\n\n# Gom tuần đầu/thứ 2/giữa/cuối tháng\ndf['MONTH_PHASE'] = pd.cut(df['SO_DAY_OF_MONTH'], bins=[0, 7, 14, 31],\n                           labels=['early', 'mid', 'late'])\n\n# Đã có biến IS_WEEKEND & MONTH_PHASE rồi nên chỉ cần trích xuất tháng/quý\ndf['Order_month'] = df['Order date'].dt.month             # Tháng trong năm (1-12)\ndf['Order_quarter'] = df['Order date'].dt.quarter         # Quý (1-4)\n\n# Tương tự với 'VSD'\ndf['VSD_month'] = df['VSD'].dt.month\ndf['VSD_quarter'] = df['VSD'].dt.quarter\n\ndf['VSD_IS_WEEKEND'] = df['VSD'].dt.dayofweek.isin([5,6]).astype(int)\ndf['VSD_MONTH_PHASE'] = pd.cut(df['VSD'].dt.day, bins=[0, 7, 14, 31],\n                           labels=['early', 'mid', 'late'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Gom nhóm cuối tuần hay không\npilot['IS_WEEKEND'] = pilot['SO_DAY_OF_WEEK'].isin([5, 6]).astype(int)\n\n# Gom tuần đầu/thứ 2/giữa/cuối tháng\npilot['MONTH_PHASE'] = pd.cut(pilot['SO_DAY_OF_MONTH'], bins=[0, 7, 14, 31],\n                           labels=['early', 'mid', 'late'])\n\n# Đã có biến IS_WEEKEND & MONTH_PHASE rồi nên chỉ cần trích xuất tháng/quý\npilot['Order_month'] = pilot['Order date'].dt.month             # Tháng trong năm (1-12)\npilot['Order_quarter'] = pilot['Order date'].dt.quarter         # Quý (1-4)\n\n#Tương tự với 'VSD'\npilot['VSD_month'] = pilot['VSD'].dt.month\npilot['VSD_quarter'] = pilot['VSD'].dt.quarter\n\npilot['VSD_IS_WEEKEND'] = pilot['VSD'].dt.dayofweek.isin([5,6]).astype(int)\npilot['VSD_MONTH_PHASE'] = pd.cut(pilot['VSD'].dt.day, bins=[0, 7, 14, 31],\n                           labels=['early', 'mid', 'late'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tạo biến ngày dự kiến sẽ giao hàng\ndf['Expected_delivery_days'] = (df['VSD'] - df['Order date']).dt.days\n# Clipping\ndf = winsorize_column(df,'Expected_delivery_days')\ndf['Expected_delivery_days']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pilot['Expected_delivery_days'] = (pilot['VSD'] - pilot['Order date']).dt.days\n#Clipping\npilot = winsorize_column(pilot,'Expected_delivery_days')\npilot['Expected_delivery_days']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Trung bình ngày giao hàng dự kiến của từng đơn vị vận chuyển\ndf['delay_per_supplier'] = df.groupby('SUPPLIER_CD')['Expected_delivery_days'].transform('mean')\ndf = winsorize_column(df,'delay_per_supplier')\npilot['delay_per_supplier'] = pilot.groupby('SUPPLIER_CD')['Expected_delivery_days'].transform('mean')\npilot = winsorize_column(pilot,'delay_per_supplier')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kiểm tra biến 'SPECIAL_DIV' và  \"SPECIAL DIV\" có hoàn toàn giống nhau ko\n(df['SPECIAL DIV'] == df['SPECIAL_DIV']).all()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = df.drop('SPECIAL DIV', axis=1)\npilot = pilot.drop('SPECIAL DIV', axis=1)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = df.drop(columns = ['GLOBAL_NO', 'INNER_CD', 'PRODUCT_CD', 'SHIP DECISION NO','SO_TIME', 'SO_TIME_str','hour','SO_DAY_OF_WEEK','SO_DAY_OF_MONTH','Order date', 'VSD'])\npilot = pilot.drop(columns = ['GLOBAL_NO', 'INNER_CD', 'PRODUCT_CD', 'SHIP DECISION NO','SO_TIME', 'SO_TIME_str','hour','SO_DAY_OF_WEEK','SO_DAY_OF_MONTH','Order date', 'VSD'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model ##","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(f\"/content/drive/MyDrive/Kì 4 - Năm 2/delay_prediction/df_train_gold_final.csv\")\npilot = pd.read_csv(f\"/content/drive/MyDrive/Kì 4 - Năm 2/delay_prediction/df_test_gold_final.csv\")\nsample_solution = pd.read_csv(f\"/content/drive/MyDrive/Kì 4 - Năm 2/delay_prediction/sample_Solution.csv\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = df.drop('label', axis=1)\ny = df['label']\n\n# Chia thành train (85%) và test (15%)\nX_train, X_dev, y_train, y_dev = train_test_split(X, y, test_size=0.15, stratify=y, random_state=42)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_test = pilot.copy().drop(columns = 'ID')\ny_test = sample_solution.copy().drop(columns = 'ID')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for col in X_train.columns:\n    types = set(X_train[col].apply(type))\n    if len(types) > 1:\n        print(f\"Cột {col} có kiểu hỗn hợp: {types}\")\nprint(f\"Không có kiểu hỗn hợp.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_encode = ['CLASSIFY_CD', 'CUST_CD', 'BRAND_CD', 'SUPPLIER_CD']\none_hot_encode = ['SUBSIDIARY_CD', 'time_period', 'Stock class',\n                    'Consider count hodiday Saturday', 'OTHER AREA SHIP DIV',\n                    'PACKING RANK', 'PRODUCT ATTRIBUTION',\n                    'LOGICAL PLANT', 'DIRECT SHIP FLG', 'DELI_DIV', 'Ship Mode',\n                    'SUPPLIER_DIV', 'SPECIAL_DIV', 'IS_WEEKEND', 'MONTH_PHASE',\n                    'Order_month', 'Order_quarter', 'VSD_month', 'VSD_quarter',\n                    'VSD_IS_WEEKEND', 'VSD_MONTH_PHASE']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_cols_new = [\"Sales order line number\",\"ALLOCATION QTY\",\"SUPPLIER INV AMOUNT\",\"PACK QTY\",\n            \"WEIGHT PER PIECE\", 'Expected_delivery_days',\n       'delay_per_supplier']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"threshold = 0.01 * len(X_train)  # Gom nhóm giá trị dưới 1%\nfor col in ['CLASSIFY_CD', 'CUST_CD', 'BRAND_CD', 'SUPPLIER_CD']:\n    value_counts = X_train[col].value_counts()\n    X_train[col] = X_train[col].apply(lambda x: x if value_counts.get(x, 0) >= threshold else 'Other')\n    X_test[col] = X_test[col].apply(lambda x: x if value_counts.get(x, 0) >= threshold else 'Other')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from category_encoders import TargetEncoder\n\nnumeric_transformer = StandardScaler()\nonehot_transformer = OneHotEncoder(handle_unknown='ignore', sparse_output=False)\ntarget_transformer = TargetEncoder(min_samples_leaf=5, smoothing=1.0)\n\npreprocessor = ColumnTransformer(\n    transformers=[\n        ('num', numeric_transformer, num_cols_new),\n         ('target', target_transformer, target_encode),\n        ('onehot', onehot_transformer, one_hot_encode)\n    ])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fit preprocessor \nX_train_transformed = preprocessor.fit_transform(X_train,y_train)\nX_dev_transformed = preprocessor.transform(X_dev)\nX_test_transformed = preprocessor.transform(X_test)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# SMOTE cho tập dev để mô phỏng tỷ lệ của tập test\nsmote = SMOTE(sampling_strategy=1/2, random_state=42)\nX_dev_smote, y_dev_smote = smote.fit_resample(X_dev_transformed, y_dev)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Áp dung undersampling\nundersampler = RandomUnderSampler(sampling_strategy=1/20, random_state=42)\nX_train_final, y_train_final = undersampler.fit_resample(X_train_transformed, y_train)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"xgb_model = XGBClassifier(\n    objective='binary:logistic',\n    eval_metric='logloss',\n    random_state=42,\n    n_jobs=1,\n    use_label_encoder=False\n)\n\nparam_grid_xgb = {\n    'n_estimators': [100, 200],\n    'max_depth': [3, 6],\n    'learning_rate': [0.01, 0.05, 0.1],\n    'subsample': [0.7, 1.0],\n    'colsample_bytree': [0.7, 1.0]\n}\ncv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)\n\ngrid_xgb = GridSearchCV(\n    xgb_model,\n    param_grid=param_grid_xgb,\n    scoring='f1_macro',\n    cv=cv,\n    verbose=1,\n    n_jobs=-1\n)\n\ngrid_xgb.fit(X_train_final, y_train_final)\n\nprint(\"Best XGBoost params:\", grid_xgb.best_params_)\nprint(\"Best XGBoost F1_macro:\", grid_xgb.best_score_)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred_xgb = grid_xgb.predict(X_dev_smote)\nprint(classification_report(y_dev_smote, y_pred_xgb))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# y_pred_test_xgb = grid_xgb.predict(X_test_transformed)\n# print(classification_report(y_test, y_pred_test_xgb))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"onehot_feature_names = preprocessor.named_transformers_['onehot'].get_feature_names_out(one_hot_encode)\n\n# 2. Từ TargetEncoder (giữ nguyên tên cột gốc)\ntarget_feature_names = target_encode  # TargetEncoder không thay đổi tên cột\n\n# 3. Từ StandardScaler (giữ nguyên tên cột số)\nscaler_feature_names = num_cols_new\n\n# Kết hợp tất cả tên đặc trưng\nfeature_names = np.concatenate([onehot_feature_names, target_feature_names, scaler_feature_names])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfeature_importance = pd.DataFrame({\n    'feature': feature_names,  # feature_names được định nghĩa từ preprocessor\n    'importance': grid_xgb.best_estimator_.feature_importances_\n}).sort_values(by='importance', ascending=False)\nprint(feature_importance)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"'Ship Mode' và 'DELI_DIV' có chỉ số FI thấp","metadata":{}},{"cell_type":"code","source":"df_submission = pd.DataFrame({\n    'ID': sample_solution['ID'],\n    \"label\": y_pred_test_xgb\n})","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_submission.to_csv('/kaggle/working/submission.csv', index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}