news 2026/4/15 17:00:08

机器学习时间特征处理:循环编码(Cyclical Encoding)与其在预测模型中的应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习时间特征处理:循环编码(Cyclical Encoding)与其在预测模型中的应用

做过电力负荷预测或者交通预测朋友,大概率都处理过时间特征。这里最直接的做法通常是把时间(比如分钟或小时)直接扔进模型里。这看起来逻辑自洽,但存在这一个大坑,就是“午夜悖论”。

比如说你的模型面对两个时间点:23:59(一天的第1439分钟) 和 00:01(一天的第1分钟)。在我们的认知里,这俩只差两分钟,但在模型的逻辑里1439 和 1 可是不一样的。大多数机器学习算法(线性回归、KNN、SVM 甚至神经网络)在处理数值时,默认遵循线性逻辑:数值越大,代表的量级越高。它们理解不了“时间是循环的”这个概念。对它们来说午夜不是终点回到起点的闭环,而是一个断崖。

这就是为什么你加了时间特征,模型却在日期变更线附近表现拉胯的根本原因。

传统编码方式的局限性

处理时间特征,最常见的路数无非两种,但这两种都有硬伤。

整数编码(Integer Encoding)

把 0 到 23 点编码成数字 0-23。这就人为制造了一个断层:23 到 0 的跳跃,被模型视作全天最大的波动。但实际上,晚上 11 点到午夜的变化,跟晚上 9 点到 10 点有什么本质区别吗?完全没有。

下面是这种线性模式下,时间特征在数据层面的表现。

# Generate data date_today = pd.to_datetime('today').normalize() datetime_24_hours = pd.date_range(start=date_today, periods=24, freq='h') df = pd.DataFrame({'dt': datetime_24_hours}) df['hour'] = df['dt'].dt.hour # Calculate Sin and Cosine df["hour_sin"] = np.sin(2 * np.pi * df["hour"] / 24) df["hour_cos"] = np.cos(2 * np.pi * df["hour"] / 24) # Plot the Hours in Linear mode plt.figure(figsize=(15, 5)) plt.plot(df['hour'], [1]*24, linewidth=3) plt.title('Hours in Linear Mode') plt.xlabel('Hour') plt.xticks(np.arange(0, 24, 1)) plt.ylabel('Value') plt.show()

线性模式下的小时表示。

独热编码(One-hot Encoding)

既然连续数字有问题,那拆成 24 个独立的列呢?断层是没了但丢失了更重要的东西:邻近性(Proximity)。在独热编码下,凌晨 2 点和 3 点的距离,跟它和晚上 10 点的距离是一样的。模型失去了“时间相邻”这个上下文信息,更别提这会让特征维度爆炸,树模型处理起来效率低,线性模型跑起来也费劲。

解决方案:三角函数映射(Trigonometric Mapping)

解决这个问题的核心在于思维视角的转换:不要把时间看作一条直线,而要看作一个圆。

24小时是一个闭环,我们的编码方式也得闭环。把每一个小时想象成圆周上均匀分布的点,要确定圆上一个点的位置单靠一个数值是不够的,我们需要两个坐标:xand y

这就是正弦(Sine)和余弦(Cosine)发挥作用的地方。

几何原理

圆周上的任意角度都可以通过正弦和余弦映射到一个唯一的坐标点。这种映射赋予了模型一个平滑、连续的时间表示。

plt.figure(figsize=(5, 5)) plt.scatter(df['hour_sin'], df['hour_cos'], linewidth=3) plt.title('Hours in Cyclical Mode') plt.xlabel('Hour')

经过正弦和余弦转换后的循环模式。

计算公式非常简单:

2 * π * hour / 24

:先把小时数值转化成弧度角度。在这个体系下,午夜和晚上 11 点的角度非常接近,通过

sin

cos

将角度投影到两个坐标轴上。

这两个值结合在一起唯一确定了当前的小时,23:00 和 00:00 在特征空间里的距离就被拉得很近了,这正是我们想要的效果。

