{"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":"gpu","dataSources":[{"sourceId":81933,"databundleVersionId":9643020,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# %% Cell 0 — 安裝所需套件\n!pip install polars tqdm keras torch colorama lightgbm xgboost catboost\n!pip install mord","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:19:45.945138Z","iopub.execute_input":"2025-05-28T01:19:45.945399Z","iopub.status.idle":"2025-05-28T01:21:12.067303Z","shell.execute_reply.started":"2025-05-28T01:19:45.945380Z","shell.execute_reply":"2025-05-28T01:21:12.066503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport re\nfrom sklearn.base import clone\nfrom sklearn.metrics import cohen_kappa_score\nfrom sklearn.model_selection import StratifiedKFold\nfrom scipy.optimize import minimize\nfrom concurrent.futures import ThreadPoolExecutor\nfrom tqdm import tqdm\nimport polars as pl\nimport polars.selectors as cs\nimport matplotlib.pyplot as plt\nfrom matplotlib.ticker import MaxNLocator, FormatStrFormatter, PercentFormatter\nimport seaborn as sns\n\nfrom sklearn.preprocessing import StandardScaler\nimport matplotlib.pyplot as plt\nfrom keras.models import Model\nfrom keras.layers import Input, Dense\nfrom keras.optimizers import Adam\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom colorama import Fore, Style\nfrom IPython.display import clear_output\nimport warnings\nfrom lightgbm import LGBMRegressor\nfrom xgboost import XGBRegressor\nfrom catboost import CatBoostRegressor\nfrom sklearn.ensemble import VotingRegressor, RandomForestRegressor, GradientBoostingRegressor\nfrom sklearn.impute import SimpleImputer, KNNImputer\nfrom sklearn.pipeline import Pipeline\nwarnings.filterwarnings('ignore')\npd.options.display.max_columns = None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:12.069189Z","iopub.execute_input":"2025-05-28T01:21:12.069474Z","iopub.status.idle":"2025-05-28T01:21:32.110577Z","shell.execute_reply.started":"2025-05-28T01:21:12.069448Z","shell.execute_reply":"2025-05-28T01:21:32.110010Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 視覺化資料","metadata":{}},{"cell_type":"code","source":"target_labels = ['None', 'Mild', 'Moderate', 'Severe']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:32.111368Z","iopub.execute_input":"2025-05-28T01:21:32.112051Z","iopub.status.idle":"2025-05-28T01:21:32.115925Z","shell.execute_reply.started":"2025-05-28T01:21:32.112027Z","shell.execute_reply":"2025-05-28T01:21:32.115228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"season_dtype = pl.Enum(['Spring', 'Summer', 'Fall', 'Winter'])\n\ntrain = (\n    pl.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/train.csv')\n    .with_columns(pl.col('^.*Season$').cast(season_dtype))\n)\n\ntest = (\n    pl.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/test.csv')\n    .with_columns(pl.col('^.*Season$').cast(season_dtype))\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:32.117598Z","iopub.execute_input":"2025-05-28T01:21:32.117831Z","iopub.status.idle":"2025-05-28T01:21:33.018892Z","shell.execute_reply.started":"2025-05-28T01:21:32.117810Z","shell.execute_reply":"2025-05-28T01:21:33.018267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"supervised_usable = (\n    train\n    .filter(pl.col('sii').is_not_null())\n)\n\nmissing_count = (\n    supervised_usable\n    .null_count()\n    .transpose(include_header=True,\n               header_name='feature',\n               column_names=['null_count'])\n    .sort('null_count', descending=True)\n    .with_columns((pl.col('null_count') / len(supervised_usable)).alias('null_ratio'))\n)\nplt.figure(figsize=(6, 15))\nplt.title(f'Missing values over the {len(supervised_usable)} samples which have a target')\nplt.barh(np.arange(len(missing_count)), missing_count.get_column('null_ratio'), color='coral', label='missing')\nplt.barh(np.arange(len(missing_count)), \n         1 - missing_count.get_column('null_ratio'),\n         left=missing_count.get_column('null_ratio'),\n         color='darkseagreen', label='available')\nplt.yticks(np.arange(len(missing_count)), missing_count.get_column('feature'))\nplt.gca().xaxis.set_major_formatter(PercentFormatter(xmax=1, decimals=0))\nplt.xlim(0, 1)\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:33.019586Z","iopub.execute_input":"2025-05-28T01:21:33.019793Z","iopub.status.idle":"2025-05-28T01:21:33.911284Z","shell.execute_reply.started":"2025-05-28T01:21:33.019754Z","shell.execute_reply":"2025-05-28T01:21:33.910376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train.select(pl.col('PCIAT-PCIAT_Total').is_null() == pl.col('sii').is_null()).to_series().mean())\n\n(train\n .select(pl.col('PCIAT-PCIAT_Total'))\n .group_by(train.get_column('sii'))\n .agg(pl.col('PCIAT-PCIAT_Total').min().alias('PCIAT-PCIAT_Total min'),\n      pl.col('PCIAT-PCIAT_Total').max().alias('PCIAT-PCIAT_Total max'),\n      pl.col('PCIAT-PCIAT_Total').len().alias('count'))\n .sort('sii')\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:33.912126Z","iopub.execute_input":"2025-05-28T01:21:33.912345Z","iopub.status.idle":"2025-05-28T01:21:33.936991Z","shell.execute_reply.started":"2025-05-28T01:21:33.912328Z","shell.execute_reply":"2025-05-28T01:21:33.936409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Columns missing in test:')\nprint([f for f in train.columns if f not in test.columns])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:33.937694Z","iopub.execute_input":"2025-05-28T01:21:33.937922Z","iopub.status.idle":"2025-05-28T01:21:33.942566Z","shell.execute_reply.started":"2025-05-28T01:21:33.937905Z","shell.execute_reply":"2025-05-28T01:21:33.941637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"vc = train.get_column('Basic_Demos-Enroll_Season').value_counts()\nplt.pie(vc.get_column('count'), labels=vc.get_column('Basic_Demos-Enroll_Season'))\nplt.title('Season of enrollment')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:33.943346Z","iopub.execute_input":"2025-05-28T01:21:33.943587Z","iopub.status.idle":"2025-05-28T01:21:34.038381Z","shell.execute_reply.started":"2025-05-28T01:21:33.943552Z","shell.execute_reply":"2025-05-28T01:21:34.037646Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"vc = train.get_column('Basic_Demos-Sex').value_counts()\nplt.pie(vc.get_column('count'), labels=['boys', 'girls'])\nplt.title('Sex of participant')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.039124Z","iopub.execute_input":"2025-05-28T01:21:34.039310Z","iopub.status.idle":"2025-05-28T01:21:34.104321Z","shell.execute_reply.started":"2025-05-28T01:21:34.039296Z","shell.execute_reply":"2025-05-28T01:21:34.103806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"_, axs = plt.subplots(2, 1, sharex=True)\nfor sex in range(2):\n    ax = axs.ravel()[sex]\n    vc = train.filter(pl.col('Basic_Demos-Sex') == sex).get_column('Basic_Demos-Age').value_counts()\n    ax.bar(vc.get_column('Basic_Demos-Age'),\n           vc.get_column('count'),\n           color=['lightblue', 'coral'][sex],\n           label=['boys', 'girls'][sex])\n    ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n    ax.set_ylabel('count')\n    ax.legend()\nplt.suptitle('Age distribution')\naxs.ravel()[1].set_xlabel('years')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.106524Z","iopub.execute_input":"2025-05-28T01:21:34.106718Z","iopub.status.idle":"2025-05-28T01:21:34.387061Z","shell.execute_reply.started":"2025-05-28T01:21:34.106703Z","shell.execute_reply":"2025-05-28T01:21:34.386336Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"_, axs = plt.subplots(2, 1, sharex=True, sharey=True)\nfor sex in range(2):\n    ax = axs.ravel()[sex]\n    vc = train.filter(pl.col('Basic_Demos-Sex') == sex).get_column('sii').value_counts()\n    ax.bar(vc.get_column('sii'),\n           vc.get_column('count') / vc.get_column('count').sum(),\n           color=['lightblue', 'coral'][sex],\n           label=['boys', 'girls'][sex])\n    ax.set_xticks(np.arange(4), target_labels)\n    ax.yaxis.set_major_formatter(PercentFormatter(xmax=1, decimals=0))\n    ax.set_ylabel('count')\n    ax.legend()\nplt.suptitle('Target distribution')\naxs.ravel()[1].set_xlabel('Severity Impairment Index (sii)')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.387736Z","iopub.execute_input":"2025-05-28T01:21:34.388915Z","iopub.status.idle":"2025-05-28T01:21:34.585745Z","shell.execute_reply.started":"2025-05-28T01:21:34.388894Z","shell.execute_reply":"2025-05-28T01:21:34.585147Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(14, 12))\ncorr_matrix = supervised_usable.select([\n    'PCIAT-PCIAT_Total', 'Basic_Demos-Age', 'Basic_Demos-Sex', 'Physical-BMI', \n    'Physical-Height', 'Physical-Weight', 'Physical-Waist_Circumference',\n    'Physical-Diastolic_BP', 'Physical-Systolic_BP', 'Physical-HeartRate',\n    'PreInt_EduHx-computerinternet_hoursday', 'SDS-SDS_Total_T', 'PAQ_A-PAQ_A_Total',\n    'PAQ_C-PAQ_C_Total', 'Fitness_Endurance-Max_Stage', 'Fitness_Endurance-Time_Mins','Fitness_Endurance-Time_Sec',\n    'FGC-FGC_CU', 'FGC-FGC_GSND','FGC-FGC_GSD','FGC-FGC_PU','FGC-FGC_SRL','FGC-FGC_SRR','FGC-FGC_TL','BIA-BIA_Activity_Level_num', \n    'BIA-BIA_BMC', 'BIA-BIA_BMI', 'BIA-BIA_BMR', 'BIA-BIA_DEE', 'BIA-BIA_ECW', 'BIA-BIA_FFM',\n    'BIA-BIA_FFMI','BIA-BIA_FMI', 'BIA-BIA_Fat','BIA-BIA_Frame_num','BIA-BIA_ICW','BIA-BIA_LDM','BIA-BIA_LST',\n    'BIA-BIA_SMM','BIA-BIA_TBW'\n    # Add other relevant columns\n]).to_pandas().corr()\n\nsii_corr = corr_matrix['PCIAT-PCIAT_Total'].drop('PCIAT-PCIAT_Total')\nfiltered_corr = sii_corr[(sii_corr > 0.1) | (sii_corr < -0.1)]\n\nprint(filtered_corr)\n\nplt.figure(figsize=(8, 6))\nfiltered_corr.sort_values().plot(kind='barh', color='coral')\nplt.title('Features with Correlation > 0.1 or < -0.1 with PCIAT-PCIAT_Total')\nplt.xlabel('Correlation coefficient')\nplt.ylabel('Features')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.586528Z","iopub.execute_input":"2025-05-28T01:21:34.586727Z","iopub.status.idle":"2025-05-28T01:21:34.898856Z","shell.execute_reply.started":"2025-05-28T01:21:34.586711Z","shell.execute_reply":"2025-05-28T01:21:34.898145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"actigraphy = pl.read_parquet('/kaggle/input/child-mind-institute-problematic-internet-use/series_train.parquet/id=0417c91e/part-0.parquet')\nactigraphy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.899622Z","iopub.execute_input":"2025-05-28T01:21:34.899877Z","iopub.status.idle":"2025-05-28T01:21:34.993053Z","shell.execute_reply.started":"2025-05-28T01:21:34.899861Z","shell.execute_reply":"2025-05-28T01:21:34.992369Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def analyze_actigraphy(id, only_one_week=False, small=False):\n    actigraphy = pl.read_parquet(f'/kaggle/input/child-mind-institute-problematic-internet-use/series_train.parquet/id={id}/part-0.parquet')\n    day = actigraphy.get_column('relative_date_PCIAT') + actigraphy.get_column('time_of_day') / 86400e9\n    sample = train.filter(pl.col('id') == id)\n    age = sample.get_column('Basic_Demos-Age').item()\n    sex = ['boy', 'girl'][sample.get_column('Basic_Demos-Sex').item()]\n    actigraphy = (\n        actigraphy\n        .with_columns(\n            (day.diff() * 86400).alias('diff_seconds'),\n            (np.sqrt(np.square(pl.col('X')) + np.square(pl.col('Y')) + np.square(pl.col('Z'))).alias('norm'))\n        )\n    )\n\n    if only_one_week:\n        start = np.ceil(day.min())\n        mask = (start <= day.to_numpy()) & (day.to_numpy() <= start + 7*3)\n        mask &= ~ actigraphy.get_column('non-wear_flag').cast(bool).to_numpy()\n    else:\n        mask = np.full(len(day), True)\n        \n    if small:\n        timelines = [\n            ('enmo', 'forestgreen'),\n            ('light', 'orange'),\n        ]\n    else:\n        timelines = [\n            ('X', 'm'),\n            ('Y', 'm'),\n            ('Z', 'm'),\n#             ('norm', 'c'),\n            ('enmo', 'forestgreen'),\n            ('anglez', 'lightblue'),\n            ('light', 'orange'),\n            ('non-wear_flag', 'chocolate')\n    #         ('diff_seconds', 'k'),\n        ]\n        \n    _, axs = plt.subplots(len(timelines), 1, sharex=True, figsize=(12, len(timelines) * 1.1 + 0.5))\n    for ax, (feature, color) in zip(axs, timelines):\n        ax.set_facecolor('#eeeeee')\n        ax.scatter(day.to_numpy()[mask],\n                   actigraphy.get_column(feature).to_numpy()[mask],\n                   color=color, label=feature, s=1)\n        ax.legend(loc='upper left', facecolor='#eeeeee')\n        if feature == 'diff_seconds':\n            ax.set_ylim(-0.5, 20.5)\n    axs[-1].set_xlabel('day')\n    axs[-1].xaxis.set_major_locator(MaxNLocator(integer=True))\n    plt.tight_layout()\n    axs[0].set_title(f'id={id}, {sex}, age={age}')\n    plt.show()\n\nanalyze_actigraphy('0417c91e', only_one_week=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:34.993741Z","iopub.execute_input":"2025-05-28T01:21:34.993983Z","iopub.status.idle":"2025-05-28T01:21:36.939652Z","shell.execute_reply.started":"2025-05-28T01:21:34.993968Z","shell.execute_reply":"2025-05-28T01:21:36.938741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 資料預處理","metadata":{}},{"cell_type":"code","source":"def process_file(filename, dirname):\n    df = pd.read_parquet(os.path.join(dirname, filename, 'part-0.parquet'))\n    df.drop('step', axis=1, inplace=True)\n    \n    # 提取所需的統計特徵\n    mean_values = df.mean().values\n    std_values = df.std().values\n    min_values = df.min().values\n    max_values = df.max().values\n    \n    # 計算活動比例\n    active_ratio = (df[['X', 'Y', 'Z']] != 0).mean().values\n    still_ratio = (df['enmo'] < 0.01).mean()\n    \n    # 合併特徵\n    features = np.concatenate([mean_values, std_values, min_values, max_values, active_ratio, [still_ratio]])\n    \n    # 創建特徵名稱\n    columns = list(df.columns)\n    feature_names = (\n        [f\"{col}_mean\" for col in columns] +\n        [f\"{col}_std\" for col in columns] +\n        [f\"{col}_min\" for col in columns] +\n        [f\"{col}_max\" for col in columns] +\n        [f\"{col}_active_ratio\" for col in ['X', 'Y', 'Z']] +\n        ['enmo_still_ratio']\n    )\n    \n    return features, feature_names, filename.split('=')[1]\n\n\n\ndef load_time_series(dirname) -> pd.DataFrame:\n    ids = os.listdir(dirname)\n    \n    with ThreadPoolExecutor() as executor:\n        # 修改：接收返回的特徵值、特徵名稱和 ID\n        results = list(tqdm(executor.map(lambda fname: process_file(fname, dirname), ids), total=len(ids)))\n    \n    # 解壓結果\n    stats_list, feature_names_list, indexes = zip(*results)\n    \n    # 獲取統一的特徵名稱（假設所有檔案的特徵名稱相同）\n    feature_names = feature_names_list[0]\n    \n    # 構建 DataFrame\n    df = pd.DataFrame(stats_list, columns=feature_names)\n    df['id'] = indexes\n    \n    return df\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:36.940505Z","iopub.execute_input":"2025-05-28T01:21:36.940796Z","iopub.status.idle":"2025-05-28T01:21:36.948928Z","shell.execute_reply.started":"2025-05-28T01:21:36.940756Z","shell.execute_reply":"2025-05-28T01:21:36.948123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/train.csv')\ntest = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/test.csv')\nsample = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/sample_submission.csv')\n\ntrain_ts = load_time_series(\"/kaggle/input/child-mind-institute-problematic-internet-use/series_train.parquet\")\ntest_ts = load_time_series(\"/kaggle/input/child-mind-institute-problematic-internet-use/series_test.parquet\")\n\ndf_train = train_ts.drop('id', axis=1)\ndf_test = test_ts.drop('id', axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:21:36.949645Z","iopub.execute_input":"2025-05-28T01:21:36.949885Z","iopub.status.idle":"2025-05-28T01:22:09.589155Z","shell.execute_reply.started":"2025-05-28T01:21:36.949868Z","shell.execute_reply":"2025-05-28T01:22:09.588464Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['sii'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.589989Z","iopub.execute_input":"2025-05-28T01:22:09.590492Z","iopub.status.idle":"2025-05-28T01:22:09.600268Z","shell.execute_reply.started":"2025-05-28T01:22:09.590466Z","shell.execute_reply":"2025-05-28T01:22:09.599548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 定義季節類型的映射字典\nseason_mapping = {'Spring': 0, 'Summer': 1, 'Fall': 2, 'Winter': 3}\n\n# 找到所有以 Season 結尾的欄位\nseason_columns = [col for col in train.columns if col.endswith('Season')]\n\n# 將這些列的值轉換為數值\nfor col in season_columns:\n    train[col] = train[col].map(season_mapping)\n\ntrain","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.601142Z","iopub.execute_input":"2025-05-28T01:22:09.601872Z","iopub.status.idle":"2025-05-28T01:22:09.701278Z","shell.execute_reply.started":"2025-05-28T01:22:09.601854Z","shell.execute_reply":"2025-05-28T01:22:09.700536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 找到 test 中的列名\ntest_columns = set(test.columns)\n\n# 找到 train 中需要保留的列（目標列 'sii' 和 test 中存在的列）\ncolumns_to_keep = [col for col in train.columns if col in test_columns or col == 'sii']\n\n# 篩選 train 中需要保留的列\nfiltered_train = train[columns_to_keep]\nfiltered_train = filtered_train[filtered_train['sii'].notnull()]\n# 打印結果\nprint(f\"原始 train 大小: {train.shape}\")\nprint(f\"篩選後 train 大小: {filtered_train.shape}\")\nprint(f\"保留的列: {columns_to_keep}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.701979Z","iopub.execute_input":"2025-05-28T01:22:09.702467Z","iopub.status.idle":"2025-05-28T01:22:09.710107Z","shell.execute_reply.started":"2025-05-28T01:22:09.702443Z","shell.execute_reply":"2025-05-28T01:22:09.709306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 獲取每列的非空值數量\nnon_null_counts = filtered_train.notnull().sum()\n\n# 計算總行數\ntotal_rows = len(filtered_train)\n\n# 計算缺失比率\ntrain_null_ratio = 1 - (non_null_counts / total_rows)\n\n# 設定閾值\nthreshold = 0.3\n\n# 篩選出 null_ratio 小於等於閾值的列\ncolumns_to_keep_train = train_null_ratio[train_null_ratio <= threshold].index.tolist()\n\n# 保留篩選出的列\nfiltered_train = filtered_train[columns_to_keep_train]\n\n# 打印結果\nprint(f\"保留的列: {columns_to_keep_train}\")\nprint(f\"篩選後的 train 大小: {filtered_train.shape}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.710846Z","iopub.execute_input":"2025-05-28T01:22:09.711265Z","iopub.status.idle":"2025-05-28T01:22:09.725652Z","shell.execute_reply.started":"2025-05-28T01:22:09.711243Z","shell.execute_reply":"2025-05-28T01:22:09.724816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 定義季節類型的映射字典\nseason_mapping = {'Spring': 0, 'Summer': 1, 'Fall': 2, 'Winter': 3}\n\n# 找到所有以 Season 結尾的欄位\nseason_columns = [col for col in test.columns if col.endswith('Season')]\n\n# 將這些列的值轉換為數值\nfor col in season_columns:\n    test[col] = test[col].map(season_mapping)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.726514Z","iopub.execute_input":"2025-05-28T01:22:09.726794Z","iopub.status.idle":"2025-05-28T01:22:09.746987Z","shell.execute_reply.started":"2025-05-28T01:22:09.726755Z","shell.execute_reply":"2025-05-28T01:22:09.746267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 合併兩個 DataFrame，根據 id\n# merged_train = pd.merge(filtered_train, train_ts, on='id', how='inner')\nmerged_train = pd.merge(filtered_train, train_ts, on='id', how='left')\n\n\n# 將 id 設置為索引\nmerged_train.set_index('id', inplace=True)\n\n# 打印結果\nmerged_train","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.747731Z","iopub.execute_input":"2025-05-28T01:22:09.747931Z","iopub.status.idle":"2025-05-28T01:22:09.822056Z","shell.execute_reply.started":"2025-05-28T01:22:09.747918Z","shell.execute_reply":"2025-05-28T01:22:09.821375Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n# 篩選數值型列\nnumeric_columns = merged_train.select_dtypes(include=['number']).columns\n\n# 計算相關係數\ncorrelation_with_sii = merged_train[numeric_columns].corr()['sii'].drop('sii')\n\n# 設置圖形大小\nplt.figure(figsize=(20, 20))  # 調整寬和高\n\n# 繪製條形圖\ncorrelation_with_sii.sort_values().plot(kind='barh', color='skyblue')\n\n# 添加標題和軸標籤\nplt.title(\"Correlation with 'sii'\", fontsize=16)  # 設置標題字體大小\nplt.xlabel(\"Correlation coefficient\", fontsize=14)  # 設置 X 軸標籤字體大小\nplt.ylabel(\"Features\", fontsize=14)  # 設置 Y 軸標籤字體大小\n\n# 調整刻度字體大小\nplt.xticks(fontsize=12)\nplt.yticks(fontsize=12)\n\n# 顯示圖形\nplt.tight_layout()  # 自動調整子圖參數以防止文字重疊\nplt.show()\n\n# 打印相關係數\nprint(\"其他欄位與 'sii' 的相關係數：\")\nprint(correlation_with_sii)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:09.822838Z","iopub.execute_input":"2025-05-28T01:22:09.823071Z","iopub.status.idle":"2025-05-28T01:22:10.646452Z","shell.execute_reply.started":"2025-05-28T01:22:09.823055Z","shell.execute_reply":"2025-05-28T01:22:10.645820Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 計算相關係數\ncorrelation_with_sii = merged_train.corr()['sii']\n\n# 篩選相關性大於 0.1 的列\ncolumns_to_keep = correlation_with_sii[correlation_with_sii.abs() > 0.1].index.tolist()\n\n# 保留相關列\nfiltered_train = merged_train[columns_to_keep]\n\n# 打印結果\nprint(f\"保留的列: {columns_to_keep}\")\nprint(f\"篩選後的 train 大小: {filtered_train.shape}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:10.647304Z","iopub.execute_input":"2025-05-28T01:22:10.647891Z","iopub.status.idle":"2025-05-28T01:22:10.676673Z","shell.execute_reply.started":"2025-05-28T01:22:10.647864Z","shell.execute_reply":"2025-05-28T01:22:10.676151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 將 'sii' 欄位移到最後\ncolumns = [col for col in filtered_train.columns if col != 'sii']  # 除了 'sii' 的其他列\ncolumns.append('sii')  # 將 'sii' 加到最後\nfiltered_train = filtered_train[columns]  # 重新排列列順序","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:10.677305Z","iopub.execute_input":"2025-05-28T01:22:10.677515Z","iopub.status.idle":"2025-05-28T01:22:10.681949Z","shell.execute_reply.started":"2025-05-28T01:22:10.677500Z","shell.execute_reply":"2025-05-28T01:22:10.681314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filtered_train","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:10.682624Z","iopub.execute_input":"2025-05-28T01:22:10.682900Z","iopub.status.idle":"2025-05-28T01:22:10.715611Z","shell.execute_reply.started":"2025-05-28T01:22:10.682878Z","shell.execute_reply":"2025-05-28T01:22:10.714831Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_test = pd.merge(test, test_ts, on='id', how='left')\n# 將 id 設置為索引\nmerged_test.set_index('id', inplace=True)\nmerged_test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:22:10.716334Z","iopub.execute_input":"2025-05-28T01:22:10.716512Z","iopub.status.idle":"2025-05-28T01:22:10.828696Z","shell.execute_reply.started":"2025-05-28T01:22:10.716498Z","shell.execute_reply":"2025-05-28T01:22:10.828021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_test = merged_test[['Basic_Demos-Age', 'Basic_Demos-Sex', 'Physical-BMI', 'Physical-Height', 'Physical-Weight', 'Physical-Systolic_BP', 'FGC-FGC_CU', 'FGC-FGC_TL', 'SDS-SDS_Total_Raw', 'SDS-SDS_Total_T', 'PreInt_EduHx-computerinternet_hoursday', 'enmo_mean', 'non-wear_flag_mean', 'X_std', 'enmo_std', 'light_std', 'X_min', 'Y_min', 'Y_max', 'enmo_max', 'enmo_still_ratio']]\nmerged_test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:15:56.534735Z","iopub.execute_input":"2025-05-28T01:15:56.534977Z","iopub.status.idle":"2025-05-28T01:15:56.575965Z","shell.execute_reply.started":"2025-05-28T01:15:56.534960Z","shell.execute_reply":"2025-05-28T01:15:56.575080Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"以下是我新寫的代碼\n/////////////////////////////////////////////////////(以此為分界)","metadata":{}},{"cell_type":"markdown","source":"**純類別**","metadata":{}},{"cell_type":"code","source":"#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport joblib\nimport warnings\n\nfrom sklearn.model_selection import train_test_split, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import classification_report, confusion_matrix, accuracy_score\nfrom sklearn.ensemble import VotingClassifier, AdaBoostClassifier\nfrom xgboost import XGBClassifier\nfrom catboost import CatBoostClassifier\n\nwarnings.filterwarnings(\"ignore\")\n\n# --- 0. 读取数据 -------------------------------------------------------------\n# filtered_train = pd.read_csv(\"your_data.csv\")\nX = filtered_train.drop('sii', axis=1)\ny = filtered_train['sii'].astype(int)\n\n# --- 1. 划分训练/测试 (80/20) --------------------------------------------\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y, test_size=0.2, random_state=42, stratify=y\n)\n\n# --- 2. 前处理 Pipeline ---------------------------------------------------\npre = Pipeline([\n    ('imp', SimpleImputer(strategy='mean')),\n    ('sc', StandardScaler())\n])\n\n# --- 3. 定义搜索空间 & 模型 ----------------------------------------------\n# 每个模型只挑最重要的两项超参数来调，其他保持默认／固定\n\nXGB_params = {\n    'clf__learning_rate': [0.01, 0.05, 0.1],\n    'clf__n_estimators':  [200, 300, 400]\n}\n\nCAT_params = {\n    'clf__depth':         [6, 8, 10]\n}\n\nADA_params = {\n    'clf__n_estimators':  [50, 100, 200],\n    'clf__learning_rate': [0.01, 0.1, 1.0]\n}\n\n\nsearch_space = {\n    'XGB': (\n        XGBClassifier(\n            random_state=42,\n            objective='multi:softprob',\n            eval_metric='mlogloss',\n            use_label_encoder=False,\n            n_jobs=-1\n        ), XGB_params\n    ),\n    'CAT': (\n        CatBoostClassifier(\n            random_state=42,\n            verbose=0,\n            loss_function='MultiClass'\n        ), CAT_params\n    ),\n    'ADA': (\n        AdaBoostClassifier(random_state=42), ADA_params\n    )\n}\n\nbest_estimators = {}\n\n# --- 4. GridSearchCV 全面搜索 & 训练每个基础模型 -----------------------------\nfor name, (model, params) in search_space.items():\n    pipe = Pipeline([('pre', pre), ('clf', model)])\n    grid = GridSearchCV(\n        pipe,\n        param_grid=params,\n        cv=5,\n        scoring='f1_weighted',\n        n_jobs=-1,\n        verbose=1\n    )\n    grid.fit(X_train, y_train)\n    best = grid.best_estimator_\n    best_estimators[name] = best\n\n    # 测试集预测 & 报告\n    y_pred = best.predict(X_test)\n    print(f\"\\n{name} best parameter：{grid.best_params_}\")\n    print(f\"=== {name} test report ===\")\n    print(classification_report(y_test, y_pred, digits=4))\n    print(f\"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}\")\n    print('-'*50)\n\n# --- 5. VotingClassifier 集成 -----------------------------------------------\nvoting = VotingClassifier(\n    estimators=[\n        ('xgb', best_estimators['XGB']),\n        ('cat', best_estimators['CAT']),\n        ('ada', best_estimators['ADA'])\n    ],\n    voting='soft',\n    weights=[4, 4, 4],\n    n_jobs=-1\n)\nvoting.fit(X_train, y_train)\n\ny_vote = voting.predict(X_test)\nprint(\"\\n=== Voting Ensemble test report ===\")\nprint(classification_report(y_test, y_vote, digits=4))\nprint(f\"Ensemble Accuracy: {accuracy_score(y_test, y_vote):.4f}\")\n\ncm = confusion_matrix(y_test, y_vote, labels=np.unique(y))\nplt.figure(figsize=(6,5))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n            xticklabels=np.unique(y), yticklabels=np.unique(y))\nplt.title('Voting Ensemble confusion matrix')\nplt.xlabel('Pred')\nplt.ylabel('True')\nplt.tight_layout()\nplt.show()\n\ndef per_class_acc(y_true, y_pred):\n    labels = sorted(np.unique(y_true))\n    cm = confusion_matrix(y_true, y_pred, labels=labels)\n    return {lbl: cm[i,i]/cm[i,:].sum() for i,lbl in enumerate(labels)}\n\nprint(\"Ensemble per class Accuracy:\", per_class_acc(y_test, y_vote))\n\n# --- 6. 序列化所有模型 & 预处理器 ----------------------------------------------\njoblib.dump(best_estimators, 'best_gradient_models.pkl')\njoblib.dump(voting, 'voting_ensemble.pkl')\njoblib.dump(pre, 'preprocessor.pkl')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T23:33:38.060778Z","iopub.execute_input":"2025-05-27T23:33:38.061011Z","iopub.status.idle":"2025-05-27T23:43:51.793057Z","shell.execute_reply.started":"2025-05-27T23:33:38.060989Z","shell.execute_reply":"2025-05-27T23:43:51.792473Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**分類加入權重**","metadata":{}},{"cell_type":"code","source":"#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport joblib\nimport warnings\n\nfrom sklearn.model_selection import train_test_split, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import (\n    classification_report,\n    confusion_matrix,\n    accuracy_score\n)\nfrom xgboost import XGBClassifier\nfrom catboost import CatBoostClassifier\nfrom sklearn.ensemble import AdaBoostClassifier\n\nwarnings.filterwarnings(\"ignore\")\n\n# --- 类别权重映射 ------------------------------------------------------------\nweight_map = {0: 0.417, 1: 0.73, 2: 0.86, 3: 1.0}\n\n# --- 0. 读取数据 -------------------------------------------------------------\n# filtered_train = pd.read_csv(\"your_data.csv\")\nX = filtered_train.drop('sii', axis=1).values\ny = filtered_train['sii'].astype(int).values\n\n# 计算每个样本的权重\nsample_weight = np.vectorize(weight_map.get)(y)\n\n# --- 1. 划分训练/测试 (80/20) --------------------------------------------\nX_train, X_test, y_train, y_test, sw_train, sw_test = train_test_split(\n    X, y, sample_weight, test_size=0.2, random_state=42, stratify=y\n)\n\n# --- 2. 前处理 Pipeline ---------------------------------------------------\npre = Pipeline([\n    ('imp', SimpleImputer(strategy='mean')),\n    ('sc',  StandardScaler())\n])\n\n# --- 3. 定义搜索空间 & 模型 ----------------------------------------------\nXGB_params = {\n    'clf__learning_rate': [0.01, 0.05, 0.1],\n    'clf__n_estimators':  [200, 300, 400]\n}\nCAT_params = {\n    'clf__depth': [6, 8, 10]\n}\nADA_params = {\n    'clf__n_estimators':  [50, 100, 200],\n    'clf__learning_rate': [0.01, 0.1, 1.0]\n}\n\nsearch_space = {\n    'XGB': (\n        XGBClassifier(\n            random_state=42,\n            objective='multi:softprob',\n            eval_metric='mlogloss',\n            use_label_encoder=False,\n            n_jobs=-1\n        ), XGB_params\n    ),\n    'CAT': (\n        CatBoostClassifier(\n            random_state=42,\n            verbose=0,\n            loss_function='MultiClass'\n        ), CAT_params\n    ),\n    'ADA': (\n        AdaBoostClassifier(random_state=42), ADA_params\n    )\n}\n\nbest_estimators = {}\n\n# --- 4. GridSearchCV 全面搜索 & 训练基础模型 -----------------------------\nfor name, (model, params) in search_space.items():\n    pipe = Pipeline([('pre', pre), ('clf', model)])\n    grid = GridSearchCV(\n        pipe,\n        param_grid=params,\n        cv=5,\n        scoring='f1_weighted',\n        n_jobs=-1,\n        verbose=1\n    )\n    # 传入 sample_weight 给 clf\n    grid.fit(X_train, y_train, **{'clf__sample_weight': sw_train})\n    best_estimators[name] = grid.best_estimator_\n\n    # 测试集预测 & 报告\n    y_pred = grid.predict(X_test)\n    print(f\"\\n=== {name} test report ===\")\n    print(classification_report(y_test, y_pred, digits=4))\n    print(f\"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}\")\n    print('-' * 50)\n\n# --- 5. Soft Voting 手动集成 -----------------------------------------------\n# 收集各模型的 predict_proba 结果\nprobas = []\nfor name, clf in best_estimators.items():\n    probas.append(clf.predict_proba(X_test))\n# 平均概率\navg_proba = np.mean(probas, axis=0)\n# 取最大概率对应的类别\ny_vote = np.argmax(avg_proba, axis=1)\n\nprint(\"\\n=== Voting Ensemble test report ===\")\nprint(classification_report(y_test, y_vote, digits=4))\nprint(f\"Ensemble Accuracy: {accuracy_score(y_test, y_vote):.4f}\")\n\n# 混淆矩阵\ncm = confusion_matrix(y_test, y_vote, labels=[0,1,2,3])\nplt.figure(figsize=(6,5))\nsns.heatmap(\n    cm, annot=True, fmt='d', cmap='Blues',\n    xticklabels=[0,1,2,3], yticklabels=[0,1,2,3]\n)\nplt.title('Voting Ensemble confusion matrix')\nplt.xlabel('Pred')\nplt.ylabel('True')\nplt.tight_layout()\nplt.show()\n\ndef per_class_acc(y_true, y_pred):\n    cm = confusion_matrix(y_true, y_pred, labels=[0,1,2,3])\n    return {lbl: cm[i,i]/cm[i,:].sum() for i,lbl in enumerate([0,1,2,3])}\n\nprint(\"Ensemble 每类 Accuracy:\", per_class_acc(y_test, y_vote))\n\n# --- 6. 序列化所有模型 & 预处理器 ----------------------------------------------\njoblib.dump(best_estimators, 'best_classifiers.pkl')\njoblib.dump(pre,             'preprocessor.pkl')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T00:18:45.974781Z","iopub.execute_input":"2025-05-28T00:18:45.975057Z","iopub.status.idle":"2025-05-28T00:28:58.631788Z","shell.execute_reply.started":"2025-05-28T00:18:45.975032Z","shell.execute_reply":"2025-05-28T00:28:58.631183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"why:AdaBoost 对权重特别敏感\nAdaBoost 本身就通过不断调整训练样本的权重来让弱学习器聚焦难分类的样本。\n\n当你在外部又给少数类打了很高的权重（比如类 3 权重设成 1，而类 0 只有 0.417），AdaBoost 在每一轮都会把这些已被你“标记”为”难分类“的少数类样本权重再提高，导致它几乎所有轮次都在拼命去纠正少数几条样本的错误，反而忽略了绝大多数的主流类，从而整体性能下滑。\n\nCatBoost 默认的正则化与采样策略\nCatBoost 在内部也做了均衡采样（subsample/bagging_temperature）和 L2 正则化。\n\n当所有少数类样本都被你“外面”硬性加权时，CatBoost 会在反复训练中“看到”它们的重要性被大幅放大，于是为了降低整体损失，会牺牲大多数类的拟合质量，整体 F1/Accuracy 也就掉下来了。","metadata":{}},{"cell_type":"markdown","source":"**回歸器**","metadata":{}},{"cell_type":"code","source":"#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport joblib\nimport warnings\n\nfrom sklearn.model_selection import train_test_split, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import (\n    mean_squared_error, mean_absolute_error, r2_score,\n    classification_report, confusion_matrix, accuracy_score\n)\nfrom sklearn.ensemble import VotingRegressor, AdaBoostRegressor\n\nfrom xgboost import XGBRegressor\nfrom catboost import CatBoostRegressor\n\nwarnings.filterwarnings(\"ignore\")\n\n# --- 0. 读取数据 -------------------------------------------------------------\n# filtered_train = pd.read_csv(\"your_data.csv\")\nX = filtered_train.drop('sii', axis=1)\ny = filtered_train['sii'].astype(float)  # 回归取 float\n\n# 确定分类范围\nmin_label, max_label = int(y.min()), int(y.max())\nlabels = list(range(min_label, max_label + 1))\n\n# --- 1. 划分训练/测试 (80/20) --------------------------------------------\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y, test_size=0.2, random_state=42\n)\n\n# --- 2. 前处理 Pipeline ---------------------------------------------------\npre = Pipeline([\n    ('imp', SimpleImputer(strategy='mean')),\n    ('sc', StandardScaler())\n])\n\n# --- 3. 定义网格搜索空间 & 模型 ------------------------------------------\n# 每个模型只挑最重要的两项超参数来调，其他保持默认／固定\n\nXGB_params = {\n    'reg__learning_rate': [0.01, 0.05, 0.1],\n    'reg__n_estimators':  [200, 300, 400]\n}\n\nCAT_params = {\n    'reg__depth': [6, 8, 10]\n}\n\nADA_params = {\n    'reg__n_estimators':  [50, 100, 200],\n    'reg__learning_rate': [0.01, 0.1, 1.0]\n}\n\nsearch_space = {\n    'XGB': (\n        XGBRegressor(\n            random_state=42,\n            objective='reg:squarederror',\n            n_jobs=-1\n        ),\n        XGB_params\n    ),\n    'CAT': (\n        CatBoostRegressor(\n            random_state=42,\n            verbose=0,\n            loss_function='RMSE'\n        ),\n        CAT_params\n    ),\n    'ADA': (\n        AdaBoostRegressor(random_state=42),\n        ADA_params\n    )\n}\n\nbest_estimators = {}\nfor name, (model, params) in search_space.items():\n    pipe = Pipeline([('pre', pre), ('reg', model)])\n    grid = GridSearchCV(\n        pipe,\n        param_grid=params,\n        cv=5,\n        scoring='r2',      # 以 R² 作为搜索指标\n        n_jobs=-1,\n        verbose=1\n    )\n    grid.fit(X_train, y_train)\n    best_estimators[name] = grid.best_estimator_\n\n    # 回归评估\n    y_pred = grid.predict(X_test)\n    print(f\"\\n{name} best parameter：\", grid.best_params_)\n    print(f\"=== {name} regression evaluation ===\")\n    print(f\"MSE:  {mean_squared_error(y_test, y_pred):.4f}\")\n    print(f\"MAE:  {mean_absolute_error(y_test, y_pred):.4f}\")\n    print(f\"R²:   {r2_score(y_test, y_pred):.4f}\")\n    print('-'*30)\n\n    # 回归→分类评估\n    y_pred_cls = np.rint(y_pred).astype(int)\n    y_pred_cls = np.clip(y_pred_cls, min_label, max_label)\n\n    print(f\"\\n=== {name} regression classification report ===\")\n    print(classification_report(\n        y_test.astype(int),\n        y_pred_cls,\n        labels=labels,\n        digits=4\n    ))\n    print(f\"{name} Accuracy: {accuracy_score(y_test.astype(int), y_pred_cls):.4f}\")\n\n    # 混淆矩阵\n    cm = confusion_matrix(\n        y_test.astype(int),\n        y_pred_cls,\n        labels=labels\n    )\n    plt.figure(figsize=(5,4))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n                xticklabels=labels, yticklabels=labels)\n    plt.title(f'{name} confusion matrix')\n    plt.xlabel('Pred')\n    plt.ylabel('True')\n    plt.tight_layout()\n    plt.show()\n\n    # 每类 Accuracy\n    per_acc = {lbl: cm[i,i] / cm[i].sum() for i, lbl in enumerate(labels)}\n    print(f\"{name} per class Accuracy:\", per_acc)\n    print('='*50)\n\n# --- 4. 构建 VotingRegressor ------------------------------------------------\nvoting = VotingRegressor(\n    estimators=[\n        ('xgb', best_estimators['XGB']),\n        ('cat', best_estimators['CAT']),\n        ('ada', best_estimators['ADA'])\n    ]\n)\nvoting.fit(X_train, y_train)\n\ny_vote = voting.predict(X_test)\nprint(\"\\n=== VotingRegressor regression evaluation ===\")\nprint(f\"MSE:  {mean_squared_error(y_test, y_vote):.4f}\")\nprint(f\"MAE:  {mean_absolute_error(y_test, y_vote):.4f}\")\nprint(f\"R²:   {r2_score(y_test, y_vote):.4f}\")\n\n# VotingRegressor → 分类评估\ny_vote_cls = np.rint(y_vote).astype(int)\ny_vote_cls = np.clip(y_vote_cls, min_label, max_label)\n\nprint(\"\\n=== VotingRegressor regression classification report ===\")\nprint(classification_report(\n    y_test.astype(int),\n    y_vote_cls,\n    labels=labels,\n    digits=4\n))\nprint(f\"Voting Accuracy: {accuracy_score(y_test.astype(int), y_vote_cls):.4f}\")\n\ncm = confusion_matrix(y_test.astype(int), y_vote_cls, labels=labels)\nplt.figure(figsize=(5,4))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n            xticklabels=labels, yticklabels=labels)\nplt.title('Voting confusion natrix')\nplt.xlabel('Pred')\nplt.ylabel('True')\nplt.tight_layout()\nplt.show()\n\nper_acc = {lbl: cm[i,i] / cm[i].sum() for i, lbl in enumerate(labels)}\nprint(\"Voting per class Accuracy:\", per_acc)\n\n# --- 5. 序列化所有模型 & 预处理器 ----------------------------------------------\njoblib.dump(best_estimators, 'best_regressors.pkl')\njoblib.dump(voting, 'voting_regressor.pkl')\njoblib.dump(pre, 'preprocessor.pkl')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T00:14:56.272912Z","iopub.execute_input":"2025-05-28T00:14:56.273297Z","iopub.status.idle":"2025-05-28T00:18:45.973580Z","shell.execute_reply.started":"2025-05-28T00:14:56.273275Z","shell.execute_reply":"2025-05-28T00:18:45.972855Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**smote加迴歸器**","metadata":{}},{"cell_type":"code","source":"#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport warnings\n# 全局屏蔽 “invalid value encountered in scalar divide” RuntimeWarning\nwarnings.filterwarnings(\n    \"ignore\",\n    message=\"invalid value encountered in scalar divide\",\n    category=RuntimeWarning\n)\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport joblib\n\nfrom sklearn.model_selection import train_test_split, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.metrics import (\n    classification_report,\n    confusion_matrix,\n    accuracy_score,\n    cohen_kappa_score,\n    make_scorer\n)\nfrom sklearn.ensemble import VotingRegressor, AdaBoostRegressor\nfrom xgboost import XGBRegressor\nfrom catboost import CatBoostRegressor\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.utils import resample\n\n# filtered_train = pd.read_csv(\"your_data.csv\")\nX = filtered_train.drop('sii', axis=1).values\ny_orig = filtered_train['sii'].astype(int).values  # 原始 0–3\n\n# 回归连续映射值直接就是 0,1,2,3\nmapped_values = np.array([0, 1, 2, 3])\n\n# --- 1. 划分训练/测试 --------------------------------------------------------\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y_orig.astype(float), test_size=0.2, random_state=42\n)\n\n# --- 2. 手动过采样少数类（原始类别3） ----------------------------------------\nmask_min = (y_train == 3)\nX_min, y_min = X_train[mask_min], y_train[mask_min]\nX_maj, y_maj = X_train[~mask_min], y_train[~mask_min]\n\nX_min_ups, y_min_ups = resample(\n    X_min, y_min,\n    replace=True,\n    n_samples=len(y_maj),\n    random_state=42\n)\n\nX_train_bal = np.vstack([X_maj, X_min_ups])\ny_train_bal = np.concatenate([y_maj, y_min_ups])\n\n# --- 3. 前处理 Pipeline -----------------------------------------------------\npre = Pipeline([\n    ('imp', SimpleImputer(strategy='mean')),\n    ('sc',  StandardScaler())\n])\n\n# --- 4. 映射连续预测到类别索引 ----------------------------------------------\ndef map_cont_to_cls(arr_cont: np.ndarray) -> np.ndarray:\n    diffs = np.abs(arr_cont.reshape(-1, 1) - mapped_values.reshape(1, -1))\n    return diffs.argmin(axis=1)\n\n# --- 5. 自定义 QWK scorer（含 NaN 保护） -----------------------------------\ndef qwk_score(y_true_cont, y_pred_cont):\n    true_cls = map_cont_to_cls(np.array(y_true_cont))\n    pred_cls = map_cont_to_cls(np.array(y_pred_cont))\n    with warnings.catch_warnings():\n        warnings.simplefilter(\"ignore\", category=RuntimeWarning)\n        kappa = cohen_kappa_score(true_cls, pred_cls, weights='quadratic')\n    return 0.0 if np.isnan(kappa) else kappa\n\nqwk_scorer = make_scorer(qwk_score, greater_is_better=True)\n\n# --- 6. 定义搜索空间与模型 --------------------------------------------------\nXGB_params = {\n    'reg__learning_rate': [0.01, 0.05, 0.1],\n    'reg__n_estimators':  [200, 300, 400]\n}\n\nCAT_params = {\n    'reg__depth': [6, 8, 10]\n}\n\nADA_params = {\n    'reg__n_estimators':  [50, 100, 200],\n    'reg__learning_rate': [0.01, 0.1, 1.0]\n}\n\nsearch_space = {\n    'XGB': (\n        XGBRegressor(\n            random_state=42,\n            objective='reg:squarederror',\n            n_jobs=-1\n        ),\n        XGB_params\n    ),\n    'CAT': (\n        CatBoostRegressor(\n            random_state=42,\n            verbose=0,\n            loss_function='RMSE'\n        ),\n        CAT_params\n    ),\n    'ADA': (\n        AdaBoostRegressor(random_state=42),\n        ADA_params\n    )\n}\n\nbest_estimators = {}\n\n# --- 7. GridSearchCV 全面搜索 & 训练基础模型 -----------------------------\nfor name, (model, params) in search_space.items():\n    pipe = Pipeline([('pre', pre), ('reg', model)])\n    grid = GridSearchCV(\n        pipe,\n        param_grid=params,\n        cv=5,\n        scoring=qwk_scorer,\n        n_jobs=-1,\n        verbose=1\n    )\n    grid.fit(X_train_bal, y_train_bal)\n    best_estimators[name] = grid.best_estimator_\n\n    # --- QWK & 分类报告 ---\n    y_pred_cont = grid.predict(X_test)\n    y_pred_cls  = map_cont_to_cls(y_pred_cont)\n\n    qwk_val = qwk_score(y_test, y_pred_cont)\n    print(f\"\\n{name} best QWK = {qwk_val:.4f}\")\n    print(f\"{name} best parameter：{grid.best_params_}\")\n    print(classification_report(y_test.astype(int), y_pred_cls, labels=mapped_values, digits=4))\n    print(f\"{name} Accuracy: {accuracy_score(y_test.astype(int), y_pred_cls):.4f}\")\n\n    cm = confusion_matrix(y_test.astype(int), y_pred_cls, labels=mapped_values)\n    plt.figure(figsize=(5,4))\n    sns.heatmap(\n        cm, annot=True, fmt='d', cmap='Blues',\n        xticklabels=mapped_values, yticklabels=mapped_values\n    )\n    plt.title(f'{name} confusion matrix')\n    plt.xlabel('Pred'); plt.ylabel('True')\n    plt.tight_layout()\n    plt.show()\n\n# --- 8. VotingRegressor 集成 -----------------------------------------------\nvoting = VotingRegressor([\n    ('xgb', best_estimators['XGB']),\n    ('cat', best_estimators['CAT']),\n    ('ada', best_estimators['ADA'])\n])\nvoting.fit(X_train_bal, y_train_bal)\n\ny_vote_cont = voting.predict(X_test)\ny_vote_cls  = map_cont_to_cls(y_vote_cont)\n\nqwk_vote = qwk_score(y_test, y_vote_cont)\nprint(\"\\n=== VotingRegressor overall evaluation (QWK) ===\")\nprint(f\"QWK: {qwk_vote:.4f}\")\nprint(classification_report(y_test.astype(int), y_vote_cls, labels=mapped_values, digits=4))\nprint(f\"Voting Accuracy: {accuracy_score(y_test.astype(int), y_vote_cls):.4f}\")\n\ncm = confusion_matrix(y_test.astype(int), y_vote_cls, labels=mapped_values)\nplt.figure(figsize=(5,4))\nsns.heatmap(\n    cm, annot=True, fmt='d', cmap='Blues',\n    xticklabels=mapped_values, yticklabels=mapped_values\n)\nplt.title('Voting confusion matrix')\nplt.xlabel('Pred'); plt.ylabel('True')\nplt.tight_layout()\nplt.show()\n\n# --- 9. 序列化 --------------------------------------------------------------\njoblib.dump(best_estimators, 'best_regressors.pkl')\njoblib.dump(voting,          'voting_regressor.pkl')\njoblib.dump(pre,             'preprocessor.pkl')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-28T01:23:37.634825Z","iopub.execute_input":"2025-05-28T01:23:37.635520Z","iopub.status.idle":"2025-05-28T01:27:32.385228Z","shell.execute_reply.started":"2025-05-28T01:23:37.635494Z","shell.execute_reply":"2025-05-28T01:27:32.384484Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" /////////////////////////////////////////////////////(以此為分界)","metadata":{}}]}