再见One-Hot!时间序列特征循环编码火了!


在进行时间序列机器学习模型的训练时,通常需要利用如下时间特征:小时、星期、月份以及周或年。将时间戳列转换为这些特征非常简单。确保将时间列转换为日期时间对象后,你可以使用.dt.Time方法来提取大量的时间序列特征。

df['Hour']=df['Datetime'].dt.hour
df['Month']=df['Datetime'].dt.month
df['Dayofweek']=df['Datetime'].dt.dayofweek

举个例子,使用一个包含每小时电力消耗数据的数据集作为参考。能源消耗数据集通常属于时间序列数据,其最终目的是利用过去的数据来预测未来的消耗量,因此这是一个很好的应用案例。尽管温度、湿度和风速等外部特征也会对能源消耗产生影响,但在这里我会着重关注时间序列特征的提取和转换。

现在你已经从 0 个可用功能变成了 3 个。

在使用 ML 时,我们需要对特征进行适当的处理,不能直接将其原样传入模型。原因在于大多数模型会将时间序列特征错误地理解为数字特征。举例来说,在能源消耗方面,某些高峰时段通常会导致较高的能源消耗,而其他特定时段则有较低的能耗。换句话说,可以将每个小时视作一个类别。

通过放大数据集的特定部分,可以证明这一点。数据显示了明显的消费模式,例如在下午 5-6 点的使用量往往达到最高峰,而在上午 5-7 点的使用量则最低。

显然,时间/年份/月份和星期等特征之间存在着复杂的相互作用,因此我们需要将更多的信息纳入我们的模型中。

为了做到这一点,我们需要使用其他格式来编码分类特征,以确保模型能够正确理解这些特征。最常见的方法是使用独热编码。

One-Hot(独热编码)的实现非常简单直接。它的基本原理是,对于一天(或月、日等)中的任何给定小时,我们会询问“它是否是第n小时/日/月”?然后用一个二进制的0或1来回答。对每一种类别都是这样操作。因此,对于原始特征中的day_of_week,将会得到7个编码特征(代表一周中的7天):

  • is_day_1
  • is_day_2
  • is_day_3
  • is_day_4
  • is_day_5
  • is_day_6
  • is_day_7

在 Python 中,最简单的方法是使用 pd.get_dummies

columns_to_encode = ['Hour''Month''Dayofweek']
df = pd.get_dummies(df, columns=columns_to_encode)

这将产生新的特征集

我们的特征非常丰富。我们已经将列数从原先的3列(小时、月、星期)增加到了40多列。随着需要编码的时间序列特征不断增加,这可能会变得越来越复杂。要跟踪所有这些特征可能会变得相当困难,特别是当您希望在数据库中存储或可视化这些特征时,您可能会希望避免产生过于混乱的图表。

循环编码

时间序列数据具有周期性循环的特点。例如,一天被划分为24个小时,当时针指向24:00(凌晨 12 点),新的一天就开始了,之后是1点、2点...按顺序循环。尽管从数值上看,1点与24点(0点)相距最远,但实际上1点与23点一样接近0点,因为它们都处于同一个24小时周期内。

因此,除了用数值直接表示时间,我们还可以将时间戳转换为正弦和余弦值。这种方法实质上是将时间映射到单位圆上,根据时间在圆周上的位置,赋予对应的正弦和余弦坐标值。它能很好地体现一天、一周或一年等周期性时间的特征。

与简单的类别编码(one-hot encoding)不同,这种方法将时间转化为数值特征,相邻时间点的特征值也相对接近,而相距较远的时间点的特征值则相去甚远。这样可以保留时间序列的关联性,而类别编码会丢失这种信息。

我们可以将单位圆的0度(3点钟方向)作为起始点,对应0:00(午夜)。然后按逆时针方向,将圆周等分为4个象限,分别对应上午6点、中午12点、下午6点和午夜12点。任意一个时间戳都可以映射到对应的象限中,从而获得其唯一的正弦和余弦坐标值,这两个值就代表了该时间戳的数值特征。通过这种方式,我们可以用这对正弦余弦值来周期性地表示一天24小时的时间序列。

为什么选择正弦余弦编码

时间序列数据有循环周期性的特点,比如一天24小时就是一个循环。我们希望编码后的特征值能够体现这种循环关系,即相邻的时间点特征值相近,而时间间隔越大,特征值差异就越大。正弦余弦函数本身具有周期性,非常适合表示这种循环模式。