这套逻辑同样适用于分钟、星期、月份等任何具有周期性的特征。

代码实战

我们拿 UCI 的Appliances Energy Prediction数据集来跑个对比实验。模型选用随机森林回归器(Random Forest Regressor)。

Candanedo, L. (2017). Appliances Energy Prediction [Dataset]. UCI Machine Learning Repository. https://doi.org/10.24432/C5VC8G. Creative Commons 4.0 License.

# Imports from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import root_mean_squared_error from ucimlrepo import fetch_ucirepo

获取数据:

# fetch dataset appliances_energy_prediction = fetch_ucirepo(id=374) # data (as pandas dataframes) X = appliances_energy_prediction.data.features y = appliances_energy_prediction.data.targets # To Pandas df = pd.concat([X, y], axis=1) df['date'] = df['date'].apply(lambda x: x[:10] + ' ' + x[11:]) df['date'] = pd.to_datetime(df['date']) df['month'] = df['date'].dt.month df['day'] = df['date'].dt.day df['hour'] = df['date'].dt.hour df.head(3)

先建立一个基准模型(Baseline),使用未处理的线性时间特征。

# X and y # X = df.drop(['Appliances', 'rv1', 'rv2', 'date'], axis=1) X = df[['hour', 'day', 'T1', 'RH_1', 'T_out', 'Press_mm_hg', 'RH_out', 'Windspeed', 'Visibility', 'Tdewpoint']] y = df['Appliances'] # Train Test Split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Fit the model lr = RandomForestRegressor().fit(X_train, y_train) # Score print(f'Score: {lr.score(X_train, y_train)}') # Test RMSE y_pred = lr.predict(X_test) rmse = root_mean_squared_error(y_test, y_pred) print(f'RMSE: {rmse}')

基准结果如下:

Score: 0.9395797670166536 RMSE: 63.60964667197874

接下来我们对

hour

day

进行循环编码,替换掉原来的线性特征然后重新训练模型。

# Add cyclical hours sin and cosine df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24) df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24) df['day_sin'] = np.sin(2 * np.pi * df['day'] / 31) df['day_cos'] = np.cos(2 * np.pi * df['day'] / 31) # X and y X = df[['hour_sin', 'hour_cos', 'day_sin', 'day_cos','T1', 'RH_1', 'T_out', 'Press_mm_hg', 'RH_out', 'Windspeed', 'Visibility', 'Tdewpoint']] y = df['Appliances'] # Train Test Split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Fit the model lr_cycle = RandomForestRegressor().fit(X_train, y_train) # Score print(f'Score: {lr_cycle.score(X_train, y_train)}') # Test RMSE y_pred = lr_cycle.predict(X_test) rmse = root_mean_squared_error(y_test, y_pred) print(f'RMSE: {rmse}')

结果显示,Score 提升了约 1%,RMSE 下降了 1 个点。

Score: 0.9416365489096074 RMSE: 62.87008070927842

看着提升不大?这只是一个简单的 Toy Example,也没做任何精细的数据清洗。但这里的提升完全来自于正弦和余弦变换带来的特征表达能力的增强。

本质上这让模型“看懂”了现实世界中电力需求的连续性,它并不会因为时钟跳过 0 点就突然归零。

为什么 Sin 和 Cos 缺一不可?

很多人可能会想,只用 Sin 这一列行不行?还能省点特征维度。答案是不行。这会破坏对称性。在一个 24 小时的圆周上,早上 6 点和晚上 6 点的 Sine 值可能是一样的。如果只给模型一个值,它就会混淆这两个截然不同的时间段(比如早高峰和晚高峰)。必须同时使用 Sin 和 Cos就像定位必须要有经度和纬度一样。只有这样圆上的每一个小时才能拥有唯一的“特征指纹”。

实际应用中的收益

