基于树的时间序列预测(LGBM)

2024-05-07 01:52
文章标签 时间 预测 序列 lgbm

本文主要是介绍基于树的时间序列预测(LGBM),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在大多数时间序列预测中,尽管有Prophet和NeuralProphet等方便的工具,但是了解基于树的模型仍然具有很高的价值。尤其是在监督学习模型中,仅仅使用单变量时间序列似乎信息有限,预测也比较困难。因此,为了生成足够的特征,需要采取一些方法,例如创建大量的滞后变量。此外,关于预测目标值,也要用过去的项来预测未来的项,而且需要决定是一步领先还是多步领先。

从单变量时间序列中创建特征

        在单变量时间序列中一般只能获得有限的信息。ARIMA 模型使用过去的值来预测未来的值,因此过去的值是重要的候选特征,可以创建许多滞后回归因子。时间指数是一个有价值的领域,因此可以基于此创建特征。由于日历上的事件和年度事件在生活中不断重复,它们为过去留下了印记,为未来提供了教益。因此可以从与时间相关的特征入手。

创建基于时间的特征

        创建基于时间的特征,包括日期、星期、季度等各种特征,通过 pandas series 的 "date" 类中提供的一系列函数,可以轻松实现这些需求。

def create_date_features(df):df['month'] = df.date.dt.monthdf['day_of_month'] = df.date.dt.daydf['day_of_year'] = df.date.dt.dayofyeardf['week_of_year'] = df.date.dt.weekofyeardf['day_of_week'] = df.date.dt.dayofweek + 1df['year'] = df.date.dt.yeardf['quarter'] = df.date.dt.quarterdf['hour_of_day'] = df.date.dt.hourdf['weekday'] = df.date.dt.weekdaydf['is_year_start'] = df.date.dt.is_year_start.astype(int)df['is_year_end'] = df.date.dt.is_year_end.astype(int)df['is_month_start'] = df.date.dt.is_month_start.astype(int)df['is_month_end'] = df.date.dt.is_month_end.astype(int)df['is_quarter_start'] = df.date.dt.is_quarter_start.astype(int)df['is_quarter_end'] = df.date.dt.is_quarter_end.astype(int)df['is_quarter_end'] = df.date.dt.is_quarter_end.astype(int)return df

这里我使用的数据集为本地的数据集,需要可自行搜集下载,除date、open字段外,它还包含其他字段(不做说明):

from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
from pymysql import connect
from sqlalchemy import create_engine, textdef check_info(code):engine = create_engine('mysql+pymysql://root:152617@127.0.0.1:3306/stock_info')conn = engine.connect()result = conn.execute(text("SELECT * FROM stocks WHERE stock_code = " + code))conn.close()return result

将使用 date(日期 )和open(开盘价)字段来处理单变量时间序列。 

df = pd.DataFrame(check_info('000001'))[['date','open']]
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values(by='date')
df.head()

这里数据量不多,因为有很久没有运行脚本去自动更新数据库了,大概只有:

进行可视化:

plt.figure(figsize=(10,4))
plt.plot(df['date'], df["open"])
plt.xlabel("date")
plt.ylabel("open_price")
plt.show()

显而易见的下跌趋势。

应用函数来创建日期特征:

df = create_date_features(df)
df.head()

note:这里需要进行一个步骤。在上面模型中,有几个字段不应作为数字特征,而应作为分类特征。需要把它们转化为虚拟变量

to_dummy = ['weekday', 'month', 'quarter', 'year', 'day_of_month', 'week_of_year', 'day_of_week', 'hour_of_day']
df = pd.get_dummies(df, columns= to_dummy)

一个特征列表就创建好了。

创建滞后特征和未来特征

在自动回归模型中,回归变量是滞后值。可以使用 .shift(n) 来创建滞后特征。接下来,在数据集 ff 中创建三个滞后特征。

ff = df.copy()
ff['open-1'] = ff['open'].shift(1)
ff['openy-2'] = ff['open'].shift(2)
ff['open-3'] = ff['open'].shift(3)
ff.head()

编写一个 forloop 来创建多个滞后特征。下面将在不同的数据集 ff 中创建 5 个滞后变量:

ff = df.copy()def create_lagged(df, n_vars):# Use a forloopfor i in range(n_vars):# The name will be y-1, y-2, etc.name = ('open-%d' % (i+1))df[name] = df['open'].shift(i+1)return dfff = create_lagged(ff, 5)
ff.head()

显然,也可以将数值前移,使其成为未来的目标值,如下所示:

