diff --git a/extended_12_feature_experiment.py b/extended_12_feature_experiment.py new file mode 100644 index 0000000..84c8043 --- /dev/null +++ b/extended_12_feature_experiment.py @@ -0,0 +1,128 @@ +from pathlib import Path + +import numpy as np +import pandas as pd +from sklearn.compose import ColumnTransformer +from sklearn.ensemble import ExtraTreesRegressor, GradientBoostingRegressor, HistGradientBoostingRegressor, RandomForestRegressor +from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import OneHotEncoder, StandardScaler +from sklearn.svm import SVR + +BASE_DIR = Path(__file__).resolve().parent +DATA_PATH = Path('/root/.hermes-web-ui/upload/default/28752a235eb6ef9d.xlsx') +OUT_DIR = BASE_DIR / 'outputs' / 'time_split_experiment' +OUT_DIR.mkdir(parents=True, exist_ok=True) + +TARGET = '总骑行量' +BASE_FEATURES = ['年', '季节', '月', '节假日', '星期', '工作日', '天气', '气温', '体感温度', '湿度', '风速'] +BASE_CAT = ['年', '季节', '月', '节假日', '星期', '工作日', '天气'] + + +def rmse(y_true, y_pred): + return float(np.sqrt(mean_squared_error(y_true, y_pred))) + + +def add_features(df, min_date): + df = df.copy() + df['日期数值'] = (df['日期'] - min_date).dt.days + df['一年中的第几天'] = df['日期'].dt.dayofyear + df['月内日'] = df['日期'].dt.day + df['周序号'] = df['日期'].dt.isocalendar().week.astype(int) + df['日期_sin'] = np.sin(2 * np.pi * df['一年中的第几天'] / 365.25) + df['日期_cos'] = np.cos(2 * np.pi * df['一年中的第几天'] / 365.25) + df['周序号_sin'] = np.sin(2 * np.pi * df['周序号'] / 52.18) + df['月内日_sin'] = np.sin(2 * np.pi * df['月内日'] / 31) + df['年内进度'] = df['一年中的第几天'] / 365.25 + df['季度内月份'] = ((df['月'] - 1) % 3) + 1 + df['是否月初'] = (df['月内日'] <= 7).astype(int) + df['是否月末'] = (df['月内日'] >= 24).astype(int) + df['日期分箱4'] = pd.qcut(df['日期数值'], q=4, labels=False, duplicates='drop').astype(int) + df['日期分箱8'] = pd.qcut(df['日期数值'], q=8, labels=False, duplicates='drop').astype(int) + return df + + +def make_pipeline(model_name, estimator, features, categorical): + numeric = [col for col in features if col not in categorical] + need_scale = model_name == 'SVR' + preprocessor = ColumnTransformer( + transformers=[ + ('cat', OneHotEncoder(handle_unknown='ignore', sparse_output=False), categorical), + ('num', StandardScaler() if need_scale else 'passthrough', numeric), + ], + verbose_feature_names_out=False, + ) + return Pipeline([('preprocess', preprocessor), ('model', estimator)]) + + +def main(): + daily = pd.read_excel(DATA_PATH, sheet_name='每天数据') + future = pd.read_excel(DATA_PATH, sheet_name='预测集') + daily['日期'] = pd.to_datetime(daily['日期']) + future['日期'] = pd.to_datetime(future['日期']) + daily = daily.sort_values('日期').reset_index(drop=True) + future = future.sort_values('日期').reset_index(drop=True) + min_date = daily['日期'].min() + daily = add_features(daily, min_date) + future = add_features(future, min_date) + + split_idx = int(len(daily) * 0.8) + train = daily.iloc[:split_idx].copy() + test = daily.iloc[split_idx:].copy() + + feature_schemes = { + '12_日期数值': (['日期数值'] + BASE_FEATURES, BASE_CAT), + '12_日期sin': (['日期_sin'] + BASE_FEATURES, BASE_CAT), + '12_日期cos': (['日期_cos'] + BASE_FEATURES, BASE_CAT), + '12_年内第几天': (['一年中的第几天'] + BASE_FEATURES, BASE_CAT), + '12_年内进度': (['年内进度'] + BASE_FEATURES, BASE_CAT), + '12_周序号': (['周序号'] + BASE_FEATURES, BASE_CAT + ['周序号']), + '12_周序号sin': (['周序号_sin'] + BASE_FEATURES, BASE_CAT), + '12_月内日': (['月内日'] + BASE_FEATURES, BASE_CAT + ['月内日']), + '12_月内日sin': (['月内日_sin'] + BASE_FEATURES, BASE_CAT), + '12_季度内月份': (['季度内月份'] + BASE_FEATURES, BASE_CAT + ['季度内月份']), + '12_是否月初': (['是否月初'] + BASE_FEATURES, BASE_CAT + ['是否月初']), + '12_是否月末': (['是否月末'] + BASE_FEATURES, BASE_CAT + ['是否月末']), + '12_日期分箱4': (['日期分箱4'] + BASE_FEATURES, BASE_CAT + ['日期分箱4']), + '12_日期分箱8': (['日期分箱8'] + BASE_FEATURES, BASE_CAT + ['日期分箱8']), + } + + models = { + '梯度提升树': lambda: GradientBoostingRegressor(random_state=42, n_estimators=300, learning_rate=0.05, max_depth=3), + '随机森林': lambda: RandomForestRegressor(n_estimators=500, random_state=42, min_samples_leaf=2, n_jobs=-1), + '极端随机树': lambda: ExtraTreesRegressor(n_estimators=500, random_state=42, min_samples_leaf=2, n_jobs=-1), + 'HistGBDT': lambda: HistGradientBoostingRegressor(random_state=42, max_iter=300, learning_rate=0.05, max_leaf_nodes=31), + 'SVR': lambda: SVR(C=200, gamma='scale', epsilon=50), + } + + rows = [] + for scheme_name, (features, categorical) in feature_schemes.items(): + for model_name, factory in models.items(): + pipe = make_pipeline(model_name, factory(), features, categorical) + pipe.fit(train[features], train[TARGET]) + pred = pipe.predict(test[features]) + future_pred = np.maximum(np.round(pipe.predict(future[features])), 0).astype(int) + rows.append({ + '方案': scheme_name, + '模型': model_name, + 'RMSE': rmse(test[TARGET], pred), + 'MAE': float(mean_absolute_error(test[TARGET], pred)), + 'R2': float(r2_score(test[TARGET], pred)), + '未来预测最小值': int(future_pred.min()), + '未来预测最大值': int(future_pred.max()), + '未来预测均值': float(future_pred.mean()), + }) + + result = pd.DataFrame(rows) + result.to_csv(OUT_DIR / 'extended_12_feature_nonlinear_comparison.csv', index=False, encoding='utf-8-sig') + print('各模型最佳前5:') + for model_name, group in result.groupby('模型'): + print('\n[' + model_name + ']') + print(group.sort_values('RMSE').head(5).to_string(index=False, float_format=lambda x: f'{x:.3f}')) + print('\n总体前15:') + print(result.sort_values('RMSE').head(15).to_string(index=False, float_format=lambda x: f'{x:.3f}')) + print('\n保存:', OUT_DIR / 'extended_12_feature_nonlinear_comparison.csv') + + +if __name__ == '__main__': + main() diff --git a/outputs/time_split_experiment/extended_12_feature_nonlinear_comparison.csv b/outputs/time_split_experiment/extended_12_feature_nonlinear_comparison.csv new file mode 100644 index 0000000..d568f6d --- /dev/null +++ b/outputs/time_split_experiment/extended_12_feature_nonlinear_comparison.csv @@ -0,0 +1,71 @@ +方案,模型,RMSE,MAE,R2,未来预测最小值,未来预测最大值,未来预测均值 +12_日期数值,梯度提升树,1115.505147894367,754.6104141829828,0.6459076101057781,3884,8023,6177.22 +12_日期数值,随机森林,1211.6404640631436,857.2259950393145,0.5822456578217434,3778,7634,5700.92 +12_日期数值,极端随机树,1016.4235733532818,804.2402630385485,0.7060165426206524,2681,6504,4381.72 +12_日期数值,HistGBDT,1081.1379394711937,734.543109383189,0.667389732676323,3657,8062,5775.15 +12_日期数值,SVR,1302.8065606379432,1122.631254189851,0.517015371360585,3203,5408,4553.72 +12_日期sin,梯度提升树,1037.9787565353552,864.907988042832,0.6934153789548839,1752,6174,3687.28 +12_日期sin,随机森林,1300.1505289996633,1061.3027606571065,0.5189826853858082,1966,5800,3536.75 +12_日期sin,极端随机树,1019.3127219314714,823.7222013605436,0.7043428918888557,1914,5888,3584.54 +12_日期sin,HistGBDT,1065.2572354517981,863.3337389503802,0.6770893113579488,2111,7633,4194.25 +12_日期sin,SVR,1514.2320642644604,1364.971543352133,0.3475333594320331,1813,4998,3673.18 +12_日期cos,梯度提升树,1102.1009812913542,934.5573526964807,0.654366194091462,1451,6394,3750.99 +12_日期cos,随机森林,1349.2073040509797,1168.881334404296,0.4819987561114465,1960,5927,3613.88 +12_日期cos,极端随机树,1167.5748397557088,989.3852519274374,0.6120793570853851,1898,5622,3508.44 +12_日期cos,HistGBDT,1207.8821487389928,1031.8720725902147,0.5848332530139425,1864,7623,4161.42 +12_日期cos,SVR,1266.5629805240928,1123.6204857013158,0.5435144697239268,1868,5162,3719.4 +12_年内第几天,梯度提升树,910.5720267585195,741.091899857133,0.7640597451116528,1626,6348,3648.54 +12_年内第几天,随机森林,1125.0275082985734,915.0238153129942,0.6398364829590523,1971,5152,3349.19 +12_年内第几天,极端随机树,1010.022890809697,801.2023117913834,0.7097074645417395,1906,5624,3509.47 +12_年内第几天,HistGBDT,1015.9546264325356,824.2028863511464,0.7062877500933793,1971,7453,4134.83 +12_年内第几天,SVR,1408.7450455752507,1271.6382669589445,0.4352734075175475,1913,4987,3632.62 +12_年内进度,梯度提升树,910.5720267585195,741.091899857133,0.7640597451116528,1626,6348,3648.54 +12_年内进度,随机森林,1125.0275082985734,915.0238153129942,0.6398364829590523,1971,5152,3349.41 +12_年内进度,极端随机树,1010.0228908096968,801.2023117913834,0.7097074645417396,1906,5624,3509.47 +12_年内进度,HistGBDT,1015.9546264325356,824.2028863511464,0.7062877500933793,1971,7453,4134.83 +12_年内进度,SVR,1408.7450455752507,1271.6382669589445,0.4352734075175476,1913,4987,3632.62 +12_周序号,梯度提升树,1069.803614918048,903.2622617081902,0.6743271479086517,2006,6530,3973.86 +12_周序号,随机森林,1243.5446976109674,1041.5134963439052,0.5599558665365664,1983,6140,3651.16 +12_周序号,极端随机树,1035.7721993969274,826.6126054421767,0.694717481574221,1964,5895,3568.12 +12_周序号,HistGBDT,1112.59713175554,920.3983467960975,0.6477513742747558,1935,7559,4098.05 +12_周序号,SVR,1292.2405499799693,1150.5398648996525,0.5248177985800409,1860,5149,3786.9 +12_周序号sin,梯度提升树,1063.281232768076,882.1390025824226,0.6782861689221613,1934,6414,3830.73 +12_周序号sin,随机森林,1292.9475995090163,1061.5729066667654,0.524297664357741,1983,5483,3536.65 +12_周序号sin,极端随机树,1014.1497745380785,816.5771680272105,0.7073303876817728,1887,5788,3534.99 +12_周序号sin,HistGBDT,1102.938167765176,893.2039949691602,0.6538408885976152,2032,7599,4183.36 +12_周序号sin,SVR,1503.9741029680147,1354.4278894326096,0.35634351127843267,1807,5003,3670.59 +12_月内日,梯度提升树,1075.2787348073598,910.5418237040739,0.6709851125668345,1823,6745,4072.24 +12_月内日,随机森林,1243.6349732406059,1038.4677767446087,0.5598919738542543,1942,6236,3665.28 +12_月内日,极端随机树,1022.4506637906521,825.6662979591832,0.7025197362733295,1971,5942,3609.59 +12_月内日,HistGBDT,1098.717892872152,907.8006929324015,0.6564849019907664,1965,7569,4085.0 +12_月内日,SVR,1295.0430615629316,1151.4692010654137,0.522754486650163,1842,5183,3805.16 +12_月内日sin,梯度提升树,1067.22297417614,906.8146759377944,0.6758964657440301,1878,6598,3910.3 +12_月内日sin,随机森林,1242.2027167294766,1044.5619603560458,0.5609051081316705,1972,6006,3668.2 +12_月内日sin,极端随机树,995.1317369549822,783.5088700680266,0.7182041522270597,1885,5868,3581.37 +12_月内日sin,HistGBDT,1105.813282654649,918.9424734351023,0.6520338164721056,1821,7375,4119.0 +12_月内日sin,SVR,1294.6875882479544,1143.1041218806386,0.5230164466831182,1735,5223,3787.08 +12_季度内月份,梯度提升树,1030.8840120603018,873.0194697266488,0.6975921616220541,1949,6684,3925.74 +12_季度内月份,随机森林,1253.0127757589096,1049.3735973230855,0.5532295571774203,1912,6205,3643.17 +12_季度内月份,极端随机树,1022.5707998779073,823.3960331065751,0.7024498253900622,1963,6010,3627.72 +12_季度内月份,HistGBDT,1104.0353223349737,907.1740751082931,0.6531518582216439,2021,7502,4113.82 +12_季度内月份,SVR,1279.4999059696324,1136.644561330494,0.5341415772015901,1813,5170,3795.71 +12_是否月初,梯度提升树,1074.707109174901,911.4478533502376,0.6713348327770166,2009,6741,4032.85 +12_是否月初,随机森林,1251.1986686370237,1047.3387084749343,0.5545222857849463,1996,6222,3699.8 +12_是否月初,极端随机树,1004.7715101011122,798.0876673469379,0.7127182352012016,1952,5982,3692.01 +12_是否月初,HistGBDT,1090.5958037531236,900.8674504846922,0.6615448864141861,2005,7533,4136.36 +12_是否月初,SVR,1266.2996781493812,1123.4419542652251,0.5437042450965154,1820,5174,3796.87 +12_是否月末,梯度提升树,1051.1491771051826,889.5436406255417,0.6855858052834116,1934,6997,4023.66 +12_是否月末,随机森林,1252.4058220639308,1048.5324678294116,0.553662279492491,2037,6112,3700.39 +12_是否月末,极端随机树,1023.9819170179634,821.4061183673467,0.7016280380651778,1955,5912,3665.4 +12_是否月末,HistGBDT,1099.3134078227854,912.4381059898651,0.6561124245479075,1990,7477,4095.89 +12_是否月末,SVR,1266.3037333555528,1118.853560743366,0.5437013226029602,1840,5168,3791.36 +12_日期分箱4,梯度提升树,1159.7677465407507,959.746334523742,0.6172497448666919,1082,6711,3760.55 +12_日期分箱4,随机森林,1354.8648153138981,1127.6347175619164,0.4776454707850357,1242,6317,3767.98 +12_日期分箱4,极端随机树,1045.4835916765785,878.4472097505668,0.6889659912708692,1464,5909,3542.3 +12_日期分箱4,HistGBDT,1081.9800025011405,878.4483264538926,0.6668714123558845,1264,7770,4128.71 +12_日期分箱4,SVR,1416.8450442399799,1279.3344630153172,0.4287606105220405,1600,5424,3788.31 +12_日期分箱8,梯度提升树,1132.6317366268574,943.8527579349204,0.6349512308901111,1356,6726,4009.71 +12_日期分箱8,随机森林,1303.8402293998106,1094.8299032973075,0.516248650968877,1246,6253,3806.88 +12_日期分箱8,极端随机树,1386.534777125338,1147.028979138322,0.4529399894883904,1012,6887,3894.88 +12_日期分箱8,HistGBDT,1039.4639834179268,842.3983219405343,0.6925373773716995,1388,7779,4362.77 +12_日期分箱8,SVR,1366.5766542260164,1225.520011226714,0.4685756746775356,1775,5362,3841.55