{"metadata":{"kaggle":{"accelerator":"none","dataSources":[{"sourceId":103436,"databundleVersionId":12476472,"sourceType":"competition"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"b19d3728","cell_type":"markdown","source":"# **Data Retrieve** ","metadata":{}},{"id":"2536c897","cell_type":"code","source":"import pandas as pd \nimport numpy as np","metadata":{},"outputs":[],"execution_count":null},{"id":"4e070a1f","cell_type":"markdown","source":"## Xác định các features","metadata":{}},{"id":"6d922481","cell_type":"markdown","source":"Phân chia dữ liệu thành 4 loại: nominal, ordinal, discrete, missing_columns; tiện cho việc xử lí ","metadata":{}},{"id":"42d34e4f","cell_type":"code","source":"nominal = ['SUBSIDIARY_CD','GLOBAL_NO','CLASSIFY_CD','CUST_CD','BRAND_CD','INNER_CD','SUPPLIER_CD','Stock class','Consider count hodiday Saturday','OTHER AREA SHIP DIV','PRODUCT_CD','PRODUCT ATTRIBUTION','SPECIAL DIV','LOGICAL PLANT','DIRECT SHIP FLG','DELI_DIV','Ship Mode','SHIP DECISION NO','SUPPLIER_DIV','SPECIAL_DIV','QTUF_RCV_NO','SOUF_RCV_NO','Order date', 'VSD','SO_TIME']\n\nordinal = ['PACKING RANK']\n\ndiscrete = ['Sales order line number', 'Consider count hodiday Saturday','SO QTY','ALLOCATION QTY','SUPPLIER INV AMOUNT','PURCHASE AMOUNT','PACK QTY','WEIGHT PER PIECE','SO_DAY_OF_MONTH','SO_DAY_OF_WEEK',]\n\nmissing_columns = ['OTHER AREA SHIP DIV',\n 'Ship Mode',\n 'SHIP DECISION NO',\n 'SUPPLIER_DIV',\n 'REASON_CD',\n 'SOUF_RCV_NO',\n 'QTUF_RCV_NO']","metadata":{},"outputs":[],"execution_count":null},{"id":"59fd7381","cell_type":"markdown","source":"Các thuộc tính phi logic trong việc dự đoán delay \n","metadata":{}},{"id":"c9416a12","cell_type":"code","source":"counter_intuitive = ['Consider count hodiday Saturday', 'REASON_CD', 'VSD'] ","metadata":{},"outputs":[],"execution_count":null},{"id":"146d1358","cell_type":"code","source":"number_features = [\n    'Sales order line number',\n    'WEIGHT PER PIECE',\n    'PURCHASE AMOUNT',\n    'SO QTY',\n    'ALLOCATION QTY',\n    'PACK QTY',\n    'SUPPLIER INV AMOUNT'\n]","metadata":{},"outputs":[],"execution_count":null},{"id":"72d4ee32","cell_type":"markdown","source":"## Merging dataset","metadata":{}},{"id":"d666e548","cell_type":"code","source":"df_not_delay_7_9 = pd.read_csv('dataset/not_delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv')\ndf_not_delay_4_6 = pd.read_csv('dataset/not_delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv')\ndf_delay_7_9 = pd.read_csv('dataset/delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv')\ndf_delay_4_6 = pd.read_csv('dataset/delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv')","metadata":{},"outputs":[],"execution_count":null},{"id":"dcd2638c","cell_type":"code","source":"features = df_not_delay_7_9.columns","metadata":{},"outputs":[],"execution_count":null},{"id":"15a46187","cell_type":"markdown","source":"## Filtering features from 7_9\n","metadata":{}},{"id":"26e1301e","cell_type":"markdown","source":"Lấy bộ df_7_9 làm chuẩn cho bộ 4_6","metadata":{}},{"id":"14dd8fcf","cell_type":"code","source":"df_not_delay_4_6 = df_not_delay_4_6[features]\ndf_delay_4_6 = df_delay_4_6[features]","metadata":{},"outputs":[],"execution_count":null},{"id":"4ff20591","cell_type":"code","source":"full_data_4_6 = pd.concat([df_delay_4_6, df_not_delay_4_6], axis=0)\nfull_data_7_9 = pd.concat([df_delay_7_9, df_not_delay_7_9], axis=0)","metadata":{},"outputs":[],"execution_count":null},{"id":"6a26317a","cell_type":"code","source":"full_data_4_6.columns","metadata":{},"outputs":[],"execution_count":null},{"id":"af58a21e","cell_type":"code","source":"full_data_4_6.shape","metadata":{},"outputs":[],"execution_count":null},{"id":"40e0568f","cell_type":"markdown","source":"## Ratio detecting","metadata":{}},{"id":"6f36f360","cell_type":"markdown","source":"Xác định tỉ lệ giữa label 0:1 ","metadata":{}},{"id":"258b285a","cell_type":"code","source":"ratio_label_0 = (full_data_4_6['label'] == 0).sum() / full_data_4_6['label'].count()\nprint(f\"Tỷ lệ label == 0: {ratio_label_0:.2%}\")\n\nratio_label_1 = (full_data_4_6['label'] == 1).sum() / full_data_4_6['label'].count()\nprint(f\"Tỷ lệ label == 1: {ratio_label_1:.2%}\")\n\nprint(f\"Tỉ lệ 0-1: {ratio_label_0/ratio_label_1:.0f}:1\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"8c971465","cell_type":"markdown","source":"# **Detect outliers**","metadata":{}},{"id":"e7590acd","cell_type":"code","source":"def detect_outliers_iqr(df, column):\n    \"\"\"\n    Phát hiện outliers và kiểm tra giá trị âm trong một cột số bằng IQR.\n\n    Returns:\n        outliers_df (pd.DataFrame): Các dòng chứa outliers.\n        outlier_indices (list): Danh sách index của outliers.\n        lower_bound (float): Ngưỡng dưới.\n        upper_bound (float): Ngưỡng trên.\n        negative_indices (list): Danh sách index của các giá trị âm.\n    \"\"\"\n    data = df[column]\n    Q1 = data.quantile(0.25)\n    Q3 = data.quantile(0.75)\n    IQR = Q3 - Q1\n\n    lower_bound = Q1 - 1.5 * IQR\n    upper_bound = Q3 + 1.5 * IQR\n\n    mask_outliers = (data < lower_bound) | (data > upper_bound)\n    outliers_df = df[mask_outliers]\n    outlier_indices = df[mask_outliers].index.tolist()\n\n    negative_indices = df[data < 0].index.tolist()\n\n    return outlier_indices\n","metadata":{},"outputs":[],"execution_count":null},{"id":"1356c3f5","cell_type":"code","source":"def remove_outliers_iqr(df1, df2, num_columns):\n    \"\"\"\n    Loại bỏ outliers trong df1 và df2 dựa trên các cột số của từng dataframe.\n    \n    Args:\n        df1 (pd.DataFrame): Dữ liệu gốc (ví dụ: tháng 4-6).\n        df2 (pd.DataFrame): Dữ liệu cần clean tương ứng (ví dụ: tháng 7-9).\n        num_columns (list): Danh sách các cột số để xét outliers.\n    \n    Returns:\n        df1_cleaned (pd.DataFrame): df1 sau khi loại bỏ outliers.\n        df2_cleaned (pd.DataFrame): df2 sau khi loại bỏ các dòng tương ứng.\n        all_outlier_indices_4_6 (set): Index bị loại bỏ từ df1.\n        all_outlier_indices_7_9 (set): Index bị loại bỏ từ df2.\n    \"\"\"\n    all_outlier_indices_4_6 = set()\n    all_outlier_indices_7_9 = set()\n    \n    for col in num_columns:\n        outlier_indices_4_6 = detect_outliers_iqr(df1, col)\n        outlier_indices_7_9 = detect_outliers_iqr(df2, col)\n\n        all_outlier_indices_4_6.update(outlier_indices_4_6)\n        all_outlier_indices_7_9.update(outlier_indices_7_9)\n\n    df1_cleaned = df1.drop(index=all_outlier_indices_4_6)\n    df2_cleaned = df2.drop(index=all_outlier_indices_7_9)\n\n    print(f\"✅ Số dòng đã loại bỏ trong df1: {len(all_outlier_indices_4_6)}\")\n    print(f\"✅ Số dòng đã loại bỏ trong df2: {len(all_outlier_indices_7_9)}\")\n    \n    return df1_cleaned, df2_cleaned, all_outlier_indices_4_6, all_outlier_indices_7_9\n","metadata":{},"outputs":[],"execution_count":null},{"id":"a5807398","cell_type":"code","source":"full_data_4_6_cleaned, full_data_7_9_cleaned, all_outlier_indices_4_6, all_outlier_indices_7_9 = remove_outliers_iqr(full_data_4_6, full_data_7_9, number_features)","metadata":{},"outputs":[],"execution_count":null},{"id":"81c745c5","cell_type":"code","source":"full_data_4_6.shape","metadata":{},"outputs":[],"execution_count":null},{"id":"105c9471","cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\n\ndef plot_percentage_bar(\n    df,\n    column,\n    figsize=(12, 6),\n    bar_color='#00C1B5',\n    edge_color='black',\n    bins=10\n):\n    \"\"\"\n    Vẽ biểu đồ phần trăm cho cột, hỗ trợ chia bin nếu là số.\n\n    Parameters:\n        df (pd.DataFrame): Dữ liệu đầu vào\n        column (str): Tên cột cần vẽ\n        figsize (tuple): Kích thước biểu đồ\n        bar_color (str): Màu cột\n        edge_color (str): Màu viền\n        bins (int or list): Số lượng bin hoặc danh sách bin\n    \"\"\"\n    plt.style.use('seaborn-v0_8-whitegrid')\n    plt.figure(figsize=figsize)\n\n    # Phân loại hoặc nhóm bin\n    if pd.api.types.is_numeric_dtype(df[column]):\n        binned = pd.cut(df[column], bins=bins, include_lowest=True)\n        counts = binned.value_counts().sort_index()\n        labels = [f\"{int(b.left)} - {int(b.right)}\" for b in counts.index]\n    else:\n        counts = df[column].value_counts().sort_index()\n        labels = counts.index.astype(str)\n\n    percent = (counts / counts.sum()) * 100\n\n    bars = plt.bar(labels, percent.values, color=bar_color, edgecolor=edge_color, width=0.7)\n\n    # Ghi nhãn phần trăm trên mỗi cột\n    for bar, pct in zip(bars, percent.values):\n        plt.text(\n            bar.get_x() + bar.get_width() / 2,\n            bar.get_height() + 0.5,\n            f'{pct:.1f}%',\n            ha='center',\n            va='bottom',\n            fontsize=10,\n            fontweight='bold'\n        )\n\n    # Giao diện\n    plt.title(f'Percentage Distribution of \"{column}\"', fontsize=14, weight='bold')\n    plt.ylabel('Percentage (%)')\n    plt.xlabel(f'{column} {\"(binned)\" if pd.api.types.is_numeric_dtype(df[column]) else \"\"}')\n    plt.xticks(rotation=45, ha='right')\n    plt.ylim(0, percent.max() * 1.2)\n    plt.tight_layout()\n    plt.grid(axis='y', linestyle='--', alpha=0.7)\n    plt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"f44a0160","cell_type":"code","source":"for feature in number_features:\n    plot_percentage_bar(full_data_4_6_cleaned, feature)","metadata":{},"outputs":[],"execution_count":null},{"id":"5ce5c054","cell_type":"markdown","source":"# **Visualize**","metadata":{}},{"id":"a32a57c6","cell_type":"markdown","source":"## Vẽ các giá trị có unique dưới 30, so sánh với target","metadata":{}},{"id":"2760da59","cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\n\n# Ngưỡng lọc số lượng unique < 100\nthreshold = 30\nlow_card_cols = [col for col in full_data_4_6_cleaned.columns if full_data_4_6_cleaned[col].nunique() < threshold and col != 'label']\n\n# Tone màu tài chính - bảo hiểm\ncolors = ['#00C1B5', '#444444']  # label 0, label 1\n\n# Vẽ biểu đồ theo phần trăm\nfor col in low_card_cols:\n    plt.figure(figsize=(40, 4))\n    \n    # Tính phần trăm label theo từng giá trị của cột\n    count_df = full_data_4_6_cleaned.groupby([col, 'label']).size().unstack(fill_value=0)\n    percent_df = count_df.div(count_df.sum(axis=1), axis=0) * 100\n    \n    # Vẽ biểu đồ\n    ax = percent_df.plot(kind='bar', stacked=False, color=colors, figsize=(10, 5))\n    \n    plt.title(f'Tỷ lệ phần trăm label theo {col}', fontsize=13)\n    plt.xlabel(col)\n    plt.ylabel('Tỷ lệ (%)')\n    plt.ylim(0, 110)\n    plt.xticks(rotation=45, ha='right')\n    plt.legend(title='Label')\n    plt.grid(axis='y', linestyle='--', alpha=0.3)\n    plt.tight_layout()\n    \n    # Hiển thị phần trăm trên cột\n    for container in ax.containers:\n        for bar in container:\n            height = bar.get_height()\n            if height > 0:\n                ax.text(\n                    bar.get_x() + bar.get_width() / 2,\n                    height + 1,\n                    f'{height:.1f}%',\n                    ha='center',\n                    va='bottom',\n                    fontsize=8,\n                    color='#444444'\n                )\n    \n    plt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"c173b7dc","cell_type":"markdown","source":"Xét lần lượt các feature: \n- `SUBSIDIARY_CD`: Mọi sample dữ liệu đều có nên em quyết định bỏ feature\n\n- `Stock class`: Với stock class 0, tỉ lệ label 1 là 4.8% và khi stock class 1, tỉ lệ label 0 là 2.4%. => Đối với các đơn hàng tồn kho loại 0 sẽ có xu hướng delay cao hơn so với loại 1\n\n- `Consider count holiday Saturday`: Với số ngày 0,1,4 sẽ có nguy cơ delay với lần lượt tỉ lệ là 2.3%, 2.9%, 3.3% => càng nhiều ngày thì nguy cơ delay càng cao. Nhưng ở ngày thứ 3 lại không có dữ liệu delay, có thể suy đoán data đã bị chỉnh sửa hoặc bị thiếu.\n\n- `OTHER AREA SHIP DIV`: Nếu vận chuyển ở khu vực khác (1) nguy cơ bị delay sẽ cao hơn 2,5% so với vận chuyển nội bộ.\n\n- `PACKING RANK`: Với các rank A, B, C tỉ lệ delay lần lượt là 2%, 2.6%, 2.8%. Với rank G, H, Z tỉ lệ delay lần lượt là 3.3%, 3.1%, 2.5%. Đặc biệt D có tỉ lệ delay thấp hơn hẳn với 1.5%\n\n- `PRODUCT ATTRIBUTION`: Sản phẩm tồn kho (1) sẽ có xu hướng ít delay hơn sản phẩm phải sản xuất (0) với tỉ lệ lần lượt là 2.4%, 4.8%\n\n- `SPECIAL DIV`: Với tiêu chuẩn 1 sẽ có tỉ lệ delay cao hơn gấp đôi so với tiêu chuẩn 0.\n\n- `LOGICAL PLANT`: Plant 53 sẽ có có tỉ lệ delay cao gấp đôi so với các tỉ lệ còn lại (3.4%)\n\n- `DIRECT SHIP FLG`: Với flag 1 sẽ có xu hướng delay cao hơn so với flag 0 (4.1% so với 2.2%)\n\n- `DELI_DIV`: Với phương thức vận chuyển đơn hàng 50 sẽ có khả năng delay cao nhất với tỉ lệ 13.8%, các phương thức khác lần lượt là 2.3%, 0.5%, 2.4%, 0.1%\n\n- `Ship Mode`: Phương thức O sẽ có tỉ lệ cao nhất là 66.7% tỉ lệ delay, phương thức X sẽ là 50% delay.","metadata":{}},{"id":"33d89e27","cell_type":"markdown","source":"Sơ lược các features có thể dùng để dự đoán","metadata":{}},{"id":"b9b42712","cell_type":"code","source":"Potential_features = ['Stock class','OTHER AREA SHIP DIV','SPECIAL DIV','LOGICAL PLANT','DELI_DIV','Ship Mode','SUPPLIER_DIV']","metadata":{},"outputs":[],"execution_count":null},{"id":"b3c8b582","cell_type":"code","source":"len(Potential_features)","metadata":{},"outputs":[],"execution_count":null},{"id":"68425354","cell_type":"markdown","source":"# **Chuẩn bị data cho WoE_IV**","metadata":{}},{"id":"d1d529ba","cell_type":"code","source":"df_WoE_IV = pd.concat([full_data_4_6_cleaned, full_data_7_9_cleaned], axis=0)","metadata":{},"outputs":[],"execution_count":null},{"id":"cbac9fc2","cell_type":"code","source":"df_WoE_IV","metadata":{},"outputs":[],"execution_count":null},{"id":"a146b669","cell_type":"code","source":"missing_columns = df_WoE_IV.columns[df_WoE_IV.isnull().any()].tolist()","metadata":{},"outputs":[],"execution_count":null},{"id":"cb48db84","cell_type":"code","source":"missing_columns","metadata":{},"outputs":[],"execution_count":null},{"id":"f25c31a6","cell_type":"markdown","source":"# **Fill Nan**","metadata":{}},{"id":"e3d94db4","cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\ndef clean_reason_cd_column(df, column='REASON_CD', fill_value=-1):\n    \"\"\"\n    Làm sạch dữ liệu cho một cột trong DataFrame:\n    - Thay thế các khoảng trắng bằng NaN\n    - Thay NaN bằng giá trị cụ thể (mặc định là -1)\n    \n    Parameters:\n        df (pd.DataFrame): DataFrame chứa dữ liệu cần xử lý\n        column (str): Tên cột cần xử lý\n        fill_value (int, float, str): Giá trị dùng để thay thế các giá trị thiếu\n        \n    Returns:\n        pd.DataFrame: DataFrame đã được xử lý\n    \"\"\"\n    df = df.copy()\n    \n    # Thay toàn bộ NaN trong DataFrame (nếu có)\n    df = df.fillna(fill_value)\n    \n    # Thay các giá trị chuỗi khoảng trắng bằng NaN\n    df.replace(\" \", np.nan, inplace=True)\n    df.replace(\"   \", np.nan, inplace=True)\n    df.replace(\"nan\", -1, inplace=True)\n    df['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].replace(\" \", \"-1\")\n    \n    # Làm sạch riêng cho cột cụ thể\n    if column in df.columns:\n        df[column] = df[column].fillna(fill_value)\n    \n    return df\n","metadata":{},"outputs":[],"execution_count":null},{"id":"2b536c6b","cell_type":"code","source":"full_data_4_6['OTHER AREA SHIP DIV'] =  pd.to_numeric(full_data_4_6['OTHER AREA SHIP DIV'], errors='coerce')\nfull_data_7_9['OTHER AREA SHIP DIV'] =  pd.to_numeric(full_data_7_9['OTHER AREA SHIP DIV'], errors='coerce')\ndf_WoE_IV['OTHER AREA SHIP DIV'] =  pd.to_numeric(df_WoE_IV['OTHER AREA SHIP DIV'], errors='coerce')\n\nfull_data_4_6_cleaned['OTHER AREA SHIP DIV'] = pd.to_numeric(full_data_4_6_cleaned['OTHER AREA SHIP DIV'], errors='coerce')\nfull_data_7_9_cleaned['OTHER AREA SHIP DIV'] = pd.to_numeric(full_data_7_9_cleaned['OTHER AREA SHIP DIV'], errors='coerce') ","metadata":{},"outputs":[],"execution_count":null},{"id":"f676a1fc","cell_type":"code","source":"df_WoE_IV = clean_reason_cd_column(df_WoE_IV, column='REASON_CD', fill_value=-1)\nfull_data_4_6 = clean_reason_cd_column(full_data_4_6, column='REASON_CD', fill_value=-1)\nfull_data_7_9 = clean_reason_cd_column(full_data_7_9, column='REASON_CD', fill_value=-1)\n\nfull_data_4_6_cleaned = clean_reason_cd_column(full_data_4_6_cleaned, column='REASON_CD', fill_value=-1)\nfull_data_7_9_cleaned = clean_reason_cd_column(full_data_7_9_cleaned, column='REASON_CD', fill_value=-1)","metadata":{},"outputs":[],"execution_count":null},{"id":"660f1c04","cell_type":"code","source":"full_data_4_6['OTHER AREA SHIP DIV'].unique()\nfull_data_7_9['OTHER AREA SHIP DIV'].unique()","metadata":{},"outputs":[],"execution_count":null},{"id":"efbf6908","cell_type":"markdown","source":"# **Datatype correcting**","metadata":{}},{"id":"c552f049","cell_type":"code","source":"def convert_column_types(df, nominal, ordinal):\n    \"\"\"\n    Chuyển kiểu dữ liệu của các cột trong DataFrame:\n    - Các cột trong nominal + ordinal → str\n    - Các cột còn lại → int64\n\n    Parameters:\n        df (pd.DataFrame): DataFrame đầu vào.\n        nominal (list): Danh sách các biến định tính (nominal).\n        ordinal (list): Danh sách các biến thứ bậc (ordinal).\n\n    Returns:\n        pd.DataFrame: DataFrame sau khi chuyển kiểu dữ liệu.\n    \"\"\"\n    df_converted = df.copy()\n    for column in df_converted.columns:\n        if column in nominal + ordinal:\n            df_converted[column] = df_converted[column].astype(str)\n        else:\n            df_converted[column] = df_converted[column].astype('int64')\n    return df_converted\n","metadata":{},"outputs":[],"execution_count":null},{"id":"a03c7864","cell_type":"code","source":"df_WoE_IV = convert_column_types(df_WoE_IV, nominal, ordinal)\nfull_data_7_9 = convert_column_types(full_data_7_9, nominal, ordinal)\nfull_data_4_6 = convert_column_types(full_data_4_6, nominal, ordinal)\n\nfull_data_4_6_cleaned = convert_column_types(full_data_4_6_cleaned, nominal, ordinal)\nfull_data_7_9_cleaned = convert_column_types(full_data_7_9_cleaned, nominal, ordinal)","metadata":{},"outputs":[],"execution_count":null},{"id":"fafa0e49","cell_type":"code","source":"df_WoE_IV.info()","metadata":{},"outputs":[],"execution_count":null},{"id":"a1cddb80","cell_type":"markdown","source":"# **Multicollinear Processing**","metadata":{}},{"id":"938c7576","cell_type":"markdown","source":"## Sử dụng WoE và IV cho feature selection: https://www.kaggle.com/code/chandrimad31/credit-risk-part-1-binning-woe-iv-pd-model#Binning,-Weight-of-Evidence-(WoE),-Information-Value-(IV)","metadata":{}},{"id":"5d1ca14b","cell_type":"code","source":"def iv_woe(data, target, bins=10, show_woe=False):\n    \n    newDF,woeDF = pd.DataFrame(), pd.DataFrame()\n    cols = data.columns\n    \n    #Run WoE and IV on all the independent variables\n    for ivars in cols[~cols.isin([target])]:\n        if (data[ivars].dtype.kind in 'bifc') and (len(np.unique(data[ivars]))>10):\n            binned_x = pd.qcut(data[ivars], bins,  duplicates='drop')\n            d0 = pd.DataFrame({'x': binned_x, 'y': data[target]})\n        else:\n            d0 = pd.DataFrame({'x': data[ivars], 'y': data[target]})\n        d = d0.groupby(\"x\", as_index=False).agg({\"y\": [\"count\", \"sum\"]})\n        d.columns = ['Cutoff', 'N', 'Events']\n        d['% of Events'] = np.maximum(d['Events'], 0.5) / d['Events'].sum()\n        d['Non-Events'] = d['N'] - d['Events']\n        d['% of Non-Events'] = np.maximum(d['Non-Events'], 0.5) / d['Non-Events'].sum()\n        d['WoE'] = np.log(d['% of Events']/d['% of Non-Events'])\n        d['IV'] = d['WoE'] * (d['% of Events'] - d['% of Non-Events'])\n        d.insert(loc=0, column='Variable', value=ivars)\n        print(\"Information value of \" + ivars + \" is \" + str(round(d['IV'].sum(),6)))\n        temp =pd.DataFrame({\"Variable\" : [ivars], \"IV\" : [d['IV'].sum()]}, columns = [\"Variable\", \"IV\"])\n        newDF=pd.concat([newDF,temp], axis=0)\n        woeDF=pd.concat([woeDF,d], axis=0)\n\n        if show_woe == True:\n            print(d)\n            \n    return newDF, woeDF\n\niv, woe = iv_woe(df_WoE_IV, target='label')","metadata":{},"outputs":[],"execution_count":null},{"id":"f98e9382","cell_type":"code","source":"iv.sort_values(by='IV', ascending=False, inplace=True)\niv","metadata":{},"outputs":[],"execution_count":null},{"id":"22eebfca","cell_type":"markdown","source":"**Encode Ordinal**","metadata":{}},{"id":"57918409","cell_type":"code","source":"from sklearn.preprocessing import OrdinalEncoder\n\ndef apply_ordinal_encoding(df, ordinal_columns):\n    \"\"\"\n    Mã hóa các cột ordinal theo thứ tự cho trước và ép kiểu thành int.\n\n    Parameters:\n        df (pd.DataFrame): DataFrame gốc.\n        ordinal_columns (list): Danh sách tên các cột ordinal cần mã hóa.\n        ranking (list): Thứ tự ưu tiên, ví dụ: ['A','B','C','D','H','G','Z']\n\n    Returns:\n        pd.DataFrame: DataFrame với các cột ordinal đã được mã hóa.\n    \"\"\"\n    ranking = ['A','B','C','D','H','G','Z']\n    # Lặp lại ranking cho từng cột (nếu tất cả dùng chung thứ tự)\n    categories = [ranking] * len(ordinal_columns)\n\n    encoder = OrdinalEncoder(categories=categories)\n    df[ordinal_columns] = encoder.fit_transform(df[ordinal_columns])\n    df[ordinal_columns] = df[ordinal_columns].astype('int')\n\n    return df","metadata":{},"outputs":[],"execution_count":null},{"id":"29cff6f6","cell_type":"code","source":"df_WoE_IV = apply_ordinal_encoding(df_WoE_IV, ordinal)","metadata":{},"outputs":[],"execution_count":null},{"id":"403195e6","cell_type":"markdown","source":"## Dùng spearman cho các thuộc tính category có thứ tự","metadata":{}},{"id":"4a0b932d","cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n# Lọc các cột ordinal và discrete\nordinal_discrete_columns = ordinal + discrete\n\n# Tính ma trận Pearson correlation\npearson_correlation_matrix = df_WoE_IV[ordinal_discrete_columns].corr(method='spearman')\n\n# Vẽ heatmap\nplt.figure(figsize=(12, 8))\nsns.heatmap(pearson_correlation_matrix, annot=True, cmap='coolwarm')\nplt.title(\"Pearson Correlation Heatmap (Ordinal & Discrete Variables)\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"eb015aea","cell_type":"markdown","source":"## Label nominal data để sử dụng Cramer's V","metadata":{}},{"id":"16e902a8","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nlabel_encoder = LabelEncoder()\n# Áp dụng LabelEncoder cho các cột 'VSD' và 'Order date'\ndf_WoE_IV['VSD'] = label_encoder.fit_transform(df_WoE_IV['VSD'])\ndf_WoE_IV['Order date'] = label_encoder.fit_transform(df_WoE_IV['Order date'])","metadata":{},"outputs":[],"execution_count":null},{"id":"9d708e76","cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport scipy.stats as ss\nimport warnings\n\n# Bỏ qua tất cả cảnh báo\nwarnings.filterwarnings(\"ignore\")\n\ndef cramers_v(x, y):\n    confusion_matrix = pd.crosstab(x, y)\n    chi2 = ss.chi2_contingency(confusion_matrix)[0]\n    n = confusion_matrix.sum().sum()\n    phi2 = chi2 / n\n    r, k = confusion_matrix.shape\n    phi2_corr = max(0, phi2 - ((k-1)*(r-1))/(n-1))  # Bias correction\n    r_corr = r - ((r-1)**2)/(n-1)\n    k_corr = k - ((k-1)**2)/(n-1)\n    return np.sqrt(phi2_corr / min((k_corr - 1), (r_corr - 1)))\n\ndef reduce_cardinality(series, top_n=100, other_label='OTHER'):\n    \"\"\"\n    Giữ lại top_n giá trị xuất hiện nhiều nhất trong một cột dạng category,\n    gán các giá trị còn lại thành nhãn 'OTHER' (hoặc tên khác do bạn chỉ định).\n    \"\"\"\n    top_categories = series.value_counts().nlargest(top_n).index\n    return series.apply(lambda x: x if x in top_categories else other_label)\n\n# Tạo ma trận Cramér's V cho các biến nominal\ncramer_matrix_nominal = pd.DataFrame(\n    np.zeros((len(nominal), len(nominal))),\n    index=nominal,\n    columns=nominal\n)\n\n# Giảm độ phân biệt của các biến nominal nếu cần\nreduced_data_nominal = df_WoE_IV[nominal].copy()\nfor col in nominal:\n    reduced_data_nominal[col] = reduce_cardinality(reduced_data_nominal[col], top_n=50)\n\n# Tính giá trị Cramér's V cho từng cặp biến nominal\nfor col1 in nominal:\n    for col2 in nominal:\n        if col1 == col2:\n            cramer_matrix_nominal.loc[col1, col2] = 1.0\n        else:\n            cramer_matrix_nominal.loc[col1, col2] = cramers_v(reduced_data_nominal[col1], reduced_data_nominal[col2])\n\n# Vẽ heatmap cho ma trận Cramér's V\nplt.figure(figsize=(15, 12))\nsns.heatmap(cramer_matrix_nominal, annot=True, fmt=\".2f\", cmap=\"YlGnBu\", square=True)\nplt.title(\"Cramér's V Heatmap giữa các biến Nominal\")\nplt.xticks(rotation=45, ha='right')\nplt.tight_layout()\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"08d62e4a","cell_type":"markdown","source":"# **Building features**","metadata":{}},{"id":"9b0fd84f","cell_type":"markdown","source":"Kết hợp visualize, IV, Spearman, Cramers'V em sẽ lọc tìm ra những feature phù hợp cho mô hình.","metadata":{}},{"id":"620fe9a9","cell_type":"code","source":"Weak_features = iv[iv['IV'] < 0.02]['Variable'].tolist()\nStrong_features = iv[(iv['IV'] >= 0.02) & (iv['IV'] <= 0.8)]['Variable'].tolist()","metadata":{},"outputs":[],"execution_count":null},{"id":"6be789bb","cell_type":"code","source":"for col in Strong_features:\n    print(col)","metadata":{},"outputs":[],"execution_count":null},{"id":"03e0ebd0","cell_type":"code","source":"full_data_7_9_cleaned['Sales order line number'].unique().max()","metadata":{},"outputs":[],"execution_count":null},{"id":"de1a8778","cell_type":"code","source":"Potential_features.extend(['Order date', 'BRAND_CD','SO QTY','PURCHASE AMOUNT','WEIGHT PER PIECE','SO_DAY_OF_MONTH','label'])","metadata":{},"outputs":[],"execution_count":null},{"id":"a6c930a7","cell_type":"code","source":"for col in Potential_features:\n    print(col)","metadata":{},"outputs":[],"execution_count":null},{"id":"83fa7509","cell_type":"code","source":"full_data_4_6['Order date'] = pd.to_datetime(full_data_4_6['Order date'])\nfull_data_7_9['Order date'] = pd.to_datetime(full_data_7_9['Order date'])","metadata":{},"outputs":[],"execution_count":null},{"id":"96cba5a8","cell_type":"code","source":"df_full_data = pd.concat([full_data_4_6, full_data_7_9], axis=0)\ndf_full_data_cleaned = pd.concat([full_data_4_6_cleaned, full_data_7_9_cleaned], axis=0)","metadata":{},"outputs":[],"execution_count":null},{"id":"b0b4ca3f","cell_type":"code","source":"Potential_features.append(\"CLASSIFY_CD\")","metadata":{},"outputs":[],"execution_count":null},{"id":"705569d6","cell_type":"code","source":"Potential_features.append(\"PRODUCT ATTRIBUTION\")\nPotential_features.append(\"SO_TIME\")\nPotential_features.append(\"SO_DAY_OF_WEEK\")\nPotential_features.append(\"VSD\")","metadata":{},"outputs":[],"execution_count":null},{"id":"4711bd01","cell_type":"code","source":"# Remove all occurrences of each character in 'CLASSIFY_CD' from Potential_features if present\nfor char in list('CLASSIFY_CD'):\n    if char in Potential_features:\n        Potential_features.remove(char)","metadata":{},"outputs":[],"execution_count":null},{"id":"98ea56f7","cell_type":"code","source":"Potential_features","metadata":{},"outputs":[],"execution_count":null},{"id":"4b461741","cell_type":"code","source":"df_full_data = df_full_data[Potential_features]\nfull_data_4_6 = full_data_4_6[Potential_features]\nfull_data_7_9 = full_data_7_9[Potential_features]\n\ndf_full_data_cleaned = df_full_data_cleaned[Potential_features]\nfull_data_4_6_cleaned = full_data_4_6_cleaned[Potential_features]\nfull_data_7_9_cleaned = full_data_7_9_cleaned[Potential_features]","metadata":{},"outputs":[],"execution_count":null},{"id":"58190a87","cell_type":"markdown","source":"# **Xuất file**","metadata":{}},{"id":"ddf74d94","cell_type":"code","source":"df_full_data.to_csv('df_full_data.csv', index=False)\nfull_data_4_6.to_csv('full_data_4_6.csv', index=False)\nfull_data_7_9.to_csv('full_data_7_9.csv', index=False)\n\ndf_full_data_cleaned.to_csv('df_full_data_cleaned.csv', index=False)\nfull_data_4_6_cleaned.to_csv('full_data_4_6_cleaned.csv', index=False)\nfull_data_7_9_cleaned.to_csv('full_data_7_9_cleaned.csv', index=False)","metadata":{},"outputs":[],"execution_count":null},{"id":"421e3b53","cell_type":"markdown","source":"# Cài đặt thư viện cần thiết","metadata":{}},{"id":"a38ad966","cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd","metadata":{},"outputs":[],"execution_count":null},{"id":"515400c9","cell_type":"code","source":"!pip install optuna\n!pip install xgboost\n!pip install catboost\n","metadata":{},"outputs":[],"execution_count":null},{"id":"c1b3ab96","cell_type":"code","source":"result = {}","metadata":{},"outputs":[],"execution_count":null},{"id":"b4e76dcb","cell_type":"code","source":"df_full_data = pd.read_csv('df_full_data.csv')\nfull_data_4_6 = pd.read_csv('full_data_4_6.csv')\nfull_data_7_9 = pd.read_csv('full_data_7_9.csv')\n\nfull_data_4_6_cleaned = pd.read_csv('full_data_4_6_cleaned.csv')\nfull_data_7_9_cleaned = pd.read_csv('full_data_7_9_cleaned.csv')\ndf_full_data_cleaned = pd.read_csv('df_full_data_cleaned.csv')","metadata":{},"outputs":[],"execution_count":null},{"id":"2a11b092","cell_type":"code","source":"full_data_4_6","metadata":{},"outputs":[],"execution_count":null},{"id":"cce40f0a","cell_type":"code","source":"nominal = ['SUBSIDIARY_CD','GLOBAL_NO','CLASSIFY_CD','CUST_CD','BRAND_CD','INNER_CD','SUPPLIER_CD','Stock class','Consider count hodiday Saturday','OTHER AREA SHIP DIV','PRODUCT_CD','PRODUCT ATTRIBUTION','SPECIAL DIV','LOGICAL PLANT','DIRECT SHIP FLG','DELI_DIV','Ship Mode','SHIP DECISION NO','SUPPLIER_DIV','SPECIAL_DIV','QTUF_RCV_NO','SOUF_RCV_NO','Order date' ,'VSD','SO_TIME']\n\nordinal = ['PACKING RANK']\n\ndiscrete = ['Sales order line number', 'Consider count hodiday Saturday','SO QTY','ALLOCATION QTY','SUPPLIER INV AMOUNT','PURCHASE AMOUNT','PACK QTY','WEIGHT PER PIECE','SO_DAY_OF_MONTH','SO_DAY_OF_WEEK','WEIGHT']\n\nmissing_columns = ['OTHER AREA SHIP DIV',\n 'Ship Mode',\n 'SHIP DECISION NO',\n 'SUPPLIER_DIV',\n 'REASON_CD',\n 'SOUF_RCV_NO',\n 'QTUF_RCV_NO']\n\nnumber = ['SO QTY','WEIGHT PER PIECE','PURCHASE AMOUNT']","metadata":{},"outputs":[],"execution_count":null},{"id":"acc2660d","cell_type":"markdown","source":"# Xác định phân phối của các biến category để sử dụng count encoding","metadata":{}},{"id":"2dd449d6","cell_type":"code","source":"# Chọn các cột object\nobject_columns = full_data_4_6.select_dtypes(include='object').columns.tolist()\n\n# Vẽ pie chart cho từng cột\nfor col in object_columns:\n    value_counts = full_data_4_6[col].value_counts(normalize=True)  # Lấy tỉ lệ %\n    labels = value_counts.index\n    sizes = value_counts.values\n\n    plt.figure(figsize=(6, 6))\n    plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=140)\n    plt.title(f'Pie Chart for {col}')\n    plt.axis('equal')  # Đảm bảo hình tròn\n    plt.tight_layout()\n    plt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"3d2c20f7","cell_type":"markdown","source":"Hàm Encode cho model học","metadata":{}},{"id":"37620d67","cell_type":"code","source":"def encode_object_columns(df, count_encode_cols, label_encode_cols):\n    \"\"\"\n    Kết hợp count encoding và label encoding cho các cột khác nhau.\n\n    Parameters:\n        df (pd.DataFrame): DataFrame đầu vào.\n        count_encode_cols (list): Danh sách các cột cần count encode.\n        label_encode_cols (list): Danh sách các cột cần label encode.\n\n    Returns:\n        df_encoded (pd.DataFrame): DataFrame sau mã hóa.\n        encoding_maps (dict): Từ điển lưu mapping cho từng cột.\n            - Với count encoding: category -> count\n            - Với label encoding: category -> code\n    \"\"\"\n    df_encoded = df.copy()\n    encoding_maps = {}\n\n    # Count encoding\n    for col in count_encode_cols:\n        counts = df_encoded[col].value_counts()\n        df_encoded[col] = df_encoded[col].map(counts)\n        encoding_maps[col] = counts.to_dict()\n\n    # Label encoding\n    for col in label_encode_cols:\n        df_encoded[col] = df_encoded[col].astype('category')\n        codes = dict(enumerate(df_encoded[col].cat.categories))\n        df_encoded[col] = df_encoded[col].cat.codes\n        encoding_maps[col] = {v: k for k, v in codes.items()}  # category -> code\n\n    return df_encoded\n","metadata":{},"outputs":[],"execution_count":null},{"id":"9fb8334a","cell_type":"code","source":"def convert_column_types(df, nominal, ordinal):\n    \"\"\"\n    Chuyển kiểu dữ liệu của các cột trong DataFrame:\n    - Các cột trong nominal + ordinal → str\n    - Các cột còn lại → int64\n\n    Parameters:\n        df (pd.DataFrame): DataFrame đầu vào.\n        nominal (list): Danh sách các biến định tính (nominal).\n        ordinal (list): Danh sách các biến thứ bậc (ordinal).\n\n    Returns:\n        pd.DataFrame: DataFrame sau khi chuyển kiểu dữ liệu.\n    \"\"\"\n    target = ['label']\n    df_converted = df.copy()\n    for column in df_converted.columns:\n        if column in nominal + ordinal:\n            df_converted[column] = df_converted[column].astype(str)\n        elif column in discrete + target:\n            df_converted[column] = df_converted[column].astype('int64')\n        else: \n            pass\n    return df_converted\n","metadata":{},"outputs":[],"execution_count":null},{"id":"3247fa53","cell_type":"code","source":"df_full_data = convert_column_types(df_full_data, nominal, ordinal)\nfull_data_7_9 = convert_column_types(full_data_7_9, nominal, ordinal)\nfull_data_4_6 = convert_column_types(full_data_4_6, nominal, ordinal)\n\ndf_full_data_cleaned = convert_column_types(df_full_data_cleaned, nominal, ordinal)\nfull_data_4_6_cleaned = convert_column_types(full_data_4_6_cleaned, nominal, ordinal)\nfull_data_7_9_cleaned = convert_column_types(full_data_7_9_cleaned, nominal, ordinal)\ndf_full_data.info()","metadata":{},"outputs":[],"execution_count":null},{"id":"82cfba7f","cell_type":"code","source":"object_columns = full_data_4_6.select_dtypes(include='object').columns.tolist()\n\ncount_encode_cols = object_columns.copy()   \nlabel_encode_cols =['Order date']","metadata":{},"outputs":[],"execution_count":null},{"id":"81fb6771","cell_type":"code","source":"df_full_data_encoded = encode_object_columns(df_full_data, count_encode_cols, label_encode_cols)\nfull_data_7_9_encoded = encode_object_columns(full_data_7_9, count_encode_cols, label_encode_cols)\nfull_data_4_6_encoded = encode_object_columns(full_data_4_6, count_encode_cols, label_encode_cols)\n\ndf_full_data_cleaned_encoded = encode_object_columns(df_full_data_cleaned, count_encode_cols, label_encode_cols)\nfull_data_4_6_cleaned_encoded = encode_object_columns(full_data_4_6_cleaned, count_encode_cols, label_encode_cols)\nfull_data_7_9_cleaned_encoded = encode_object_columns(full_data_7_9_cleaned, count_encode_cols, label_encode_cols)","metadata":{},"outputs":[],"execution_count":null},{"id":"dbdd07d1","cell_type":"markdown","source":"## Check outlier","metadata":{}},{"id":"d88d5ba9","cell_type":"code","source":"def remove_outliers_iqr(df, columns):\n    \"\"\"\n    Phát hiện và loại bỏ outliers trong nhiều cột số bằng phương pháp IQR.\n    Đồng thời kiểm tra các giá trị âm trong các cột đó.\n\n    Args:\n        df (pd.DataFrame): DataFrame gốc.\n        columns (list): Danh sách tên các cột cần xử lý.\n\n    Returns:\n        cleaned_df (pd.DataFrame): DataFrame sau khi loại bỏ outliers ở tất cả các cột.\n        outliers_info (dict): Thông tin chi tiết outliers cho từng cột.\n    \"\"\"\n    cleaned_df = df.copy()\n    outliers_info = {}\n\n    for column in columns:\n        data = cleaned_df[column]\n        Q1 = data.quantile(0.25)\n        Q3 = data.quantile(0.75)\n        IQR = Q3 - Q1\n\n        lower_bound = Q1 - 1.5 * IQR\n        upper_bound = Q3 + 1.5 * IQR\n\n        mask_outliers = (data < lower_bound) | (data > upper_bound)\n        outliers_df = cleaned_df[mask_outliers]\n        outlier_indices = outliers_df.index.tolist()\n\n        # Kiểm tra các giá trị âm\n        negative_indices = cleaned_df[data < 0].index.tolist()\n\n        # Cập nhật thông tin\n        outliers_info[column] = {\n            'outliers_df': outliers_df,\n            'outlier_indices': outlier_indices,\n            'lower_bound': lower_bound,\n            'upper_bound': upper_bound,\n            'negative_indices': negative_indices\n        }\n\n        # Loại bỏ outliers ở cột này\n        cleaned_df = cleaned_df[~mask_outliers]\n\n    return cleaned_df\n","metadata":{},"outputs":[],"execution_count":null},{"id":"3108d90d","cell_type":"markdown","source":"# Xử lí imbalanced - undersampling - 1:5","metadata":{}},{"id":"371cf776","cell_type":"code","source":"pip install imbalanced-learn","metadata":{},"outputs":[],"execution_count":null},{"id":"d5f34489","cell_type":"code","source":"from imblearn.under_sampling import RandomUnderSampler\n\ndef apply_random_undersampling(df, label_col='label', desired_ratio=1/5, random_state=42):\n    \"\"\"\n    Thực hiện undersampling để đạt được tỷ lệ thiểu số:đa số mong muốn (ví dụ: 1:5).\n\n    Parameters:\n    - df (pd.DataFrame): Dữ liệu đầu vào.\n    - label_col (str): Tên cột nhãn (label).\n    - desired_ratio (float): Tỷ lệ thiểu số:đa số sau khi undersample (vd: 1/5).\n    - random_state (int): Seed để tái lập kết quả.\n\n    Returns:\n    - pd.DataFrame: DataFrame sau khi undersample.\n    \"\"\"\n    X = df.drop(columns=[label_col])\n    y = df[label_col]\n\n    # Đếm số lượng mẫu từng lớp\n    class_counts = y.value_counts()\n    minority_class = class_counts.idxmin()\n    majority_class = class_counts.idxmax()\n    n_minority = class_counts[minority_class]\n    n_majority_target = int(n_minority / desired_ratio)\n\n    # Đảm bảo không lấy nhiều hơn dữ liệu hiện có\n    n_majority_target = min(n_majority_target, class_counts[majority_class])\n\n    sampling_strategy = {\n        minority_class: n_minority,\n        majority_class: n_majority_target\n    }\n\n    undersampler = RandomUnderSampler(sampling_strategy=sampling_strategy, random_state=random_state)\n    X_resampled, y_resampled = undersampler.fit_resample(X, y)\n\n    df_resampled = pd.concat([X_resampled, y_resampled], axis=1)\n    return df_resampled\n","metadata":{},"outputs":[],"execution_count":null},{"id":"e3e0184e","cell_type":"markdown","source":"# **Model**","metadata":{}},{"id":"527003f1","cell_type":"markdown","source":"## LightGBM","metadata":{}},{"id":"96372303","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\ndef label_encode_df(df, columns):\n    \"\"\"\n    Encode các biến phân loại bằng LabelEncoder.\n    \n    Args:\n        df (DataFrame): Dữ kiện bạn muốn mã hoá.\n        columns (list of str): Danh sách các dòng cần mã hoá.\n\n    Returns:\n        DataFrame: Dữ kiện sau khi được mã hoá.\n    \"\"\"\n    df = df.copy()\n    label = LabelEncoder()\n    for col in columns:\n        df[col] = label.fit_transform(df[col].astype(str))\n    return df\n\n\n# Cách sử dụng:\ncategory_columns = ['Stock class', 'OTHER AREA SHIP DIV',\n                    'SPECIAL DIV', 'LOGICAL PLANT',\n                    'DELI_DIV', 'Ship Mode',\n                    'SUPPLIER_DIV', 'BRAND_CD', 'Order date','CLASSIFY_CD','PRODUCT ATTRIBUTION','VSD']\n\ndf_full_data_encoded = label_encode_df(df_full_data_encoded, category_columns)\ndf_full_data_cleaned_encoded = label_encode_df(df_full_data_cleaned_encoded, category_columns)","metadata":{},"outputs":[],"execution_count":null},{"id":"31d8edf9","cell_type":"code","source":"df_full_data_encoded.dtypes","metadata":{},"outputs":[],"execution_count":null},{"id":"73aaa800","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom sklearn.metrics import f1_score, confusion_matrix, accuracy_score\nimport numpy as np\nfrom catboost import CatBoostClassifier, Pool\n\n# ------------------------------------------------------------------------------\n# 1. Chuẩn bị dữ kiện\n# ------------------------------------------------------------------------------\n# Giả sử bạn vẫn sử dụng df_full_data_encoded, label\nX_all = df_full_data_encoded.drop(columns=['label'])\n\ny_all = df_full_data_encoded['label'].astype(int)\nprint(\"X_all shape :\", X_all.shape)\n\n\n# ------------------------------------------------------------------------------\n# 2. Tách train, validation, test\n# ------------------------------------------------------------------------------\ntrain_idx, temp_idx = train_test_split(\n    X_all.index,\n    test_size=0.4,\n    random_state=42,\n    stratify=y_all\n)\n\nval_idx, test_idx = train_test_split(\n    temp_idx,\n    test_size=0.5,\n    random_state=42,\n    stratify=y_all.loc[temp_idx]\n)\n\nprint(f\"Train size: {len(train_idx)}\")\nprint(f\"Validation size: {len(val_idx)}\")\nprint(f\"Test size: {len(test_idx)}\")\n\n# ------------------------------------------------------------------------------\n# 3. Chuẩn bị dữ kiện huấn luyện\n# ------------------------------------------------------------------------------\ndf_train = df_full_data_encoded.loc[train_idx].copy()\ndf_train = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\nX_train = df_train.drop(columns=['label'])\n\ny_train = df_train['label'].astype(int)\n\n# ------------------------------------------------------------------------------\n# 4. Chuẩn bị dữ kiện validation\n# ------------------------------------------------------------------------------\nX_val = df_full_data_encoded.loc[val_idx].drop(columns=['label'])\n\ny_val = df_full_data_encoded.loc[val_idx, 'label'].astype(int)\n\n# ------------------------------------------------------------------------------\n# 5. Chuẩn bị dữ kiện test\n# ------------------------------------------------------------------------------\nX_test = df_full_data_encoded.loc[test_idx].drop(columns=['label'])\n\ny_test = df_full_data_encoded.loc[test_idx, 'label'].astype(int)\n\nprint(\"Train size (after undersampling):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n\n\n# ------------------------------------------------------------------------------\n# 6. Huấn luyện CatBoost mà KHÔNG grid search\n# ------------------------------------------------------------------------------\nmodel = CatBoostClassifier(\n    random_seed=42,\n    depth=6,\n    learning_rate=0.05,\n    iterations=1000,\n    l2_leaf_reg=3,\n    cat_features=category_columns,\n    early_stopping_rounds=100,\n    verbose=100\n)\n\nmodel.fit(\n    X_train, y_train,\n    eval_set=(X_val, y_val)\n)\n\n\n# ------------------------------------------------------------------------------\n# 7. Dùng mô hình để dự đoán\n# ------------------------------------------------------------------------------\ny_pred = model.predict(X_test)\nprint(\"Predictions finished.\")\nprint(\"F1-test =\", f1_score(y_test, y_pred))\nprint(\"Confusion matrix:\")\nprint(confusion_matrix(y_test, y_pred))\nprint(\"Accuracy-test =\", accuracy_score(y_test, y_pred))\n","metadata":{},"outputs":[],"execution_count":null},{"id":"ae74eb69","cell_type":"code","source":"import pickle\n\nwith open(\"catboost.pkl\", \"wb\") as f:\n    pickle.dump(model, f)\nprint(\"Mô hình catboost (catboost) đã được pickle.\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"0c6b68ba","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport lightgbm as lgb\nfrom sklearn.metrics import f1_score, confusion_matrix, accuracy_score\nimport numpy as np\n\n# ------------------------------------------------------------------------------\n# 1. Chuẩn bị dữ kiện\n# ------------------------------------------------------------------------------\n# Giả sử bạn vẫn sử dụng df_full_data_encoded, label\nX_all = df_full_data_encoded.drop(columns=['label'])\n\ny_all = df_full_data_encoded['label'].astype(int)\nprint(\"X_all shape :\", X_all.shape)\n\n\n# ------------------------------------------------------------------------------\n# 2. Tách train, validation, test\n# ------------------------------------------------------------------------------\ntrain_idx, temp_idx = train_test_split(\n    X_all.index,\n    test_size=0.4,\n    random_state=42,\n    stratify=y_all\n)\n\nval_idx, test_idx = train_test_split(\n    temp_idx,\n    test_size=0.5,\n    random_state=42,\n    stratify=y_all.loc[temp_idx]\n)\n\nprint(f\"Train size: {len(train_idx)}\")\nprint(f\"Validation size: {len(val_idx)}\")\nprint(f\"Test size: {len(test_idx)}\")\n\n# ------------------------------------------------------------------------------\n# 3. Chuẩn bị dữ kiện huấn luyện\n# ------------------------------------------------------------------------------\ndf_train = df_full_data_encoded.loc[train_idx].copy()\ndf_train = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\nX_train = df_train.drop(columns=['label'])\n\ny_train = df_train['label'].astype(int)\n\n# ------------------------------------------------------------------------------\n# 4. Chuẩn bị dữ kiện validation\n# ------------------------------------------------------------------------------\nX_val = df_full_data_encoded.loc[val_idx].drop(columns=['label'])\n\ny_val = df_full_data_encoded.loc[val_idx, 'label'].astype(int)\n\n# ------------------------------------------------------------------------------\n# 5. Chuẩn bị dữ kiện test\n# ------------------------------------------------------------------------------\nX_test = df_full_data_encoded.loc[test_idx].drop(columns=['label'])\n\ny_test = df_full_data_encoded.loc[test_idx, 'label'].astype(int)\n\nprint(\"Train size (after undersampling):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n\n\n# ------------------------------------------------------------------------------\n# 6. Huấn luyện LightGBM mà KHÔNG grid search\n# ------------------------------------------------------------------------------\nmodel = lgb.LGBMClassifier(\n    random_state=42,\n    num_leaves=31,\n    max_depth=-1,\n    min_data_in_leaf=20,\n    learning_rate=0.05\n)\n\nmodel.fit(\n    X_train, y_train,\n    eval_set=[(X_val, y_val)],\n    categorical_feature=category_columns,\n)\n\n\n# ------------------------------------------------------------------------------\n# 7. Dùng mô hình để dự đoán\n# ------------------------------------------------------------------------------\ny_pred = model.predict(X_test)\nprint(\"Predictions finished.\")\nprint(\"F1-test =\", f1_score(y_test, y_pred))\nprint(\"Confusion matrix:\")\nprint(confusion_matrix(y_test, y_pred))\nprint(\"Accuracy-test =\", accuracy_score(y_test, y_pred))\n","metadata":{},"outputs":[],"execution_count":null},{"id":"2fb09a93","cell_type":"code","source":"from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, roc_auc_score, confusion_matrix, classification_report\n\ndef evaluate_model(y_true, y_pred, y_pred_proba=None):\n    \"\"\"\n    Đánh giá mô hình phân loại với các chỉ số cơ bản.\n    \n    Args:\n        y_true (array-like): Nhãn thật.\n        y_pred (array-like): Dự đoán của mô hình.\n        y_pred_proba (array-like, optional): Dự đoán xác suất của mô hình nếu có.\n    \"\"\"\n    print(\"Accuracy :\", accuracy_score(y_true, y_pred))\n    print(\"F1 score :\", f1_score(y_true, y_pred, average='macro'))\n    print(\"Precision :\", precision_score(y_true, y_pred, average='weighted'))\n    print(\"Recall :\", recall_score(y_true, y_pred, average='weighted'))\n    print(\"Confusion Matrix:\")\n    print(confusion_matrix(y_true, y_pred))\n    print(\"\\nClassification Report:\")\n    print(classification_report(y_true, y_pred))\n    \n    if y_pred_proba is not None:\n        auc = roc_auc_score(y_true, y_pred_proba)\n        print(\"AUC :\", auc)\n\n\n# Cách sử dụng:\n# Giả sử bạn có:\n# y_test, y_pred, y_pred_proba\nevaluate_model(y_test, y_pred, y_pred_proba=None)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"7f2b13f4","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"ffcd36d2","cell_type":"code","source":"import pickle\n\nwith open(\"lightgbm_model.pkl\", \"wb\") as f:\n    pickle.dump(model, f)\nprint(\"Mô hình LightGBM (LGBMClassifier) đã được pickle.\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"96caa342","cell_type":"markdown","source":"# presshere","metadata":{}},{"id":"5b69a8be","cell_type":"code","source":"with open(\"lightgbm_model.pkl\", \"rb\") as f:\n    model_new = pickle.load(f)\nprint(\"Mô hình LightGBM (LGBMClassifier) được tải lại.\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"82e5fbfe","cell_type":"markdown","source":"## XGboost - Đã xử lí IB - A - No Fold","metadata":{}},{"id":"ed2401e2","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom catboost import CatBoostClassifier, Pool\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n# 1. X và y từ bản encode gốc\nX_all = full_data_4_6.drop(columns=['label'])\n# Áp dụng encode toàn bộ trước\ny_all = full_data_4_6['label'].astype(int)\nprint(\"X_all shape:\", X_all.shape)\n# 2. Tách chỉ số index cho train/test (80/20)\ntrain_idx, temp_idx = train_test_split(\n    X_all.index,\n    test_size=0.4,   # 40% còn lại\n    random_state=42,\n    stratify=y_all\n)\n\n# Tách temp_idx thành validation 20% và test 20%\nval_idx, test_idx = train_test_split(\n    temp_idx,\n    test_size=0.5,   # chia đôi temp_idx thành 20% val + 20% test\n    random_state=42,\n    stratify=y_all.loc[temp_idx]\n)\nprint(f\"Train size: {len(train_idx)} ({len(train_idx)/len(X_all)*100:.1f}%)\")\nprint(f\"Validation size: {len(val_idx)} ({len(val_idx)/len(X_all)*100:.1f}%)\")\nprint(f\"Test size: {len(test_idx)} ({len(test_idx)/len(X_all)*100:.1f}%)\")\n\n# 3. Chuẩn bị df_train \ndf_train = full_data_4_6.loc[train_idx].copy()\n# Áp dụng remove outliers cho df_train\ndf_train = remove_outliers_iqr(df_train, number)\n# Ap dụng undersampling\ndf_train_under = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\n# Áp dụng encode cho df_train_under\nX_train = df_train_under.drop(columns=['label'])\ny_train = df_train_under['label'].astype(int)\n\n# 4. Chuẩn bị df_val \ndf_val = full_data_4_6.loc[val_idx].copy()\nX_val = df_val.drop(columns=['label'])\ny_val = df_val['label'].astype(int)\n\n# 5. Chuẩn bị df_test\ndf_test = full_data_4_6.loc[test_idx].copy()\nX_test = df_test.drop(columns=['label'])\ny_test = df_test['label'].astype(int)\n\n# Tạo lại Pool cho val/test nếu dùng CatBoost\nval_pool = Pool(X_val, y_val, cat_features=category_columns)\ntest_pool = Pool(X_test, y_test, cat_features=category_columns)\ntrain_pool = Pool(X_train, y_train, cat_features=category_columns)\n# In kết quả để kiểm tra\nprint(\"Train size (after undersample):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"925137e6","cell_type":"code","source":"from xgboost import XGBClassifier\nfrom sklearn.metrics import f1_score\nfrom sklearn.metrics import precision_score, recall_score\n\nimport optuna\ndef objective(trial):\n    scale_pos_weight = (y_train == 0).sum() / (y_train == 1).sum()\n    params = {\n        'n_estimators': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'max_depth': trial.suggest_int('max_depth', 3, 10),\n        'subsample': trial.suggest_float('subsample', 0.5, 1.0),\n        'gamma': trial.suggest_float('gamma', 0, 5),\n        'reg_alpha': trial.suggest_float('reg_alpha', 0.0, 5.0),\n        'reg_lambda': trial.suggest_float('reg_lambda', 0.0, 5.0),\n        'tree_method': 'hist',\n        'eval_metric': 'logloss',\n        'early_stopping_rounds': 100,\n        'scale_pos_weight': scale_pos_weight\n    }\n\n    model = XGBClassifier(**params)\n    eval_set = [(X_val, y_val)]\n    model.fit(\n        X_train, y_train,\n        eval_set=eval_set,\n        verbose=False\n    )\n\n    preds = model.predict(X_val)\n    return f1_score(y_val, preds)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"dd0dc3f9","cell_type":"code","source":"study = optuna.create_study(direction='maximize')\nstudy.optimize(objective, n_trials=50)\n\nprint(\"Best trial:\")\nprint(study.best_trial)","metadata":{},"outputs":[],"execution_count":null},{"id":"df4c3225","cell_type":"code","source":"from xgboost import XGBClassifier\nfrom sklearn.metrics import (\n    f1_score, precision_score, recall_score,\n    roc_auc_score, roc_curve, precision_recall_curve,\n    classification_report, confusion_matrix\n)\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Lấy params tốt nhất từ Optuna\nbest_params = study.best_trial.params\n\n# Thêm các tham số cố định nếu cần\nbest_params.update({\n    'n_estimators': 1000,\n    'eval_metric': 'logloss',  # XGBoost không có f1 làm eval_metric trực tiếp\n})\n\n# Khởi tạo mô hình XGBoost\nbest_model = XGBClassifier(**best_params)\n\n# Huấn luyện mô hình\nbest_model.fit(\n    X_train, y_train,\n    eval_set=[(X_val, y_val)],\n    verbose=False\n)\n\n# Lấy xác suất dự đoán\ny_probs = best_model.predict_proba(X_test)[:, 1]\n\n# ===== Tối ưu threshold theo F1 =====\nval_probs = best_model.predict_proba(X_val)[:, 1]\nthresholds = np.linspace(0.1, 0.9, 100)\nf1_scores = [f1_score(y_val, val_probs >= t) for t in thresholds]\n\nbest_idx = np.argmax(f1_scores)\nbest_threshold = thresholds[best_idx]\n\nprint(f'Best threshold: {best_threshold:.3f}')\n\n# Dự đoán với threshold tối ưu\ny_pred_opt = (y_probs >= best_threshold).astype(int)","metadata":{},"outputs":[],"execution_count":null},{"id":"6278928e","cell_type":"code","source":"# ==== In các độ đo ====\nprint(f\"✅ Best Threshold: {best_threshold:.4f}\")\nprint(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\nprint(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\nprint(f\"PR AUC         : {pr_auc:.4f}\")\nprint(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n# ==== Biểu đồ F1 theo threshold ====\nplt.figure(figsize=(6, 4))\nplt.plot(thresholds, f1_scores, label='F1-score')\nplt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\nplt.xlabel('Threshold')\nplt.ylabel('F1 Score')\nplt.title('F1 Score vs. Threshold')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"1b24def2","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, roc_auc_score\n\n# Tính FPR, TPR và thresholds của ROC\nfpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n# Tính AUC ROC\nauc_score = roc_auc_score(y_test, y_probs)\n\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n# Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n# Tìm index gần nhất của best_threshold trong roc_thresholds\nbest_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\nplt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\nplt.xlabel('False Positive Rate (FPR)')\nplt.ylabel('True Positive Rate (TPR)')\nplt.title('ROC Curve')\nplt.legend(loc='lower right')\nplt.grid(True)\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"5de87446","cell_type":"code","source":"plt.figure(figsize=(6, 4))\nplt.plot(recall, precision, label=f'PR AUC = {pr_auc:.4f}')\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"e9bfb2db","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred_opt)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"7bf032c1","cell_type":"code","source":"result['XGBoost'] = {\n    'Precision': precision_score(y_test, y_pred_opt),\n    'Recall': recall_score(y_test, y_pred_opt),\n    'F1 Score': f1_score(y_test, y_pred_opt),\n}","metadata":{},"outputs":[],"execution_count":null},{"id":"ad52f211","cell_type":"markdown","source":"## CatBoost - đã xử lí imbalanced - A - A - No Kfold","metadata":{}},{"id":"e72f00d3","cell_type":"markdown","source":"Dùng Optuna ","metadata":{}},{"id":"12fc74c1","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom catboost import CatBoostClassifier, Pool\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n# 1. X và y từ bản encode gốc\nX_all = full_data_4_6.drop(columns=['label'])\n# Áp dụng encode toàn bộ trước\ny_all = full_data_4_6['label'].astype(int)\nprint(\"X_all shape:\", X_all.shape)\n# 2. Tách chỉ số index cho train/test (80/20)\ntrain_idx, temp_idx = train_test_split(\n    X_all.index,\n    test_size=0.4,   # 40% còn lại\n    random_state=42,\n    stratify=y_all\n)\n\n# Tách temp_idx thành validation 20% và test 20%\nval_idx, test_idx = train_test_split(\n    temp_idx,\n    test_size=0.5,   # chia đôi temp_idx thành 20% val + 20% test\n    random_state=42,\n    stratify=y_all.loc[temp_idx]\n)\nprint(f\"Train size: {len(train_idx)} ({len(train_idx)/len(X_all)*100:.1f}%)\")\nprint(f\"Validation size: {len(val_idx)} ({len(val_idx)/len(X_all)*100:.1f}%)\")\nprint(f\"Test size: {len(test_idx)} ({len(test_idx)/len(X_all)*100:.1f}%)\")\n\n# 3. Chuẩn bị df_train \ndf_train = full_data_4_6.loc[train_idx].copy()\n# Áp dụng remove outliers cho df_train\ndf_train = remove_outliers_iqr(df_train, number)\n# Ap dụng undersampling\ndf_train_under = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\n# Áp dụng encode cho df_train_under\nX_train = df_train_under.drop(columns=['label'])\ny_train = df_train_under['label'].astype(int)\n\n# 4. Chuẩn bị df_val \ndf_val = full_data_4_6.loc[val_idx].copy()\nX_val = df_val.drop(columns=['label'])\ny_val = df_val['label'].astype(int)\n\n# 5. Chuẩn bị df_test\ndf_test = full_data_4_6.loc[test_idx].copy()\nX_test = df_test.drop(columns=['label'])\ny_test = df_test['label'].astype(int)\n\n# Tạo lại Pool cho val/test nếu dùng CatBoost\nval_pool = Pool(X_val, y_val, cat_features=category_columns)\ntest_pool = Pool(X_test, y_test, cat_features=category_columns)\ntrain_pool = Pool(X_train, y_train, cat_features=category_columns)\n# In kết quả để kiểm tra\nprint(\"Train size (after undersample):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"56b82160","cell_type":"code","source":"import optuna\nfrom catboost import CatBoostClassifier, Pool\nfrom sklearn.metrics import f1_score\nfrom sklearn.model_selection import train_test_split\n\n# Hàm mục tiêu cho Optuna\ndef objective(trial):\n    # Hyperparameters cần tối ưu\n    params = {\n        'iterations': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'depth': trial.suggest_int('depth', 4, 10),\n        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n        'border_count': trial.suggest_int('border_count', 32, 255),\n        'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n        'eval_metric': 'F1',\n        'verbose': 0,\n        'task_type': 'CPU',  # hoặc 'GPU' nếu bạn dùng GPU\n        \"class_weights\": [1.0, 20.0],\n        'early_stopping_rounds': 30,\n    }\n\n    model = CatBoostClassifier(**params)\n    model.fit(\n    train_pool,\n    eval_set=val_pool,\n    use_best_model=True,\n    early_stopping_rounds=30\n    )\n\n    preds = model.predict(X_val)\n    return f1_score(y_val, preds)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"0020a38d","cell_type":"code","source":"study = optuna.create_study(direction='maximize')\nstudy.optimize(objective, n_trials=50)\n\nprint(\"Best trial:\")\nprint(study.best_trial)","metadata":{},"outputs":[],"execution_count":null},{"id":"7e57d7d5","cell_type":"code","source":"from sklearn.metrics import (\n    f1_score, precision_score, recall_score,\n    roc_auc_score, roc_curve, precision_recall_curve,\n    classification_report, confusion_matrix\n)\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Lấy params tốt nhất từ Optuna\nbest_params = study.best_trial.params\n\n# Thêm các tham số cố định nếu cần\nbest_params.update({\n    'n_estimators': 10000,\n    'eval_metric': 'F1',  \n    'random_state': 42,\n})\n\n# Khởi tạo mô hình XGBoost\nbest_model = CatBoostClassifier(**best_params)\n\n# Huấn luyện mô hình\nbest_model.fit(\n    train_pool,\n    eval_set=val_pool,\n    verbose=False\n)\n\n# Lấy xác suất dự đoán\ny_probs = best_model.predict_proba(X_test)[:, 1]\n\n# ===== Tối ưu threshold theo F1 =====\nval_probs = best_model.predict_proba(X_val)[:, 1]\nthresholds = np.linspace(0.1, 0.9, 100)\nf1_scores = [f1_score(y_val, val_probs >= t) for t in thresholds]\n\nbest_idx = np.argmax(f1_scores)\nbest_threshold = thresholds[best_idx]\n\nprint(f'Best threshold: {best_threshold:.3f}')\n\n# Dự đoán với threshold tối ưu\ny_pred_opt = (y_probs >= best_threshold).astype(int)\n\nprecision, recall, _ = precision_recall_curve(y_test, y_probs)\npr_auc = auc(recall, precision)","metadata":{},"outputs":[],"execution_count":null},{"id":"89cea75f","cell_type":"code","source":"# ==== In các độ đo ====\nprint(f\"✅ Best Threshold: {best_threshold:.4f}\")\nprint(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\nprint(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\nprint(f\"PR AUC         : {pr_auc:.4f}\")\nprint(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n# ==== Biểu đồ F1 theo threshold ====\nplt.figure(figsize=(6, 4))\nplt.plot(thresholds, f1_scores, label='F1-score')\nplt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\nplt.xlabel('Threshold')\nplt.ylabel('F1 Score')\nplt.title('F1 Score vs. Threshold')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"20283176","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, roc_auc_score\n\n# Tính FPR, TPR và thresholds của ROC\nfpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n# Tính AUC ROC\nauc_score = roc_auc_score(y_test, y_probs)\n\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n# Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n# Tìm index gần nhất của best_threshold trong roc_thresholds\nbest_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\nplt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\nplt.xlabel('False Positive Rate (FPR)')\nplt.ylabel('True Positive Rate (TPR)')\nplt.title('ROC Curve')\nplt.legend(loc='lower right')\nplt.grid(True)\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"695f5c7d","cell_type":"code","source":"plt.figure(figsize=(6, 4))\nplt.plot(recall, precision, label=f'PR AUC = {pr_auc:.4f}')\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"00a37842","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred_opt)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"b2da055f","cell_type":"code","source":"result['CatBoost_IB_A_A'] = {\n    'Precision': precision_score(y_test, y_pred_opt),\n    'Recall': recall_score(y_test, y_pred_opt),\n    'F1 Score': f1_score(y_test, y_pred_opt),\n}","metadata":{},"outputs":[],"execution_count":null},{"id":"e48188ca","cell_type":"markdown","source":"## CatBoost - đã xử lí imbalanced - A - B - No Kfold","metadata":{}},{"id":"c4d8adde","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom catboost import CatBoostClassifier, Pool\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n# 1. X và y từ bản encode gốc\nX_all = full_data_4_6.drop(columns=['label'])\n# Áp dụng encode toàn bộ trước\ny_all = full_data_4_6['label'].astype(int)\nprint(\"X_all shape:\", X_all.shape)\n# 2. Tách chỉ số index cho train/test (80/20)\nX_train, X_val, y_train, y_val = train_test_split(\n    X_all,\n    y_all,\n    test_size=0.2,\n    random_state=42,\n    stratify=y_all\n)\nprint(\"Train size (raw data):\", X_train.shape)\nprint(\"Val size (raw data):\", y_train.shape)\n\n# 3. Chuẩn bị df_train \ndf_train = pd.concat([X_train, y_train], axis=1)\n# Áp dụng remove outliers cho df_train\ndf_train = remove_outliers_iqr(df_train, number)\n# Ap dụng undersampling cho df_train\ndf_train = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\n\nX_train = df_train.drop(columns=['label'])\ny_train = df_train['label'].astype(int)\n\n# 4. Chuẩn bị df_val \ndf_val = pd.concat([X_val, y_val], axis=1)\nX_val = df_val.drop(columns=['label'])\ny_val = df_val['label'].astype(int)\n\n# 5. Chuẩn bị df_test\ndf_test = full_data_7_9.copy()\nX_test = df_test.drop(columns=['label'])\ny_test = df_test['label'].astype(int)\n\n# Tạo lại Pool cho val/test nếu dùng CatBoost\nval_pool = Pool(X_val, y_val, cat_features=category_columns)\ntest_pool = Pool(X_test, y_test, cat_features=category_columns)\ntrain_pool = Pool(X_train, y_train, cat_features=category_columns)\n# In kết quả để kiểm tra\nprint(\"Train size (after undersample):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"633b9f88","cell_type":"code","source":"import optuna\nfrom catboost import CatBoostClassifier, Pool\nfrom sklearn.metrics import f1_score\nfrom sklearn.model_selection import train_test_split\n\n# Hàm mục tiêu cho Optuna\ndef objective(trial):\n    # Hyperparameters cần tối ưu\n    params = {\n        'iterations': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'depth': trial.suggest_int('depth', 4, 10),\n        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n        'border_count': trial.suggest_int('border_count', 32, 255),\n        'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n        'eval_metric': 'F1',\n        'verbose': 0,\n        'task_type': 'CPU',  # hoặc 'GPU' nếu bạn dùng GPU\n        \"class_weights\": [1.0, 20.0],\n        'early_stopping_rounds': 30,\n    }\n\n    model = CatBoostClassifier(**params)\n    model.fit(\n    train_pool,\n    eval_set=val_pool,\n    use_best_model=True,\n    early_stopping_rounds=30\n    )\n\n    preds = model.predict(X_val)\n    return f1_score(y_val, preds)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"40fd5f89","cell_type":"code","source":"study = optuna.create_study(direction='maximize')\nstudy.optimize(objective, n_trials=50)\n\nprint(\"Best trial:\")\nprint(study.best_trial)","metadata":{},"outputs":[],"execution_count":null},{"id":"063eba73","cell_type":"code","source":"from sklearn.metrics import (\n    f1_score, precision_score, recall_score,\n    roc_auc_score, roc_curve, precision_recall_curve,\n    classification_report, confusion_matrix\n)\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Lấy params tốt nhất từ Optuna\nbest_params = study.best_trial.params\n\n# Thêm các tham số cố định nếu cần\nbest_params.update({\n    'n_estimators': 1000,\n    'eval_metric': 'F1',  \n    'random_state': 42,\n})\n\n# Khởi tạo mô hình XGBoost\nbest_model = CatBoostClassifier(**best_params)\n\n# Huấn luyện mô hình\nbest_model.fit(\n    train_pool,\n    eval_set=val_pool,\n    verbose=False\n)\n\n# Lấy xác suất dự đoán\ny_probs = best_model.predict_proba(X_test)[:, 1]\n\n# ===== Tối ưu threshold theo F1 =====\nval_probs = best_model.predict_proba(X_val)[:, 1]\nthresholds = np.linspace(0.1, 0.9, 100)\nf1_scores = [f1_score(y_val, val_probs >= t) for t in thresholds]\n\nbest_idx = np.argmax(f1_scores)\nbest_threshold = thresholds[best_idx]\n\nprint(f'Best threshold: {best_threshold:.3f}')\n\n# Dự đoán với threshold tối ưu\ny_pred_opt = (y_probs >= best_threshold).astype(int)\n\nprecision, recall, _ = precision_recall_curve(y_test, y_probs)\npr_auc = auc(recall, precision)","metadata":{},"outputs":[],"execution_count":null},{"id":"b5b41655","cell_type":"code","source":"# ==== In các độ đo ====\nprint(f\"✅ Best Threshold: {best_threshold:.4f}\")\nprint(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\nprint(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\nprint(f\"PR AUC         : {pr_auc:.4f}\")\nprint(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n# ==== Biểu đồ F1 theo threshold ====\nplt.figure(figsize=(6, 4))\nplt.plot(thresholds, f1_scores, label='F1-score')\nplt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\nplt.xlabel('Threshold')\nplt.ylabel('F1 Score')\nplt.title('F1 Score vs. Threshold')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"058fa95d","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, roc_auc_score\n\n# Tính FPR, TPR và thresholds của ROC\nfpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n# Tính AUC ROC\nauc_score = roc_auc_score(y_test, y_probs)\n\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n# Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n# Tìm index gần nhất của best_threshold trong roc_thresholds\nbest_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\nplt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\nplt.xlabel('False Positive Rate (FPR)')\nplt.ylabel('True Positive Rate (TPR)')\nplt.title('ROC Curve')\nplt.legend(loc='lower right')\nplt.grid(True)\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"a53687f0","cell_type":"code","source":"plt.figure(figsize=(6, 4))\nplt.plot(recall, precision, label=f'PR AUC = {pr_auc:.4f}')\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"2790e99c","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred_opt)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"c9532ef1","cell_type":"code","source":"result['CatBoost_IB_A_B'] = {\n    'Precision': precision_score(y_test, y_pred_opt),\n    'Recall': recall_score(y_test, y_pred_opt),\n    'F1 Score': f1_score(y_test, y_pred_opt),\n}","metadata":{},"outputs":[],"execution_count":null},{"id":"5215fbdd","cell_type":"markdown","source":"## CatBoost - đã xử lí imbalanced - B - A - No Kfold","metadata":{}},{"id":"1a9bac52","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom catboost import CatBoostClassifier, Pool\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n# 1. X và y từ bản encode gốc\nX_all = full_data_7_9.drop(columns=['label'])\n# Áp dụng encode toàn bộ trước\ny_all = full_data_7_9['label'].astype(int)\nprint(\"X_all shape:\", X_all.shape)\n# 2. Tách chỉ số index cho train/test (80/20)\nX_train, X_val, y_train, y_val = train_test_split(\n    X_all,\n    y_all,\n    test_size=0.2,\n    random_state=42,\n    stratify=y_all\n)\nprint(\"Train size (raw data):\", X_train.shape)\nprint(\"Val size (raw data):\", y_train.shape)\n\n# 3. Chuẩn bị df_train \ndf_train = pd.concat([X_train, y_train], axis=1)\n# Áp dụng remove outliers cho df_train\ndf_train = remove_outliers_iqr(df_train, number)\n# Ap dụng undersampling cho df_train\ndf_train = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20\n)\n\nX_train = df_train.drop(columns=['label'])\ny_train = df_train['label'].astype(int)\n\n# 4. Chuẩn bị df_val \ndf_val = pd.concat([X_val, y_val], axis=1)\nX_val = df_val.drop(columns=['label'])\ny_val = df_val['label'].astype(int)\n\n# 5. Chuẩn bị df_test\ndf_test = full_data_4_6.copy()\nX_test = df_test.drop(columns=['label'])\ny_test = df_test['label'].astype(int)\n\n# Tạo lại Pool cho val/test nếu dùng CatBoost\nval_pool = Pool(X_val, y_val, cat_features=category_columns)\ntest_pool = Pool(X_test, y_test, cat_features=category_columns)\ntrain_pool = Pool(X_train, y_train, cat_features=category_columns)\n# In kết quả để kiểm tra\nprint(\"Train size (after undersample):\", X_train.shape)\nprint(\"Validation size (raw data):\", X_val.shape)\nprint(\"Test size (raw data):\", X_test.shape)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"136797c0","cell_type":"code","source":"import optuna\nfrom catboost import CatBoostClassifier, Pool\nfrom sklearn.metrics import f1_score\nfrom sklearn.model_selection import train_test_split\n\n# Hàm mục tiêu cho Optuna\ndef objective(trial):\n    # Hyperparameters cần tối ưu\n    params = {\n        'iterations': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'depth': trial.suggest_int('depth', 4, 10),\n        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n        'border_count': trial.suggest_int('border_count', 32, 255),\n        'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n        'eval_metric': 'F1',\n        'verbose': 0,\n        'task_type': 'CPU',  # hoặc 'GPU' nếu bạn dùng GPU\n        \"class_weights\": [1.0, 20.0],\n        'early_stopping_rounds': 30,\n    }\n\n    model = CatBoostClassifier(**params)\n    model.fit(\n    train_pool,\n    eval_set=val_pool,\n    use_best_model=True,\n    early_stopping_rounds=30\n    )\n\n    preds = model.predict(X_val)\n    return f1_score(y_val, preds)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"73553efa","cell_type":"code","source":"study = optuna.create_study(direction='maximize')\nstudy.optimize(objective, n_trials=50)\n\nprint(\"Best trial:\")\nprint(study.best_trial)","metadata":{},"outputs":[],"execution_count":null},{"id":"13a9b6b7","cell_type":"code","source":"from sklearn.metrics import (\n    f1_score, precision_score, recall_score,\n    roc_auc_score, roc_curve, precision_recall_curve,\n    classification_report, confusion_matrix\n)\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Lấy params tốt nhất từ Optuna\nbest_params = study.best_trial.params\n\n# Thêm các tham số cố định nếu cần\nbest_params.update({\n    'n_estimators': 1000,\n    'eval_metric': 'F1',  \n    'random_state': 42,\n})\n\n# Khởi tạo mô hình XGBoost\nbest_model = CatBoostClassifier(**best_params)\n\n# Huấn luyện mô hình\nbest_model.fit(\n    train_pool,\n    eval_set=val_pool,\n    verbose=False\n)\n\n# Lấy xác suất dự đoán\ny_probs = best_model.predict_proba(X_test)[:, 1]\n\n# ===== Tối ưu threshold theo F1 =====\nval_probs = best_model.predict_proba(X_val)[:, 1]\nthresholds = np.linspace(0.1, 0.9, 100)\nf1_scores = [f1_score(y_val, val_probs >= t) for t in thresholds]\n\nbest_idx = np.argmax(f1_scores)\nbest_threshold = thresholds[best_idx]\n\nprint(f'Best threshold: {best_threshold:.3f}')\n\n# Dự đoán với threshold tối ưu\ny_pred_opt = (y_probs >= best_threshold).astype(int)\n\nprecision, recall, _ = precision_recall_curve(y_test, y_probs)\npr_auc = auc(recall, precision)","metadata":{},"outputs":[],"execution_count":null},{"id":"07251435","cell_type":"code","source":"# ==== In các độ đo ====\nprint(f\"✅ Best Threshold: {best_threshold:.4f}\")\nprint(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\nprint(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\nprint(f\"PR AUC         : {pr_auc:.4f}\")\nprint(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n# ==== Biểu đồ F1 theo threshold ====\nplt.figure(figsize=(6, 4))\nplt.plot(thresholds, f1_scores, label='F1-score')\nplt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\nplt.xlabel('Threshold')\nplt.ylabel('F1 Score')\nplt.title('F1 Score vs. Threshold')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"eff941b9","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, roc_auc_score\n\n# Tính FPR, TPR và thresholds của ROC\nfpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n# Tính AUC ROC\nauc_score = roc_auc_score(y_test, y_probs)\n\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n# Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n# Tìm index gần nhất của best_threshold trong roc_thresholds\nbest_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\nplt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\nplt.xlabel('False Positive Rate (FPR)')\nplt.ylabel('True Positive Rate (TPR)')\nplt.title('ROC Curve')\nplt.legend(loc='lower right')\nplt.grid(True)\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"d3937b49","cell_type":"code","source":"plt.figure(figsize=(6, 4))\nplt.plot(recall, precision, label=f'PR AUC = {pr_auc:.4f}')\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"eed3fa72","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred_opt)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"ac41710d","cell_type":"code","source":"result['CatBoost_IB_B_A'] = {\n    'Precision': precision_score(y_test, y_pred_opt),\n    'Recall': recall_score(y_test, y_pred_opt),\n    'F1 Score': f1_score(y_test, y_pred_opt),\n}","metadata":{},"outputs":[],"execution_count":null},{"id":"105d2db3","cell_type":"markdown","source":"## CatBoost - đã xử lí imbalanced - train: A + k%B -> test (1-k)%B - No Kfold","metadata":{}},{"id":"a46ff0ef","cell_type":"markdown","source":"## CatBoost - đã xử lí imbalanced - AB - Kfold 5","metadata":{}},{"id":"5cd5276b","cell_type":"markdown","source":"Dùng Optuna ","metadata":{}},{"id":"89bc878c","cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom catboost import Pool\n\nfrom sklearn.model_selection import train_test_split\n\n# 1. X và y từ bản encode gốc\nX_all = df_full_data.drop(columns=['label'])\ny_all = df_full_data['label'].astype(int)\n\n# 2. Tách chỉ số index cho train/test (80/20)\ntrain_idx, test_idx = train_test_split(\n    X_all.index,\n    test_size=0.2,\n    random_state=42,\n    stratify=y_all\n)\nprint(\"Train size (raw data):\", len(train_idx))\n# 3. Chuẩn bị df_train từ bản cleaned + encoded\ndf_train = df_full_data.loc[train_idx].copy()\n\n# Áp dụng remove outliers cho df_train\ndf_train = remove_outliers_iqr(df_train, number)\n\n# --- Áp dụng undersampling lên df_train (cần cả label) ---\ndf_train_under = apply_random_undersampling(\n    df_train,\n    label_col='label',\n    desired_ratio=1/20 \n)\n\n# Tạo tập validation từ df_train_under\nX_train = df_train_under.drop(columns=['label'])\ny_train = df_train_under['label'].astype(int)\n\n\n# 5. Lấy X_test, y_test từ bản gốc encode (chưa clean, chưa undersample)\ndf_test = df_full_data.loc[test_idx]\nX_test = df_test.drop(columns=['label'])\ny_test = df_test['label'].astype(int)\n\nX_tr, X_val, y_tr, y_val = train_test_split(\n    X_train, y_train,\n    test_size=0.2,\n    stratify=y_train,\n    random_state=42\n)\n\n# In kết quả để kiểm tra\nprint(\"Train size (after undersample):\", X_train.shape)\nprint(\"Test size (raw data):         \", X_test.shape)\n\n\n# Danh sách các cột phân loại\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n\n# Tạo Pool cho CatBoost\ntrain_pool = Pool(data=X_train, label=y_train, cat_features=category_columns)\nval_pool = Pool(data=X_val, label=y_val, cat_features=category_columns)\ntest_pool = Pool(data=X_test, label=y_test, cat_features=category_columns)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"e20ecaca","cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import f1_score\nfrom catboost import CatBoostClassifier, Pool\nimport optuna\nimport numpy as np\nfrom functools import partial\n\n# Chuẩn bị dữ liệu đầu vào cho objective\nX = X_train.reset_index(drop=True)\ny = y_train.reset_index(drop=True)\n\ndef objective(trial, X, y):\n    params = {\n        'iterations': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'depth': trial.suggest_int('depth', 4, 10),\n        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n        'border_count': trial.suggest_int('border_count', 32, 255),\n        'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n        'eval_metric': 'Logloss',  # Dùng logloss hoặc AUC\n        'verbose': 0,\n        'task_type': 'CPU',\n        'class_weights': {0: 1, 1: 20},\n        'early_stopping_rounds': 30,\n    }\n\n    skf = StratifiedKFold(n_splits=7, shuffle=True, random_state=42)\n    f1_scores = []\n\n    for train_idx, valid_idx in skf.split(X, y):\n        X_tr, X_val = X.iloc[train_idx], X.iloc[valid_idx]\n        y_tr, y_val = y.iloc[train_idx], y.iloc[valid_idx]\n\n        train_pool = Pool(X_tr, y_tr, cat_features=category_columns)\n        valid_pool = Pool(X_val, y_val, cat_features=category_columns)\n\n        model = CatBoostClassifier(**params)\n        model.fit(train_pool, eval_set=valid_pool, use_best_model=True)\n\n        preds = model.predict(X_val)\n        score = f1_score(y_val, preds.astype(int))\n        f1_scores.append(score)\n\n    return np.mean(f1_scores)\n\n# Tạo partial để truyền X, y vào objective\noptuna_objective = partial(objective, X=X, y=y)\n\nstudy = optuna.create_study(direction='maximize')\nstudy.optimize(optuna_objective, n_trials=5)\n\nprint(\"Best F1:\", study.best_value)\nprint(\"Best params:\", study.best_params)\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import f1_score\nfrom catboost import CatBoostClassifier, Pool\nimport optuna\nimport numpy as np\nfrom functools import partial\n\n# Chuẩn bị dữ liệu đầu vào cho objective\nX = X_train.reset_index(drop=True)\ny = y_train.reset_index(drop=True)\n\ndef objective(trial, X, y):\n    params = {\n        'iterations': 1000,\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n        'depth': trial.suggest_int('depth', 4, 10),\n        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n        'border_count': trial.suggest_int('border_count', 32, 255),\n        'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n        'eval_metric': 'Logloss',  # Dùng logloss hoặc AUC\n        'verbose': 0,\n        'task_type': 'CPU',\n        'class_weights': {0: 1, 1: 20},\n        'early_stopping_rounds': 30,\n    }\n\n    skf = StratifiedKFold(n_splits=7, shuffle=True, random_state=42)\n    f1_scores = []\n\n    for train_idx, valid_idx in skf.split(X, y):\n        X_tr, X_val = X.iloc[train_idx], X.iloc[valid_idx]\n        y_tr, y_val = y.iloc[train_idx], y.iloc[valid_idx]\n\n        train_pool = Pool(X_tr, y_tr, cat_features=category_columns)\n        valid_pool = Pool(X_val, y_val, cat_features=category_columns)\n\n        model = CatBoostClassifier(**params)\n        model.fit(train_pool, eval_set=valid_pool, use_best_model=True)\n\n        preds = model.predict(X_val)\n        score = f1_score(y_val, preds.astype(int))\n        f1_scores.append(score)\n\n    return np.mean(f1_scores)\n\n# Tạo partial để truyền X, y vào objective\noptuna_objective = partial(objective, X=X, y=y)\n\nstudy = optuna.create_study(direction='maximize')\nstudy.optimize(optuna_objective, n_trials=3)\n\nprint(\"Best F1:\", study.best_value)\nprint(\"Best params:\", study.best_params)","metadata":{},"outputs":[],"execution_count":null},{"id":"0406d98b","cell_type":"code","source":"from sklearn.metrics import (\n    f1_score, precision_score, recall_score,\n    roc_auc_score, roc_curve, precision_recall_curve,\n    classification_report, confusion_matrix\n)\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Lấy params tốt nhất từ Optuna\nbest_params = study.best_trial.params\n\n# Thêm các tham số cố định nếu cần\nbest_params.update({\n    'n_estimators': 1000,\n    'eval_metric': 'F1',  \n    'random_state': 42,\n})\n\n# Khởi tạo mô hình XGBoost\nbest_model = CatBoostClassifier(**best_params)\n\n# Huấn luyện mô hình\nbest_model.fit(\n    train_pool,\n    eval_set=test_pool,\n    verbose=False\n)\n\n# Lấy xác suất dự đoán\ny_probs = best_model.predict_proba(X_test)[:, 1]\n\n# ===== Tối ưu threshold theo F1 =====\nthresholds = np.linspace(0.1, 0.9, 100)\nf1_scores = [f1_score(y_test, y_probs >= t) for t in thresholds]\n\nbest_idx = np.argmax(f1_scores)\nbest_threshold = thresholds[best_idx]\nbest_f1 = f1_scores[best_idx]\n\nprint(f'Best threshold: {best_threshold:.3f} with F1 score: {best_f1:.4f}')\n\n# Dự đoán với threshold tối ưu\ny_pred_opt = (y_probs >= best_threshold).astype(int)","metadata":{},"outputs":[],"execution_count":null},{"id":"9598acad","cell_type":"code","source":"# ==== In các độ đo ====\nprint(f\"✅ Best Threshold: {best_threshold:.4f}\")\nprint(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\nprint(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\nprint(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\nprint(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n# ==== Biểu đồ F1 theo threshold ====\nplt.figure(figsize=(6, 4))\nplt.plot(thresholds, f1_scores, label='F1-score')\nplt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\nplt.xlabel('Threshold')\nplt.ylabel('F1 Score')\nplt.title('F1 Score vs. Threshold')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"d7ae1409","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, roc_auc_score\n\n# Tính FPR, TPR và thresholds của ROC\nfpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n# Tính AUC ROC\nauc_score = roc_auc_score(y_test, y_probs)\n\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n# Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n# Tìm index gần nhất của best_threshold trong roc_thresholds\nbest_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\nplt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\nplt.xlabel('False Positive Rate (FPR)')\nplt.ylabel('True Positive Rate (TPR)')\nplt.title('ROC Curve')\nplt.legend(loc='lower right')\nplt.grid(True)\nplt.show()\n","metadata":{},"outputs":[],"execution_count":null},{"id":"7f916f64","cell_type":"code","source":"from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport matplotlib.pyplot as plt\n\n# Tính confusion matrix\ncm = confusion_matrix(y_test, y_pred_opt)\n\n# Hiển thị confusion matrix\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"id":"b1c0401f","cell_type":"code","source":"result['CatBoost_IB_AB'] = {\n    'Precision': precision_score(y_test, y_pred_opt),\n    'Recall': recall_score(y_test, y_pred_opt),\n    'F1 Score': f1_score(y_test, y_pred_opt),\n}","metadata":{},"outputs":[],"execution_count":null},{"id":"ebf1bbf3","cell_type":"code","source":"# Sau khi bạn tìm được best_threshold\nmodel_file = \"catboost_model.cbm\"\nthreshold_file = \"catboost_threshold.txt\"\n\n# Lưu mô hình\nbest_model.save_model(model_file)\n\n# Lưu threshold vào một file văn bản\nwith open(threshold_file, \"w\") as f:\n    f.write(str(best_threshold))\n\nprint(\"Model and threshold successfully saved.\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"7823bc74","cell_type":"code","source":"import optuna\nfrom catboost import CatBoostClassifier, Pool\nfrom sklearn.metrics import f1_score, confusion_matrix\nfrom sklearn.utils import shuffle\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Cột phân loại\ncategory_columns = ['Stock class','BRAND_CD','Ship Mode','Order date','SUPPLIER_DIV','LOGICAL PLANT','OTHER AREA SHIP DIV','SPECIAL DIV','DELI_DIV']\n\n# Các giá trị k muốn thử\nk_values = [10, 30, 50, 70, 90]\n\n# Duyệt qua các giá trị k\nfor k_percent in k_values:\n    print(f\"\\n==== Running for test set = {k_percent}% ====\")\n    # Chia dữ liệu\n     # Tổng số bản ghi 7_9 raw\n    total_7_9 = len(full_data_7_9)\n    test_size = int(total_7_9 * (k_percent / 100))\n    train_size = total_7_9 - test_size\n\n    # Shuffle để lấy mẫu ngẫu nhiên\n    df_7_9_shuffled = full_data_7_9.sample(frac=1, random_state=42).reset_index(drop=True)\n\n    # Tách test và phần train từ tháng 7_9\n    df_test = df_7_9_shuffled.iloc[:test_size].copy()           # <- giữ nguyên, không clean\n    df_train_7_9_clean_part = full_data_7_9_cleaned.sample(n=train_size, random_state=42)  # <- phần clean tương ứng\n\n    # Gộp với df_train_4_6_clean để tạo tập train đầy đủ\n    df_train_full = pd.concat([full_data_4_6_cleaned, df_train_7_9_clean_part], ignore_index=True) \n\n    # Áp dụng undersampling\n    df_train_under = apply_random_undersampling(df_train_full, label_col='label', desired_ratio=1/20)\n\n    # Tách X, y\n    X_train = df_train_under.drop(columns=['label'])\n    y_train = df_train_under['label'].astype(int)\n\n    X_tr, X_val, y_tr, y_val = train_test_split(\n    X_train, y_train,\n    test_size=0.2,\n    stratify=y_train,\n    random_state=42\n    )  \n\n    X_test = df_test.drop(columns=['label'])\n    y_test = df_test['label'].astype(int)\n\n    # Tạo Pool\n    train_pool = Pool(data=X_tr, label=y_tr, cat_features=category_columns)\n    val_pool = Pool(data=X_val, label=y_val, cat_features=category_columns)\n    test_pool = Pool(data=X_test, label=y_test, cat_features=category_columns)\n\n    # Hàm mục tiêu Optuna\n    def objective(trial):\n    # Hyperparameters cần tối ưu\n        params = {\n            'iterations': 1000,\n            'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),\n            'depth': trial.suggest_int('depth', 4, 10),\n            'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 10),\n            'border_count': trial.suggest_int('border_count', 32, 255),\n            'random_strength': trial.suggest_float('random_strength', 1e-9, 10.0),\n            'eval_metric': 'F1',\n            'verbose': 0,\n            'task_type': 'CPU',  # hoặc 'GPU' nếu bạn dùng GPU\n            'class_weights': {0: 1, 1: 20},\n            'early_stopping_rounds': 30,\n        }\n\n        model = CatBoostClassifier(**params)\n        model.fit(\n            train_pool,\n            eval_set=val_pool,\n            use_best_model=True\n        )\n\n        preds = model.predict(X_test)\n        return f1_score(y_test, preds)\n\n    # Tối ưu với Optuna\n    study = optuna.create_study(direction='maximize')\n    study.optimize(objective, n_trials=5)\n\n    print(\"Best trial:\")\n    print(study.best_trial)\n\n    # Lấy params tốt nhất từ Optuna\n    best_params = study.best_trial.params\n\n    # Thêm các tham số cố định nếu cần\n    best_params.update({\n        'n_estimators': 1000,\n        'eval_metric': 'logloss',  # XGBoost không có f1 làm eval_metric trực tiếp\n        'random_state': 42,\n    })\n\n    # Khởi tạo mô hình XGBoost\n    best_model = CatBoostClassifier(**best_params)\n\n    # Huấn luyện mô hình\n    best_model.fit(\n        train_pool,\n        eval_set=test_pool,\n        verbose=False\n    )\n\n    # Lấy xác suất dự đoán\n    y_probs = best_model.predict_proba(X_test)[:, 1]\n\n    # ===== Tối ưu threshold theo F1 =====\n    thresholds = np.linspace(0.1, 0.9, 100)\n    f1_scores = [f1_score(y_test, y_probs >= t) for t in thresholds]\n\n    best_idx = np.argmax(f1_scores)\n    best_threshold = thresholds[best_idx]\n    best_f1 = f1_scores[best_idx]\n\n    print(f'Best threshold: {best_threshold:.3f} with F1 score: {best_f1:.4f}')\n\n    # Dự đoán với threshold tối ưu\n    y_pred_opt = (y_probs >= best_threshold).astype(int)\n\n    # ==== In các độ đo ====\n    print(f\"✅ Best Threshold: {best_threshold:.4f}\")\n    print(f\"F1-score       : {f1_score(y_test, y_pred_opt):.4f}\")\n    print(f\"Precision      : {precision_score(y_test, y_pred_opt):.4f}\")\n    print(f\"Recall         : {recall_score(y_test, y_pred_opt):.4f}\")\n    print(f\"AUC ROC        : {roc_auc_score(y_test, y_probs):.4f}\")\n    print(\"\\nClassification Report:\\n\", classification_report(y_test, y_pred_opt))\n\n    # ==== Biểu đồ F1 theo threshold ====\n    plt.figure(figsize=(6, 4))\n    plt.plot(thresholds, f1_scores, label='F1-score')\n    plt.axvline(best_threshold, color='red', linestyle='--', label=f'Best Threshold = {best_threshold:.2f}')\n    plt.xlabel('Threshold')\n    plt.ylabel('F1 Score')\n    plt.title('F1 Score vs. Threshold')\n    plt.legend()\n    plt.grid(True)\n    plt.tight_layout()\n    plt.show()\n\n    # Tính FPR, TPR và thresholds của ROC\n    fpr, tpr, roc_thresholds = roc_curve(y_test, y_probs)\n\n    # Tính AUC ROC\n    auc_score = roc_auc_score(y_test, y_probs)\n\n    plt.figure(figsize=(8, 6))\n    plt.plot(fpr, tpr, label=f'ROC curve (AUC = {auc_score:.4f})', color='blue')\n    plt.plot([0, 1], [0, 1], linestyle='--', color='gray')  # Đường chéo tham khảo\n\n    # Vẽ điểm tương ứng với threshold tối ưu (best_threshold)\n    # Tìm index gần nhất của best_threshold trong roc_thresholds\n    best_idx = (np.abs(roc_thresholds - best_threshold)).argmin()\n    plt.scatter(fpr[best_idx], tpr[best_idx], color='red', label=f'Best Threshold = {best_threshold:.2f}')\n\n    plt.xlabel('False Positive Rate (FPR)')\n    plt.ylabel('True Positive Rate (TPR)')\n    plt.title('ROC Curve')\n    plt.legend(loc='lower right')\n    plt.grid(True)\n    plt.show()\n\n    # Tính confusion matrix\n    cm = confusion_matrix(y_test, y_pred_opt)\n\n    # Hiển thị confusion matrix\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=best_model.classes_)\n    disp.plot(cmap=plt.cm.Blues)\n    plt.title(\"Confusion Matrix\")\n    plt.show()\n\n    # Lưu kết quả vào dictionary\n    result[f'CatBoost_k_{k_percent}'] = {\n        'Precision': precision_score(y_test, y_pred_opt),\n        'Recall': recall_score(y_test, y_pred_opt),\n        'F1 Score': f1_score(y_test, y_pred_opt),\n    }","metadata":{},"outputs":[],"execution_count":null},{"id":"00e4c174","cell_type":"markdown","source":"# **Nhận xét**","metadata":{}},{"id":"adee24b1","cell_type":"markdown","source":"Dựa vào bảng kết quả cho thấy mô hình CatBoost cho kết quả cao nhất so với LightGBM, XGBoost trong thực nghiệm train A test B. Quyết định sử dụng CatBoost cho 4 thực nghiệm tiếp theo cho thấy, Train A test B cho thấy mô hình sẽ ưu tiên recall  => Với độ phủ cao nhưng độ chính xác còn hạn chế, mô hình hiện tại có xu hướng ưu tiên không bỏ sót các đơn hàng bị trễ, chấp nhận việc có thể cảnh báo nhầm một số đơn hàng không bị trễ.","metadata":{}},{"id":"b5d069e4","cell_type":"markdown","source":"Xét trên bài toán delay, việc bỏ qua các đơn hàng bị delay sẽ dẫn đến những tình huống bất ngờ cho doanh nghiệp, chi phí phát sinh, giảm uy tín của doanh nghiệp đối với đơn vị cung cấp và khách hàng,... => Ta sẽ ưu tiên giảm lỗi FN để mô hình có thể nhận ra được càng nhiều đơn hàng có khả năng bị delay => Ưu tiên Recall hơn. ","metadata":{}},{"id":"7c934e07","cell_type":"markdown","source":"Với K tăng dần cho thấy, cả 3 thang đo điểm càng ngày càng giảm, cho thấy mô hình bị overfitting dẫn đến điểm F1 score giảm","metadata":{}},{"id":"d6560f6e","cell_type":"markdown","source":"Trong tương lai: em sẽ làm thêm các kĩ thuật feature engineering (Tạo ra thuộc tính mới), sử dụng ROC AUC để đánh giá kết quả","metadata":{}},{"id":"b276a519","cell_type":"code","source":"df = pd.DataFrame(result)\ndf.to_excel(\"result.xlsx\", index=True)","metadata":{},"outputs":[],"execution_count":null},{"id":"1fe41a6a","cell_type":"code","source":"import pandas as pd\n","metadata":{},"outputs":[],"execution_count":null},{"id":"333ca7cd","cell_type":"code","source":"PILOT10 = pd.read_csv('PILOT_10.csv')","metadata":{},"outputs":[],"execution_count":null},{"id":"e88d5387","cell_type":"code","source":"PILOT10","metadata":{},"outputs":[],"execution_count":null},{"id":"5081bf0a","cell_type":"code","source":"nominal = ['SUBSIDIARY_CD','GLOBAL_NO','CLASSIFY_CD','CUST_CD','BRAND_CD','INNER_CD','SUPPLIER_CD','Stock class','Consider count hodiday Saturday','OTHER AREA SHIP DIV','PRODUCT_CD','PRODUCT ATTRIBUTION','SPECIAL DIV','LOGICAL PLANT','DIRECT SHIP FLG','DELI_DIV','Ship Mode','SHIP DECISION NO','SUPPLIER_DIV','SPECIAL_DIV','QTUF_RCV_NO','SOUF_RCV_NO','Order date', 'VSD','SO_TIME']\n\nordinal = ['PACKING RANK']\n\ndiscrete = ['Sales order line number', 'Consider count hodiday Saturday','SO QTY','ALLOCATION QTY','SUPPLIER INV AMOUNT','PURCHASE AMOUNT','PACK QTY','WEIGHT PER PIECE','SO_DAY_OF_MONTH','SO_DAY_OF_WEEK',]\n\nmissing_columns = ['OTHER AREA SHIP DIV',\n 'Ship Mode',\n 'SHIP DECISION NO',\n 'SUPPLIER_DIV',\n 'REASON_CD',\n 'SOUF_RCV_NO',\n 'QTUF_RCV_NO']","metadata":{},"outputs":[],"execution_count":null},{"id":"6257d533","cell_type":"code","source":"Potential_features = ['Stock class',\n 'OTHER AREA SHIP DIV',\n 'SPECIAL DIV',\n 'LOGICAL PLANT',\n 'DELI_DIV',\n 'Ship Mode',\n 'SUPPLIER_DIV',\n 'Order date',\n 'BRAND_CD',\n 'SO QTY',\n 'PURCHASE AMOUNT',\n 'WEIGHT PER PIECE',\n 'SO_DAY_OF_MONTH',\n 'CLASSIFY_CD',\n 'PRODUCT ATTRIBUTION',\n 'SO_TIME',\n 'SO_DAY_OF_WEEK',\n 'VSD']\n\nPILOT10 = PILOT10[Potential_features]","metadata":{},"outputs":[],"execution_count":null},{"id":"c9782e40","cell_type":"code","source":"PILOT10","metadata":{},"outputs":[],"execution_count":null},{"id":"0280bf4f","cell_type":"code","source":"PILOT10['Order date'] = pd.to_datetime(PILOT10['Order date'])","metadata":{},"outputs":[],"execution_count":null},{"id":"2761711f","cell_type":"code","source":"def convert_column_types(df, nominal, ordinal):\n    \"\"\"\n    Chuyển kiểu dữ liệu của các cột trong DataFrame:\n    - Các cột trong nominal + ordinal → str\n    - Các cột còn lại → int64\n\n    Parameters:\n        df (pd.DataFrame): DataFrame đầu vào.\n        nominal (list): Danh sách các biến định tính (nominal).\n        ordinal (list): Danh sách các biến thứ bậc (ordinal).\n\n    Returns:\n        pd.DataFrame: DataFrame sau khi chuyển kiểu dữ liệu.\n    \"\"\"\n    df_converted = df.copy()\n    for column in df_converted.columns:\n        if column in nominal + ordinal:\n            df_converted[column] = df_converted[column].astype(str)\n        else:\n            df_converted[column] = df_converted[column].astype('int64')\n    return df_converted\n","metadata":{},"outputs":[],"execution_count":null},{"id":"cca29211","cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\ndef clean_reason_cd_column(df, column='REASON_CD', fill_value=-1):\n    \"\"\"\n    Làm sạch dữ liệu cho một cột trong DataFrame:\n    - Thay thế các khoảng trắng bằng NaN\n    - Thay NaN bằng giá trị cụ thể (mặc định là -1)\n    \n    Parameters:\n        df (pd.DataFrame): DataFrame chứa dữ liệu cần xử lý\n        column (str): Tên cột cần xử lý\n        fill_value (int, float, str): Giá trị dùng để thay thế các giá trị thiếu\n        \n    Returns:\n        pd.DataFrame: DataFrame đã được xử lý\n    \"\"\"\n    df = df.copy()\n    \n    # Thay toàn bộ NaN trong DataFrame (nếu có)\n    df = df.fillna(fill_value)\n    \n    # Thay các giá trị chuỗi khoảng trắng bằng NaN\n    df.replace(\" \", np.nan, inplace=True)\n    df.replace(\"   \", np.nan, inplace=True)\n    df.replace(\"nan\", -1, inplace=True)\n    df['OTHER AREA SHIP DIV'] = df['OTHER AREA SHIP DIV'].replace(\" \", \"-1\")\n    \n    # Làm sạch riêng cho cột cụ thể\n    if column in df.columns:\n        df[column] = df[column].fillna(fill_value)\n    \n    return df\n","metadata":{},"outputs":[],"execution_count":null},{"id":"e5a6a9b3","cell_type":"code","source":"PILOT10 = clean_reason_cd_column(PILOT10, column='OTHER AREA SHIP DIV')","metadata":{},"outputs":[],"execution_count":null},{"id":"3d9b83ae","cell_type":"code","source":"PILOT10 = convert_column_types(PILOT10, nominal, ordinal)","metadata":{},"outputs":[],"execution_count":null},{"id":"231e62bf","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nlabel_encoder = LabelEncoder()\nPILOT10['Order date'] = label_encoder.fit_transform(PILOT10['Order date'])","metadata":{},"outputs":[],"execution_count":null},{"id":"ffc97656","cell_type":"code","source":"def encode_object_columns(df, count_encode_cols, label_encode_cols):\n    \"\"\"\n    Kết hợp count encoding và label encoding cho các cột khác nhau.\n\n    Parameters:\n        df (pd.DataFrame): DataFrame đầu vào.\n        count_encode_cols (list): Danh sách các cột cần count encode.\n        label_encode_cols (list): Danh sách các cột cần label encode.\n\n    Returns:\n        df_encoded (pd.DataFrame): DataFrame sau mã hóa.\n        encoding_maps (dict): Từ điển lưu mapping cho từng cột.\n            - Với count encoding: category -> count\n            - Với label encoding: category -> code\n    \"\"\"\n    df_encoded = df.copy()\n    encoding_maps = {}\n\n    # Count encoding\n    for col in count_encode_cols:\n        counts = df_encoded[col].value_counts()\n        df_encoded[col] = df_encoded[col].map(counts)\n        encoding_maps[col] = counts.to_dict()\n\n    # Label encoding\n    for col in label_encode_cols:\n        df_encoded[col] = df_encoded[col].astype('category')\n        codes = dict(enumerate(df_encoded[col].cat.categories))\n        df_encoded[col] = df_encoded[col].cat.codes\n        encoding_maps[col] = {v: k for k, v in codes.items()}  # category -> code\n\n    return df_encoded\n","metadata":{},"outputs":[],"execution_count":null},{"id":"3a46727e","cell_type":"code","source":"PILOT10 = convert_column_types(PILOT10, nominal, ordinal)","metadata":{},"outputs":[],"execution_count":null},{"id":"fc481d25","cell_type":"code","source":"PILOT10.columns","metadata":{},"outputs":[],"execution_count":null},{"id":"bb0a3265","cell_type":"code","source":"for i in PILOT10.columns:\n    print(PILOT10[i].unique())","metadata":{},"outputs":[],"execution_count":null},{"id":"b9b89a03","cell_type":"code","source":"PILOT10.columns","metadata":{},"outputs":[],"execution_count":null},{"id":"6317d8c6","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\ndef label_encode_df(df, columns):\n    \"\"\"\n    Encode các biến phân loại bằng LabelEncoder.\n    \n    Args:\n        df (DataFrame): Dữ kiện bạn muốn mã hoá.\n        columns (list of str): Danh sách các dòng cần mã hoá.\n\n    Returns:\n        DataFrame: Dữ kiện sau khi được mã hoá.\n    \"\"\"\n    df = df.copy()\n    label = LabelEncoder()\n    for col in columns:\n        df[col] = label.fit_transform(df[col].astype(str))\n    return df\n\n\n# Cách sử dụng:\ncategory_columns = ['Stock class', 'OTHER AREA SHIP DIV',\n                    'SPECIAL DIV', 'LOGICAL PLANT',\n                    'DELI_DIV', 'Ship Mode',\n                    'SUPPLIER_DIV', 'BRAND_CD', 'Order date','CLASSIFY_CD','PRODUCT ATTRIBUTION','SO_TIME','VSD']\n\ndf_full_data_encoded = label_encode_df(PILOT10, category_columns)\ndf_full_data_cleaned_encoded = label_encode_df(PILOT10, category_columns)","metadata":{},"outputs":[],"execution_count":null},{"id":"d6f783cc","cell_type":"code","source":"df_full_data_encoded.dtypes","metadata":{},"outputs":[],"execution_count":null},{"id":"bf4ea653","cell_type":"code","source":"X_new = PILOT10","metadata":{},"outputs":[],"execution_count":null},{"id":"101a0292","cell_type":"code","source":"import pickle\nwith open(\"catboost.pkl\", \"rb\") as f:\n    model_new = pickle.load(f)\nprint(\"Mô hình LightGBM (LGBMClassifier) được tải lại.\")\n","metadata":{},"outputs":[],"execution_count":null},{"id":"98be132c","cell_type":"code","source":"len(df_full_data_encoded.columns)","metadata":{},"outputs":[],"execution_count":null},{"id":"0e090263","cell_type":"code","source":"y_pred = model_new.predict(df_full_data_encoded)\nprint(\"Predictions finished.\")","metadata":{},"outputs":[],"execution_count":null},{"id":"d59d7dd7","cell_type":"code","source":"import pandas as pd\n\n# Tạo DataFrame để lưu\nresults = pd.DataFrame({'ID': range(1, len(y_pred) + 1), 'label': y_pred})\n\n# Lưu CSV\nresults.to_csv(\"submission.csv\", index=False)\nprint(\"File submission.csv đã được lưu.\")\n","metadata":{"vscode":{"languageId":"ruby"}},"outputs":[],"execution_count":null}]}