具体是如何编码的

以每天24小时为例,我们将时间映射到单位圆上。圆周代表一天,设圆心为原点(0,0),半径为1。我们可将0点(午夜)设为起点,对应圆周上(1,0)的位置,并按逆时针方向进行。那么:

  • 6点钟对应(0,1)
  • 中午12点对应(-1,0)
  • 18点钟对应(0,-1)

对于任意一个时间t,我们就可以根据它在圆周上的位置,计算出其对应的(cos(t), sin(t))坐标值。这对坐标值就是该时间点的正弦余弦特征编码。

为什么这样编码好

  • 保持周期性:相邻时间的编码值接近,间隔大则编码差异大
  • 无边界:0点与24点编码相同,避免了"边界"问题
  • 更多信息:与one-hot编码相比,正余弦值更加连续,信息更丰富

其他周期也可类似编码

对于其他周期性时间序列,如一周七天、一年十二个月等,也可类似地将其映射到单位圆并编码为正余弦值对。甚至可将多个不同的周期合并编码。

基本单位圆

可以将相同的方法应用于其他周期,比如星期或年。在Python中实现这一点,首先需要将日期时间(在我这个例子中是每小时的时间戳)转换为数值变量。通过将此列转换为pd.Timestamp.timestamp对象,我们可以将每个时间戳转换为Unix时间(从1970年1月1日以来已过去的秒数)。

此时,可以将此数值列转换为正弦和余弦特征。

# 将日期时间转换为数字秒时间戳对象 
# (tells you the date/time in seconds)
timestamp_s = df['Datetime'].map(pd.Timestamp.timestamp)

# 获取每个时间段的秒数
day = 24*60*60
week = day*7
year = day*(365.2425)

# 使用正弦和余弦进行变换
# Time of day
df['Day_sin'] = np.sin(timestamp_s * (2 * np.pi / day))
df['Day_cos'] = np.cos(timestamp_s * (2 * np.pi / day))

# Time of week
df['Week_sin'] = np.sin(timestamp_s * (2 * np.pi / week))
df['Week_cos'] = np.cos(timestamp_s * (2 * np.pi / week))

# Time of year
df['Year_sin'] = np.sin(timestamp_s * (2 * np.pi / year))
df['Year_cos'] = np.cos(timestamp_s * (2 * np.pi / year))

大致而言,我们首先需要将时间戳从秒转换为弧度,即乘以2 * np.pi,因为一个完整的圆/周期有 2 * pi 的弧度。然后,我们将结果除以周期,这样就能以秒(日、周或年)为单位得到周期持续时间。接下来,通过乘以弧度数,我们将每个时间戳映射到一个唯一的角度,表示其在周期中的位置。

例如,如果周期为天,一天开始时的时间戳将被映射为 0 弧度,一天中间的时间戳将被映射为 np.pi 弧度,一天结束时的时间戳将被映射为 2 * np.pi 弧度。

最后,我们计算结果的 值,得到单位圆上实际的 x 和 y 坐标值。这些值将始终介于 -1 和 1 之间。

通过这种方法,每个原始时间序列特征(如每天的小时、每周的天、每年的月)现在只映射到 2 个新特征(原始特征的正弦和余弦),而不是 24、7、12 等。

缺点

在使用正弦余弦编码时间序列特征的方法时,需要格外谨慎并注意以下几点:

编码方式的选择有赖于数据分布

  • 如果数据在某些特定时间点/月份等存在显著的峰值,使用one-hot编码可能更合适,因为它能够明确区分这些异常值。
  • 但如果数据在较大的时间范围内(如中午12点至下午2点)呈现周期性波动,正弦余弦编码可能更加高效,能够较好捕捉数据的连续性和周期规律。

编码方式与模型算法相关

  • 正弦余弦编码特别适用于深度学习/神经网络等模型,因为这些模型擅长学习数值型特征之间的非线性关系。
  • 但对于基于决策树的模型如随机森林,由于其每次只根据一个特征进行分裂,可能无法很好利用正弦余弦编码所带来的优势。因为一个原始的时间特征被拆分为两个正弦余弦值,决策树会分别对待这两个数值。

具体问题具体分析

  • 并非完全禁止在树模型中使用正弦余弦编码。在一些特殊情况下,它仍可能对模型有益。需根据具体数据集和问题进行交叉验证和测试集评估。在选择编码方案前,务必对比one-hot与正弦余弦编码在你的数据上的表现,以确定更优方案。