这套方法在不同模型下的收益是不一样的:

  • 基于距离的模型(KNN, SVMs):这是最大的受益者。循环编码消除了边界上的伪“长距离”,让数据点之间的距离计算回归真实。
  • 神经网络(Neural Networks):平滑的特征空间有助于网络更快的收敛和更稳定的训练表现,消除了午夜那种剧烈的数值跳变。
  • 树模型(Tree-based models):虽然像 XGBoost 或 LightGBM 这种强力模型最终也能通过不断分裂学到这种模式,但提供循环编码特征相当于给了它们一个极佳的先验知识(Inductive Bias),在追求极致性能和解释性时非常有用。

适用场景

使用这套方法的判断标准很简单,问自己一个问题:**这个特征是循环往复的吗?**如果是,那就试试。常见的例子包括:

  • 一天中的小时(0-23)
  • 一周中的星期(1-7)
  • 一年中的月份(1-12)
  • 风向(0-360度)

总结

时间在数据科学里不应该只是一个冰冷的数字,它本质上是圆周上的坐标。如果你执意把它当直线处理,模型在周期边界处跌倒是迟早的事。使用正弦和余弦进行循环编码,是一种优雅且低成本的修正手段。它保留了数据的邻近性,消除了人工伪影,能让模型学得更快、更准。下次如果你的模型预测曲线在日期交界处出现诡异的跳变,不妨试试这个方法。

https://avoid.overfit.cn/post/5fea3ffcb7ac4b27a3a0d7bb55b9bd39

作者:Gustavo Santos

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 10:20:26

4 倍扩容 + 700 + 流程图极速展示!ProDB×TDengine 赋能泰州石化

小T导读:中海油泰州石化原有 AspenTech InfoPlus.21 实时数据库系统建设至今已有十余年,随着企业的逐步发展,原有采集点数已达上限,相关应用取数效率下降,限制了企业新需求的增长,借助该国产化项目汉中诺 P…

作者头像 李华
网站建设 2026/4/15 10:22:03

【电动汽车响应率】考虑的是针对电动汽车充放电调度问题,由于放电奖励不同导致部分车主不愿参与放电,设计出响应率计算方法附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/4/15 7:20:30

从数据瓶颈到ROAS飙升21%!Skygo牵手热力引擎,按下游戏增长快进键

在游戏行业全球化竞争日趋激烈的市场环境下,如何依托高效的移动营销合作伙伴(MMP)破解数据短板、优化广告投放效能,不仅是必修课,更是游戏厂商实现规模化增长的破局关键。越南游戏开发与发行公司 Skygo,凭借…

作者头像 李华
网站建设 2026/4/15 2:14:07

需求接口人与研发接口人的职责分别是什么

需求接口人与研发接口人是连接“业务价值”与“技术实现”的两个核心枢纽。需求接口人(通常是产品经理或业务分析师)的核心职责是“定义”,即明确“做什么”和“为什么做”,他们对业务价值、需求优先级和用户体验负责。研发接口人…

作者头像 李华
网站建设 2026/4/15 10:23:28

基于大数据的短视频用户兴趣分析的设计与实现(程序+文档+讲解)

课题介绍在短视频平台精细化运营、个性化推荐需求下,传统用户兴趣分析存在 “维度单一、实时性差、精准度不足” 的痛点,基于大数据技术构建的短视频用户兴趣分析体系,整合用户行为数据、内容交互数据、环境数据等多源信息,实现用…

作者头像 李华
网站建设 2026/4/8 0:33:12

【最新2023】各省地区生产总值GDP 人均生产总值(人均GDP) 地区生产总值指数的 省级省份

【最新2023】各省地区生产总值GDP 人均生产总值(人均GDP) 地区生产总值指数的 省级省份 时间范围1999-2023年 人均生产总值指数(人均GDP指数) 包括一下: 人均生产总值(人均GDP) 各省地区生产总值GDP 人均生产总值指数(人均GDP指数) 地区生产总值指数 见图 说明:人均生产总值指…

作者头像 李华