ff = df.copy()
ff['open+1'] = ff['open'].shift(-1)
ff['open+2'] = ff['open'].shift(-2)
ff['open+3'] = ff['open'].shift(-3)
ff.tail()

正式为建模数据 df 创建 25 个滞后变量:

df = create_lagged(df, 25)
df.columns

数据集中包含了['date', 'open', 'open-1', ..., 'open-25']的数据。在此基础上,可以进行一系列汇总统计,如过去 n 小时、n 天或 n 周的总和或平均值。

创建移动平均值

另外,可以创建1,3,5,7,10的移动平均值。

def roll_mean_features(df, windows):df = df.copy()for window in windows:df['mv_' + str(window)] = df['open'].transform(lambda x: x.shift(1).rolling(window=window, min_periods=1, win_type="triang").mean())# min_periods=1表示即使在窗口初期数据不足时也计算平均值# win_type='triang'指定了窗口的权重类型为三角形(Triangular)权重return df
df = roll_mean_features(df, [1, 3, 5, 7, 10])
df.tail()

监督学习框架用于提前预测。模型目标是 open,特征包括滞后项 open-1到open-25以及时间相关和移动平均变量。

该模型可以通过yt-1到yt-25产生下一期的yt,即提前一步预测。在现实应用中,多步预测也很常见,传统方法是建立n个模型来预测接下来的n期。

建立 LightGBM 预测模型

LightGBM是微软开发的梯度提升框架,它使用叶向树生长以提高准确性。相比之下,level-wise树会尝试在同一级别的分支上生长,看起来更平衡。由于其能够处理大型数据集和并行化训练,因此比其他提升算法更高效、更快速,同时内存占用更低。此外,它原生支持分类特征,无需进行单次编码。梯度提升模型是机器学习算法的一种,它将多个较弱的模型组合在一起,从而创建一个强大的预测模型。它的基本思想是迭代训练决策树,每棵树都试图纠正前一棵树所犯的错误。最终的预测结果是所有决策树预测结果的总和。梯度提升模型特别适用于处理复杂的数据集,可以处理大量特征和特征之间的交互,并且对过度拟合也很稳健,同时能够处理缺失值。常用的算法有梯度提升机(GBM)、XGB 和 LightGBM。

划分训练和测试集

将时间序列切割成 "实时" 数据作为训练数据,"非实时" 数据作为测试数据:

from datetime import timedelta
# Count the days
num_days = (df['date'].max() - df['date'].min()).days
# reserve 20% for out-of-time
oot = num_days * 0.2 
# Get the cutdate
cutdate = df['date'].max() - timedelta(days = oot)# Create the training data
train = df.loc[(df['date'] <= cutdate), :]
print("Training data: from", train['date'].min(), "to", train['date'].max())# Create the test data
test = df.loc[(df['date'] > cutdate), :]
print("Test data: from", test['date'].min(), "to", test['date'].max())

LightGBM 建模

LightGBM 有许多超参数可以调整。可指定关键超参数:

