{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":103436,"databundleVersionId":12476472,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:20.976141Z","iopub.execute_input":"2025-06-18T11:53:20.976296Z","iopub.status.idle":"2025-06-18T11:53:21.242732Z","shell.execute_reply.started":"2025-06-18T11:53:20.976278Z","shell.execute_reply":"2025-06-18T11:53:21.242127Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"1. Đọc dữ liệu","metadata":{}},{"cell_type":"code","source":"delay_4_6 = pd.read_csv(\"/kaggle/input/ds-108-p-21-assigment-06/delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv\")\ndelay_7_9 = pd.read_csv(\"/kaggle/input/ds-108-p-21-assigment-06/delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv\")\nnot_delay_4_6 = pd.read_csv(\"/kaggle/input/ds-108-p-21-assigment-06/not_delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv\", low_memory=False)\nnot_delay_7_9 = pd.read_csv(\"/kaggle/input/ds-108-p-21-assigment-06/not_delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv\", low_memory=False)\nPILOT_10 = pd.read_csv(\"/kaggle/input/ds-108-p-21-assigment-06/PILOT_10.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:21.243485Z","iopub.execute_input":"2025-06-18T11:53:21.243801Z","iopub.status.idle":"2025-06-18T11:53:31.985471Z","shell.execute_reply.started":"2025-06-18T11:53:21.243783Z","shell.execute_reply":"2025-06-18T11:53:31.984915Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"2. Gộp tất cả dữ liệu CÓ NHÃN vào một DataFrame duy nhất (df)","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ndf = pd.concat([delay_4_6, not_delay_4_6, delay_7_9, not_delay_7_9], ignore_index=True)\ntest_df = pd.concat([PILOT_10], ignore_index=True)\n\n# Đảm bảo cột 'label' của df sạch NaN \ndf['label'] = df['label'].astype(str).str.strip()\ndf['label'] = df['label'].replace('', pd.NA)\ndf.dropna(subset=['label'], inplace=True)\n\n\n# Chia train và val\ntrain_df, val_df = train_test_split(df, test_size=0.2, random_state=42, stratify=df['label'])\n\n# Tách X và y từ train_df\nX_train = train_df.drop(columns=['label']).copy()\ny_train = train_df['label'].copy()\n\n# Tách X và y_true từ val_df (tập validation)\nX_val = val_df.drop(columns=['label']).copy()\ny_val_true = val_df['label'].copy()\n\n# Gán test_df vào biến X_test \nX_test = test_df.copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:31.986888Z","iopub.execute_input":"2025-06-18T11:53:31.987081Z","iopub.status.idle":"2025-06-18T11:53:37.470547Z","shell.execute_reply.started":"2025-06-18T11:53:31.987064Z","shell.execute_reply":"2025-06-18T11:53:37.469947Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**PROCESSING**","metadata":{}},{"cell_type":"code","source":"# Danh sách các feature bị loại bỏ từ báo cáo (do tương quan thấp, ảnh hưởng thấp, hoặc target leak)\nfeatures_to_drop = [\n    'SUBSIDIARY_CD', 'GLOBAL_NO', 'BRAND_CD', 'Sales order line number',\n    'Stock class', 'ALLOCATION QTY', 'SPECIAL DIV', 'LOGICAL PLANT',\n    'PURCHASE AMOUNT', 'DIRECT SHIP FLG', 'SHIP DECISION NO', 'PACK QTY',\n    'SUPPLIER_DIV', 'SPECIAL_DIV', 'SO_DAY_OF_MONTH', 'SO_DAY_OF_WEEK',\n    'SO_TIME', 'QTUF_RCV_NO', 'SOUF_RCV_NO','REASON_CD' \n]\n\n# Loại bỏ các feature đã xác định khỏi X_train, X_val và X_test\nX_train_processed = X_train.drop(columns=[col for col in features_to_drop if col in X_train.columns], errors='ignore')\nX_val_processed = X_val.drop(columns=[col for col in features_to_drop if col in X_val.columns], errors='ignore')\nX_test_processed = X_test.drop(columns=[col for col in features_to_drop if col in X_test.columns], errors='ignore') # Đã đổi final_test_df thành X_test\n\n# Đảm bảo X_train, X_val và X_test có cùng tập hợp cột và thứ tự sau khi loại bỏ.\nfinal_model_cols = list(set(X_train_processed.columns) & set(X_val_processed.columns) & set(X_test_processed.columns))\n\nX_train_processed = X_train_processed[final_model_cols].copy()\nX_val_processed = X_val_processed[final_model_cols].copy()\nX_test_processed = X_test_processed[final_model_cols].copy() # Đã đổi final_test_df thành X_test\n\n# Sắp xếp lại cột để đảm bảo thứ tự giống nhau cho LightGBM\nX_train_processed = X_train_processed.reindex(columns=sorted(X_train_processed.columns))\nX_val_processed = X_val_processed.reindex(columns=sorted(X_val_processed.columns))\nX_test_processed = X_test_processed.reindex(columns=sorted(X_test_processed.columns)) # Đã đổi final_test_df thành X_test\n\n\n# Xử lý giá trị thiếu (NaN)\nnumerical_cols = X_train_processed.select_dtypes(include=['number']).columns\nfor col in numerical_cols:\n    X_train_processed[col] = pd.to_numeric(X_train_processed[col], errors='coerce')\n    X_val_processed[col] = pd.to_numeric(X_val_processed[col], errors='coerce')\n    X_test_processed[col] = pd.to_numeric(X_test_processed[col], errors='coerce') # Áp dụng cho X_test\n\n    median_val = X_train_processed[col].median() # Tính từ X_train_processed\n    X_train_processed[col].fillna(median_val, inplace=True)\n    X_val_processed[col].fillna(median_val, inplace=True)\n    X_test_processed[col].fillna(median_val, inplace=True) # Áp dụng cho X_test\n\n# Lấy danh sách các cột object sau khi điền NaN (để chuyển thành category)\ncategorical_features_for_lgbm = X_train_processed.select_dtypes(include='object').columns.tolist()\n\nfor col in categorical_features_for_lgbm:\n    mode_val = X_train_processed[col].mode()[0] # Tính từ X_train_processed\n    X_train_processed[col].fillna(mode_val, inplace=True)\n    X_val_processed[col].fillna(mode_val, inplace=True)\n    X_test_processed[col].fillna(mode_val, inplace=True) # Áp dụng cho X_test\n\n# Chuyển đổi sang Category\nfor col in categorical_features_for_lgbm:\n    # Đảm bảo các cấp độ (categories) chỉ được xác định từ X_train_processed\n    all_categories = X_train_processed[col].astype(str).unique()\n    \n    # Áp dụng các cấp độ này cho tất cả các tập\n    X_train_processed[col] = pd.Categorical(X_train_processed[col].astype(str), categories=all_categories)\n    X_val_processed[col] = pd.Categorical(X_val_processed[col].astype(str), categories=all_categories)\n    X_test_processed[col] = pd.Categorical(X_test_processed[col].astype(str), categories=all_categories)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:37.471189Z","iopub.execute_input":"2025-06-18T11:53:37.471441Z","iopub.status.idle":"2025-06-18T11:53:44.836041Z","shell.execute_reply.started":"2025-06-18T11:53:37.471427Z","shell.execute_reply":"2025-06-18T11:53:44.835451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(X_val_processed.info())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:44.836688Z","iopub.execute_input":"2025-06-18T11:53:44.836872Z","iopub.status.idle":"2025-06-18T11:53:45.294202Z","shell.execute_reply.started":"2025-06-18T11:53:44.836854Z","shell.execute_reply":"2025-06-18T11:53:45.293509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(X_test_processed.info())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:45.294922Z","iopub.execute_input":"2025-06-18T11:53:45.295437Z","iopub.status.idle":"2025-06-18T11:53:45.744699Z","shell.execute_reply.started":"2025-06-18T11:53:45.295412Z","shell.execute_reply":"2025-06-18T11:53:45.744006Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Train model CatBoost**","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nfrom lightgbm import LGBMClassifier\nfrom sklearn.metrics import f1_score\n\nmodel = LGBMClassifier(\n    objective='binary',\n    metric='logloss',\n    random_state=42,\n    n_jobs=-1,\n    categorical_feature=categorical_features_for_lgbm\n)\n\nmodel.fit(X_train_processed, y_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-18T11:53:45.745344Z","iopub.execute_input":"2025-06-18T11:53:45.745500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predict & Evaluate trên tập validation (X_val_processed)\ny_val_pred = model.predict(X_val_processed)\nf1_val = f1_score(y_val_true, y_val_pred, average='macro')\nprint(f\"Macro F1-score on validation set: {f1_val:.4f}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dự đoán trên tập test cuối cùng (X_test_processed)\ny_test_pred = model.predict(X_test_processed)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.DataFrame({\n    'ID': test_df['ID'],\n    'label': y_test_pred\n})\n\n# Lưu thành file CSV\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from collections import Counter\n\nprint(Counter(y_test_pred))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}