往期推荐阅读


用于时间序列概率预测的分位数回归
多步时间序列预测策略实战
用 Lag-Llama 进行时间序列预测实战
时间序列预测中的探索性数据分析
用于时间序列中的变点检测算法
全自动机器学习 AutoML 高效预测时间序列
经典时间序列模型 DeepAR 预测股票趋势
用于时间序列概率预测的蒙特卡罗模拟
用于时间序列概率预测的共形预测
时间序列 | 从开始到结束日期自增扩充数据
时间序列 | 重采样及频率转换
时间序列 | 时期(Period)及其算术运算
时间序列 | 字符串和日期的相互转换
时间序列 | pandas时间序列基础时间序列数据分析与预测之Python工具汇总
时间序列数据处理,不再使用pandas
用Python将时间序列转换为监督学习问题
使用 Auto-TS 自动化时间序列预测
终于把时间序列预测ARIMA模型讲明白了
亚马逊最新时序大模型Chronos:将时间序列作为一种语言进行学习一文彻底搞懂 Facebook 时间序列预测算法 Prophet 的研究
LazyProphet:使用 LightGBM 进行时间序列预测
一文讲透时间序列的平稳性一文解读时间序列基本概念时间序列数据的预处理方法总结
用LightGBM进行时间序列预测项目实战
时间序列预测中如何检测随机游走和白噪声
9个时间序列交叉验证方法的介绍和对比
基于树模型的时间序列预测实战时间序列分析和预测实战
基于趋势和季节性的时间序列预测实战
使用 Transformers 进行概率时间序列预测实战用LightGBM进行时间序列预测项目实战时间序列的季节性:3种模式及8种建模方法
PatchTST:长时间序列预测神器
又一个自动化时间序列分析与预测神器
用于时间序列预测的最佳深度学习模型总结时间序列预测任务的模型选择最全总结神经网络时间序列预测PyTorch-Forecasting基于Python的时间序列异常值检测XGBoost和LightGBM时间序列预测对比特征工程:时间特征构造以及时间序列特征构造使用PyTorch-LSTM进行单变量时间序列预测的示例教程如何检测时间序列中的异方差性如何做好多元时间序列特征工程?基于SARIMA、XGBoost和CNN-LSTM的时间序列预测对比用于时间序列异常值检测的全栈机器学习系统多变量时序预测中的向量自回归(VAR)再来一个时序异常检测工具:ADTK炒股必看的时序预测基本方法 -- 移动平均(SMA、EMA、WMA)
Transformer王者归来!无需修改任何模块,时序预测全面领先
一文讲透时序和序列数据的存储、搜索、分析及挖掘
基于深度学习模型的空气污染时序预测
🏴‍☠️宝藏级🏴‍☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉 PythonMySQL数据分析数据可视化机器学习与数据挖掘爬虫 等,从入门到进阶!

长按👇关注- 数据STUDIO -设为星标,干货速递

相关推荐

  • 本周四得到 App 直播,欢迎你来捧场😄🤝
  • 统计学神书之后,国内概率论教材的天花板来了!
  • 暑期限定|机器学习、联邦学习,隐私计算实训营免费参与!
  • ACL2024 | AI的时空穿越记:大型语言模型共时推理的奇幻之旅!
  • 又发了一篇顶会!
  • 【SpringBoot】 Test & Junit5 看这一篇就足够了
  • 29岁,大厂女程序员,总包六折结束北漂,聊聊换城市。
  • Stream很好,Map很酷,但答应我别用toMap()
  • 分享10 个功能强大的单行CSS布局技巧
  • “一秒”宕机一个半小时!这个让程序员抓狂的续一秒,今年无了!
  • 28.3K Star ,你绝不能错过的JSON可视化工具
  • 分享两个浏览器小插件:提升效率美化页面、增加鼠标点击定制效果
  • 张小龙:视频号红利
  • 李彦宏呼吁行业不要卷模型、无人驾驶来了、快手可灵火了
  • Python 是怎么存储字符串的?
  • ES2024新特性:object.groupBy()让分组更简单
  • 使用缓存,踩过的7个坑
  • 思路打开!腾讯造了10亿个角色,驱动数据合成!7B模型效果打爆了
  • 一步生成超过10个Tokens!! 无损模型解码加速最新工作
  • 2024阿贝尔奖得主致年轻数学家:你们选择了绝对正确且完美的领域