import lightgbm as lgb
lgb_params = {# 平均绝对误差'metric': {'mae'}, # 树中树叶的数量'num_leaves': 6, # 10以上训练效果才比较好# 学习日期'learning_rate': 0.02, # 随机选取 80% 的特征到训练              'feature_fraction': 0.8, # 树的最大深度'max_depth':5,# 忽略训练进度(不显示任何内容)'verbose': 0,# 提升迭代次数'num_boost_round': 150,# 如果精度没有提高,就停止训练'early_stopping_rounds': 200,# 使用计算机上的所有内核'nthread': -1}

LightGBM 有一个".Dataset()"代码类,用于打包目标变量、回归变量和数据。如下所示,操作非常简单。

train = train.dropna()Y_train = train[['open']]
X_train = train[cols]
Y_test = test[['open']]
X_test = test[cols]from sklearn import preprocessing
lbl = preprocessing.LabelEncoder()
Y_train['open'] = lbl.fit_transform(Y_train['open'].astype(float))
Y_test['open'] = lbl.fit_transform(Y_test['open'].astype(float))
for c in cols:X_train[c] = lbl.fit_transform(X_train[c].astype(float)) #将提示的包含错误数据类型这一列进行转换X_test[c] = lbl.fit_transform(X_test[c].astype(float))# Use the Dataset class of lightGBM
lgbtrain = lgb.Dataset(data=X_train, label=Y_train, feature_name=cols)
lgbtest = lgb.Dataset(data=X_test, label=Y_test, reference=lgbtrain, feature_name=cols)model = lgb.train(lgb_params, lgbtrain,valid_sets=[lgbtrain, lgbtest],num_boost_round=1000)

在这里需要注意你的数据特征或者label里面有没有NA,否则会出现报错:pandas dtype only support int float bool,检查数据是否是object

其次如果出现warm:-inf意味着可能你的数据过于稀疏(数据中特征的分布非常不均匀,或者特征值的范围很小,可能导致分割增益为负);特征质量差(某些特征可能对模型预测没有帮助,或者特征与目标变量的关联性太弱);参数设置(min_data_in_leaf(叶子节点最小样本数)或min_gain_to_split(最小增益阈值)设置得过高,导致模型在寻找分割时过于保守);需要进行参数重新设定

预测准确性评估

完成后,就可以得出训练数据和测试数据的预测值,并评估预测准确度。使用标准指标平均绝对百分比误差 (MAPE) 来评估预测准确度。MAPE 是绝对百分比误差的平均值,10% 的 MAPE 意味着预测值和实际值之间的平均偏差为 10%。

from sklearn.metrics import mean_absolute_percentage_error
y_pred_train = model.predict(X_train)
y_pred_test = model.predict(X_test)
mean_absolute_percentage_error(Y_test, y_pred_test)

此处数据量过少,提取的特征很勉强,训练效果不好,就不放了。

实际值与预测值可视化

# 将预测值添加到训练期
train_pred = train.copy()
train_pred['open_pred_train'] = y_pred_train# 将预测值添加到测试期
test_pred = test.copy()
test_pred['open_pred_test'] = y_pred_test
print([train_pred.shape, test_pred.shape])# 合并训练期和测试期
actual_pred = pd.concat([train_pred, test_pred], axis=0)
actual_pred.shape# 用蓝色绘制实际值
# 用橙色绘制训练期的预测值
# 用绿色标出测试期的预测值
plt.figure(figsize=(10,4))
plt.plot(actual_pred['date'], actual_pred[["open",'open_pred_train','open_pred_test']])
plt.xlabel("Date")
plt.ylabel("Actual vs. Predictions")
plt.show()

 不忍直视

模型可解释性

基于树的模型的优势之一是其可视性。可以通过变量重要性图直观地看到特征对预测的影响。

lgb.plot_importance(model, max_num_features=20, figsize=(10, 10),importance_type="gain")
plt.show()

特征重要性图显示,影响最大的三个变量是 *、*和*。 毫无解释力。变更数据重新检验即可。

这里探讨了单变量时间序列特征的创建方法,以及如何将其纳入基于树的监督学习框架中。利用 lightGBM 模型进行了一步预测,并展示了如何利用变量显著图提高模型可解释性。

对于timeseries predict进一步还有用LSTM进行分析。

这篇关于基于树的时间序列预测(LGBM)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/966001

相关文章

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

MySQL按时间维度对亿级数据表进行平滑分表

《MySQL按时间维度对亿级数据表进行平滑分表》本文将以一个真实的4亿数据表分表案例为基础,详细介绍如何在不影响线上业务的情况下,完成按时间维度分表的完整过程,感兴趣的小伙伴可以了解一下... 目录引言一、为什么我们需要分表1.1 单表数据量过大的问题1.2 分表方案选型二、分表前的准备工作2.1 数据评估

MySQL中DATE_FORMAT时间函数的使用小结

《MySQL中DATE_FORMAT时间函数的使用小结》本文主要介绍了MySQL中DATE_FORMAT时间函数的使用小结,用于格式化日期/时间字段,可提取年月、统计月份数据、精确到天,对大家的学习或... 目录前言DATE_FORMAT时间函数总结前言mysql可以使用DATE_FORMAT获取日期字段

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

Python标准库datetime模块日期和时间数据类型解读

《Python标准库datetime模块日期和时间数据类型解读》文章介绍Python中datetime模块的date、time、datetime类,用于处理日期、时间及日期时间结合体,通过属性获取时间... 目录Datetime常用类日期date类型使用时间 time 类型使用日期和时间的结合体–日期时间(

Java获取当前时间String类型和Date类型方式

《Java获取当前时间String类型和Date类型方式》:本文主要介绍Java获取当前时间String类型和Date类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Java获取当前时间String和Date类型String类型和Date类型输出结果总结Java获取

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

Spring的RedisTemplate的json反序列泛型丢失问题解决

《Spring的RedisTemplate的json反序列泛型丢失问题解决》本文主要介绍了SpringRedisTemplate中使用JSON序列化时泛型信息丢失的问题及其提出三种解决方案,可以根据性... 目录背景解决方案方案一方案二方案三总结背景在使用RedisTemplate操作redis时我们针对

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间