{"metadata":{"kernelspec":{"display_name":"base","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.7"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":103436,"databundleVersionId":12476472,"sourceType":"competition"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"a3151d32","cell_type":"markdown","source":"# Import thư viện","metadata":{}},{"id":"dbd02f08","cell_type":"code","source":"import numpy as np\nimport pandas as pd\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.preprocessing import StandardScaler, OrdinalEncoder\nfrom sklearn.metrics import classification_report,f1_score\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.preprocessing import FunctionTransformer\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.model_selection import train_test_split","metadata":{},"outputs":[],"execution_count":null},{"id":"b1ae8716","cell_type":"markdown","source":"# Huấn luyện tìm model tốt nhất","metadata":{}},{"id":"99c7a386","cell_type":"markdown","source":"## Đọc và gộp file","metadata":{}},{"id":"b6d8a291","cell_type":"code","source":"data_delay_79=pd.read_csv('D:/KHOAI/Nam2/HK2/DS108/Bai Tap/Delivery_project/delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv')\ndata_not_delay_79=pd.read_csv('D:/KHOAI/Nam2/HK2/DS108/Bai Tap/Delivery_project/not_delay_7_9_CONDITION_PRODUCT_SUPPLIER.csv')\ndata_delay_46=pd.read_csv('D:/KHOAI/Nam2/HK2/DS108/Bai Tap/Delivery_project/delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv')\ndata_not_delay_46=pd.read_csv('D:/KHOAI/Nam2/HK2/DS108/Bai Tap/Delivery_project/not_delay_4_6_CONDITION_PRODUCT_SUPPLIER.csv')\ndata_46=pd.concat([data_delay_46, data_not_delay_46], axis=0, ignore_index=True)\ndata_79=pd.concat([data_delay_79, data_not_delay_79], axis=0, ignore_index=True)","metadata":{},"outputs":[],"execution_count":null},{"id":"0d0af6eb","cell_type":"code","source":"data_46['OTHER AREA SHIP DIV'] = data_46['OTHER AREA SHIP DIV'].replace(r'^\\s*$', np.nan, regex=True)","metadata":{},"outputs":[],"execution_count":null},{"id":"1d4e4ca2","cell_type":"code","source":"data_79['OTHER AREA SHIP DIV'] = data_79['OTHER AREA SHIP DIV'].astype('Int64')","metadata":{},"outputs":[],"execution_count":null},{"id":"376ca79a","cell_type":"code","source":"data= pd.concat([data_79, data_46[data_79.columns]],axis=0, ignore_index=True)","metadata":{},"outputs":[],"execution_count":null},{"id":"14aeda37","cell_type":"code","source":"data.info()","metadata":{},"outputs":[],"execution_count":null},{"id":"1e06bc14","cell_type":"markdown","source":"## Tiền xử lý","metadata":{}},{"id":"7eb10898","cell_type":"markdown","source":"### Feature selection ","metadata":{}},{"id":"a69544e4","cell_type":"code","source":"useful_columns=['Order date','CLASSIFY_CD','CUST_CD','BRAND_CD','SUPPLIER_CD',\n                'Stock class', 'SO QTY','OTHER AREA SHIP DIV','SUPPLIER INV AMOUNT',\n                'PACKING RANK' ,'SPECIAL DIV','LOGICAL PLANT','VSD','DIRECT SHIP FLG','DELI_DIV',\n                'Ship Mode', 'WEIGHT PER PIECE','SUPPLIER_DIV','SO_DAY_OF_MONTH', 'SO_DAY_OF_WEEK',\n                'SO_TIME','label']\n\nremove_columns = [x for x in data.columns if x not in useful_columns]\ndata= data.drop(remove_columns, axis=1)","metadata":{},"outputs":[],"execution_count":null},{"id":"9f1a3e25","cell_type":"markdown","source":"### Chuyển kiểu dữ liệu","metadata":{}},{"id":"f5b600a7","cell_type":"code","source":"# Định nghĩa kiểu cho các cột\nkind_dict = {\n    'Order date': 'datetime64[ns]',\n    'CLASSIFY_CD' : 'category',\n    'CUST_CD' :'category',\n    'BRAND_CD': 'category',\n    'SUPPLIER_CD': 'category',\n    'Stock class':'category', \n    'SO QTY' : 'int32',\n    'OTHER AREA SHIP DIV' : 'category',\n    'SUPPLIER INV AMOUNT' : 'float32',\n    'PACKING RANK' : 'category',\n    'SPECIAL DIV': 'category',\n    'LOGICAL PLANT': 'category',\n    'VSD' : 'datetime64[ns]',\n    'DIRECT SHIP FLG' : 'category',\n    'DELI_DIV' : 'category',\n    'Ship Mode' : 'category',\n    'WEIGHT PER PIECE'  : 'float32',\n    'SUPPLIER_DIV'  : 'category',\n    'SO_DAY_OF_MONTH' : 'category',\n    'SO_DAY_OF_WEEK' : 'category',\n    'SO_TIME' : 'int32',\n    'label' : 'category'\n\n}\ndata = data.astype(kind_dict,errors='ignore')","metadata":{},"outputs":[],"execution_count":null},{"id":"c535d760","cell_type":"code","source":"data.info()","metadata":{},"outputs":[],"execution_count":null},{"id":"086249f2","cell_type":"markdown","source":"### Bỏ Nan","metadata":{}},{"id":"328c60bd","cell_type":"code","source":"data=data.dropna(subset=['Ship Mode','SUPPLIER_DIV'])","metadata":{},"outputs":[],"execution_count":null},{"id":"1eadd4b3","cell_type":"markdown","source":"### Future extraction","metadata":{}},{"id":"eff86657","cell_type":"code","source":"def feature_extraction(data):   \n    data['Wait_days']=data['VSD']-data['Order date']\n    data['Wait_days']=data['Wait_days'].dt.days\n    data['Weight']=data['WEIGHT PER PIECE']*data['SO QTY']\n    data['Moth_Order']=data['Order date'].dt.month\n    data['Day_VSD']=data['VSD'].dt.day\n    data['Month_VSD']=data['VSD'].dt.month\n    data=data.drop(['Order date','VSD'],axis=1) \n    return data\ndata=feature_extraction(data)","metadata":{},"outputs":[],"execution_count":null},{"id":"afc0bf56","cell_type":"markdown","source":"### Encoding","metadata":{}},{"id":"42e60869","cell_type":"code","source":"from sklearn import set_config\nset_config(transform_output=\"pandas\")\nordinal_col = ['PACKING RANK','SUPPLIER_DIV']\nDefaul_imputer = SimpleImputer(strategy='constant', fill_value=0)\nordi_encode=OrdinalEncoder()\ntransformer=ColumnTransformer(\n    transformers=[\n    ('Defaul_imputer', Defaul_imputer,['OTHER AREA SHIP DIV']),\n    ('Ordinal encoder', ordi_encode, ordinal_col)\n    ],\n    remainder='passthrough'\n)\ndata = transformer.fit_transform(data)","metadata":{},"outputs":[],"execution_count":null},{"id":"94939cff","cell_type":"code","source":"data=pd.DataFrame(data,columns=transformer.get_feature_names_out())","metadata":{},"outputs":[],"execution_count":null},{"id":"2f33b430","cell_type":"code","source":"passthrough_cols = [col for col in data.columns if col.startswith('remainder__')]\nnew_cols = [col.replace('remainder__', '') if col.startswith('remainder__') else col for col in data.columns]\ndata.columns = new_cols","metadata":{},"outputs":[],"execution_count":null},{"id":"3b778780","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nle=LabelEncoder()\ndata['BRAND_CD']=le.fit_transform(data['BRAND_CD'])","metadata":{},"outputs":[],"execution_count":null},{"id":"25e7cc7d","cell_type":"markdown","source":"### xử lý imbalance","metadata":{}},{"id":"0af4742c","cell_type":"code","source":"from imblearn.under_sampling import RandomUnderSampler\n\ndef apply_random_undersampling(df, label_col='label', ratio=20, random_state=42):\n    \"\"\"\n    Thực hiện undersampling dữ liệu mất cân bằng theo nhãn, với tỷ lệ tùy chỉnh.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame đầu vào.\n    - label_col (str): Tên cột nhãn (target).\n    - ratio (int): Tỷ lệ majority:minority mong muốn (VD: 20 nghĩa là 1:20).\n    - random_state (int): Seed để tái lập kết quả.\n\n    Returns:\n    - pd.DataFrame: DataFrame sau khi được undersample.\n    \"\"\"\n    X = df.drop(columns=[label_col])\n    y = df[label_col]\n\n    # Đếm số lượng mẫu theo nhãn\n    from collections import Counter\n    counts = Counter(y)\n    \n    # Xác định lớp thiểu số và đa số\n    minority_class = min(counts, key=counts.get)\n    majority_class = max(counts, key=counts.get)\n    minority_count = counts[minority_class]\n\n    # Tính số lượng mẫu cần giữ lại cho lớp đa số\n    majority_target_count = ratio * minority_count\n\n    # Tạo dictionary cho sampling_strategy\n    sampling_strategy = {\n        minority_class: minority_count,\n        majority_class: majority_target_count\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","metadata":{},"outputs":[],"execution_count":null},{"id":"c4458077","cell_type":"code","source":"data_undersample=apply_random_undersampling(data, label_col='label', ratio=20)","metadata":{},"outputs":[],"execution_count":null},{"id":"24c4e9b7","cell_type":"code","source":"data_undersample['Defaul_imputer__OTHER AREA SHIP DIV']=data_undersample['Defaul_imputer__OTHER AREA SHIP DIV'].astype('category')","metadata":{},"outputs":[],"execution_count":null},{"id":"dd63739e","cell_type":"code","source":"category_columns = data_undersample.select_dtypes(include=['category']).columns.tolist()\ncategory_columns.remove('label')","metadata":{},"outputs":[],"execution_count":null},{"id":"5a4f376f","cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport optuna\nimport json\nimport lightgbm as lgb\nfrom sklearn.model_selection import train_test_split, StratifiedKFold\nfrom sklearn.metrics import f1_score, precision_score, recall_score\n\n# ----------------------------\n# STEP 1: Chia dữ liệu\n# ----------------------------\n\nX = data_undersample.drop(columns=['label'])\ny = data_undersample['label']\n\nX_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)\nX_train, X_dev, y_train, y_dev = train_test_split(X_temp, y_temp, test_size=0.25, stratify=y_temp, random_state=42)","metadata":{},"outputs":[],"execution_count":null},{"id":"fe8450ae","cell_type":"code","source":"def optimize_and_train_lightgbm(X_train, y_train, X_dev, y_dev, n_trials=100):\n    def objective(trial):\n        params = {\n            'n_estimators': trial.suggest_int('n_estimators', 200, 1500),\n            'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.3, log=True),\n            'max_depth': trial.suggest_int('max_depth', 3, 10),\n            'num_leaves': trial.suggest_int('num_leaves', 31, 255),\n            'min_child_samples': trial.suggest_int('min_child_samples', 10, 100),\n            'subsample': trial.suggest_float('subsample', 0.5, 1.0),\n            'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0),\n            'reg_lambda': trial.suggest_float('reg_lambda', 0, 10),\n            'objective': 'binary',\n            'metric': 'binary_logloss',\n            'random_state': 42,\n            'verbose': -1,\n            'is_unbalance': True\n        }\n\n        f1_scores = []\n        cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=1002)\n        for train_idx, valid_idx in cv.split(X_train, y_train):\n            X_train_fold, X_valid_fold = X_train.iloc[train_idx], X_train.iloc[valid_idx]\n            y_train_fold, y_valid_fold = y_train.iloc[train_idx], y_train.iloc[valid_idx]\n\n            model = lgb.LGBMClassifier(**params)\n            model.fit(X_train_fold, y_train_fold)\n            y_pred = model.predict(X_valid_fold)\n            f1 = f1_score(y_valid_fold, y_pred, average='macro')\n            f1_scores.append(f1)\n\n        return np.mean(f1_scores)\n\n    # Tạo và chạy tối ưu hóa\n    study = optuna.create_study(direction='maximize')\n    study.optimize(objective, n_trials=n_trials)\n\n    # Lấy tham số tốt nhất\n    best_params = study.best_params\n    best_params.update({\n        'objective': 'binary',\n        'metric': 'binary_logloss',\n        'random_state': 42,\n        'verbose': -1,\n        'is_unbalance': True\n    })\n\n    # Huấn luyện mô hình tốt nhất trên toàn bộ tập train\n    best_model = lgb.LGBMClassifier(**best_params)\n    best_model.fit(X_train, y_train)\n\n    # Đánh giá trên tập dev\n    y_pred_dev = best_model.predict(X_dev)\n    f1_dev = f1_score(y_dev, y_pred_dev, average='macro')\n    print(f\"Best params: {best_params}\")\n    print(f\"F1-score on dev set: {f1_dev:.4f}\")\n\n    return best_model, best_params","metadata":{},"outputs":[],"execution_count":null},{"id":"b0984cb8","cell_type":"code","source":"best_model,best_params=optimize_and_train_lightgbm(X_train,y_train,X_dev,y_dev,n_trials=150)","metadata":{},"outputs":[],"execution_count":null},{"id":"49ae5035","cell_type":"code","source":"# Dự đoán\npreds = best_model.predict(X_test)\n\n# Đánh giá\nf1 = f1_score(y_test, preds)\nprecision = precision_score(y_test, preds)\nrecall = recall_score(y_test, preds)\n\nprint(\"\\nFinal Evaluation on Test Set:\")\nprint(f\"F1 Score: {f1:.4f}\") \nprint(f\"Precision: {precision:.4f}\")\nprint(f\"Recall: {recall:.4f}\")","metadata":{},"outputs":[],"execution_count":null},{"id":"270dd459","cell_type":"code","source":"from sklearn.metrics import classification_report\nprint(classification_report(y_test,preds))","metadata":{},"outputs":[],"execution_count":null},{"id":"64ef3b74","cell_type":"markdown","source":"# Dự đoán trên tập pilot 10","metadata":{}},{"id":"d2d6c86c","cell_type":"code","source":"df_test=pd.read_csv('D:/KHOAI/Nam2/HK2/DS108/Bai Tap/Delivery_project/PILOT_10.csv')","metadata":{},"outputs":[],"execution_count":null},{"id":"3a68864d","cell_type":"code","source":"ID_col=df_test['ID']\ndf_test=df_test.drop('ID',axis=1)","metadata":{},"outputs":[],"execution_count":null},{"id":"17d2b536","cell_type":"code","source":"df_test['SO_DAY_OF_WEEK']=df_test['SO_DAY_OF_WEEK'].astype('int')","metadata":{},"outputs":[],"execution_count":null},{"id":"cd7bfad2","cell_type":"markdown","source":"## Tiền xử lý giống tập huấn luyện","metadata":{}},{"id":"37fe77a5","cell_type":"code","source":"useful_columns=['Order date','CLASSIFY_CD','CUST_CD','BRAND_CD','SUPPLIER_CD',\n                'Stock class', 'SO QTY','OTHER AREA SHIP DIV','SUPPLIER INV AMOUNT',\n                'PACKING RANK' ,'SPECIAL DIV','LOGICAL PLANT','VSD','DIRECT SHIP FLG','DELI_DIV',\n                'Ship Mode', 'WEIGHT PER PIECE','SUPPLIER_DIV','SO_DAY_OF_MONTH', 'SO_DAY_OF_WEEK',\n                'SO_TIME']\n\nremove_columns = [x for x in df_test.columns if x not in useful_columns]\ndf_test= df_test.drop(remove_columns, axis=1)","metadata":{},"outputs":[],"execution_count":null},{"id":"6c9a8be7","cell_type":"code","source":"# Định nghĩa kiểu cho các cột\nkind_dict = {\n    'Order date': 'datetime64[ns]',\n    'CLASSIFY_CD' : 'category',\n    'CUST_CD' :'category',\n    'BRAND_CD': 'category',\n    'SUPPLIER_CD': 'category',\n    'Stock class':'category', \n    'SO QTY' : 'int32',\n    'OTHER AREA SHIP DIV' : 'category',\n    'SUPPLIER INV AMOUNT' : 'float32',\n    'PACKING RANK' : 'category',\n    'SPECIAL DIV': 'category',\n    'LOGICAL PLANT': 'category',\n    'VSD' : 'datetime64[ns]',\n    'DIRECT SHIP FLG' : 'category',\n    'DELI_DIV' : 'category',\n    'Ship Mode' : 'category',\n    'WEIGHT PER PIECE'  : 'float32',\n    'SUPPLIER_DIV'  : 'category',\n    'SO_DAY_OF_MONTH' : 'category',\n    'SO_DAY_OF_WEEK' : 'category',\n    'SO_TIME' : 'int32'\n\n}\ndf_test = df_test.astype(kind_dict,errors='ignore')","metadata":{},"outputs":[],"execution_count":null},{"id":"334e724d","cell_type":"code","source":"df_test['OTHER AREA SHIP DIV']=df_test['OTHER AREA SHIP DIV'].astype('Int64')","metadata":{},"outputs":[],"execution_count":null},{"id":"b4376c9a","cell_type":"code","source":"df_test=feature_extraction(df_test)","metadata":{},"outputs":[],"execution_count":null},{"id":"39ae491e","cell_type":"code","source":"Mode_imputer= SimpleImputer(strategy='most_frequent')\ndf_test[['Ship Mode','SUPPLIER_DIV']]=Mode_imputer.fit_transform(df_test[['Ship Mode','SUPPLIER_DIV']])\n# Ép kiểu lại về category\ndf_test['Ship Mode'] = df_test['Ship Mode'].astype('category')\ndf_test['SUPPLIER_DIV'] = df_test['SUPPLIER_DIV'].astype('category')","metadata":{},"outputs":[],"execution_count":null},{"id":"abb8ee38","cell_type":"code","source":"from sklearn import set_config\nset_config(transform_output=\"pandas\")\nordinal_col = ['PACKING RANK','SUPPLIER_DIV']\nDefaul_imputer = SimpleImputer(strategy='constant', fill_value=0)\nordi_encode=OrdinalEncoder()\ntransformer_pilot10=ColumnTransformer(\n    transformers=[\n    ('Defaul_imputer', Defaul_imputer,['OTHER AREA SHIP DIV']),\n    ('Ordinal encoder', ordi_encode, ordinal_col)\n    ],\n    remainder='passthrough'\n)\n","metadata":{},"outputs":[],"execution_count":null},{"id":"ad631750","cell_type":"code","source":"df_test = transformer_pilot10.fit_transform(df_test)","metadata":{},"outputs":[],"execution_count":null},{"id":"76782d6c","cell_type":"code","source":"df_test=pd.DataFrame(df_test,columns=transformer_pilot10.get_feature_names_out())","metadata":{},"outputs":[],"execution_count":null},{"id":"3414e35c","cell_type":"code","source":"passthrough_cols = [col for col in df_test.columns if col.startswith('remainder__')]\nnew_cols = [col.replace('remainder__', '') if col.startswith('remainder__') else col for col in df_test.columns]\ndf_test.columns = new_cols","metadata":{},"outputs":[],"execution_count":null},{"id":"291ae088","cell_type":"code","source":"df_test['BRAND_CD']=le.fit_transform(df_test['BRAND_CD'])","metadata":{},"outputs":[],"execution_count":null},{"id":"7eec82a1","cell_type":"code","source":"df_test['Defaul_imputer__OTHER AREA SHIP DIV']=df_test['Defaul_imputer__OTHER AREA SHIP DIV'].astype('int')\ndf_test['Defaul_imputer__OTHER AREA SHIP DIV']=df_test['Defaul_imputer__OTHER AREA SHIP DIV'].astype('category')","metadata":{},"outputs":[],"execution_count":null},{"id":"cd38fdab","cell_type":"markdown","source":"## Dự đoán","metadata":{}},{"id":"cb92f717","cell_type":"code","source":"y_pred=best_model.predict(df_test)","metadata":{},"outputs":[],"execution_count":null},{"id":"418a7f1d","cell_type":"code","source":"result_df = pd.DataFrame({\n    'ID': ID_col,\n    'label': y_pred\n})\nresult_df.to_csv('submission7.csv', index=False)","metadata":{},"outputs":[],"execution_count":null}]}