如何使用LSTM结合历史与当前特征预测?含DataFrame重塑咨询
嘿,这两个问题都是时序预测里非常典型的场景,我来给你拆解清楚~
LSTM的核心优势是捕捉时序数据的时间依赖关系,而你提到的“当前/未来特征”(比如次日要执行的折扣)属于非时序的条件特征,我们需要把这两类特征合理整合,让模型同时学习时序规律和条件约束。具体步骤如下:
第一步:明确特征分类
先把特征分成两类,避免混淆:- 时序历史特征:过去N天的
unit_sales(销量)、Discount(折扣)——这部分是随时间变化的序列,LSTM会学习它们的趋势、周期等规律。 - 非时序条件特征:次日将要执行的
Discount——这部分是每个预测样本对应的“已知条件”,因为你提前知道次日的折扣,所以可以作为附加特征输入模型。
- 时序历史特征:过去N天的
第二步:构造适配LSTM的输入数据
LSTM的标准输入是三维张量:(样本数量, 时间步长, 特征维度)。针对两类特征,有两种常用的整合方式:- 将条件特征拼接在每个时间步上:把次日折扣加到过去3天每个时间步的特征里,比如每个时间步的特征从
[销量, 当日折扣]变成[销量, 当日折扣, 次日折扣],这样输入张量形状为(样本数, 3, 3)。这种方式简单直接,适合条件特征对整个时序都有影响的场景。 - LSTM编码后拼接条件特征:先用LSTM处理历史时序特征,得到一个包含时序信息的编码向量,再把次日折扣和这个向量拼接,通过全连接层输出预测结果。这种方式更灵活,能区分时序规律和条件特征的独立影响。
- 将条件特征拼接在每个时间步上:把次日折扣加到过去3天每个时间步的特征里,比如每个时间步的特征从
第三步:模型构建与训练
用TensorFlow/Keras举个简单的伪代码示例:# 方式1:条件特征拼接在每个时间步 from tensorflow.keras import Input, Model from tensorflow.keras.layers import LSTM, Dense input_layer = Input(shape=(3, 3)) # 3个时间步,每个步3个特征 lstm_layer = LSTM(64)(input_layer) output_layer = Dense(1)(lstm_layer) model = Model(inputs=input_layer, outputs=output_layer) model.compile(optimizer='adam', loss='mse') # 方式2:LSTM后拼接条件特征 seq_input = Input(shape=(3, 2)) # 时序特征:3步,每步2个特征(销量、当日折扣) lstm_out = LSTM(64)(seq_input) static_input = Input(shape=(1,)) # 条件特征:次日折扣 concat = tf.keras.layers.Concatenate()([lstm_out, static_input]) dense = Dense(32, activation='relu')(concat) output_layer = Dense(1)(dense) model = Model(inputs=[seq_input, static_input], outputs=output_layer) model.compile(optimizer='adam', loss='mse')训练时,方式2需要把时序数据和条件数据分开传入模型。
第四步:预测阶段
预测时,要和训练时的输入结构一致:比如要预测某一天的销量,先取出过去3天的销量和折扣组成时序序列,再加上已知的次日折扣,输入模型即可得到预测结果。
首先要确保你的DataFrame是按日期升序排列的,这是时序处理的基础。假设你的原始DataFrame包含date、price、marked_price、Discount、unit_sales列,下面提供两种重塑方式:
方式1:生成扁平特征列(适合数据清洗和可视化)
这种方式把所有需要的特征展开成单独的列,方便你查看每个样本的完整信息:
import pandas as pd # 1. 按日期排序 df = df.sort_values('date').reset_index(drop=True) # 2. 生成过去3天的销量和折扣特征 for day in range(1, 4): df[f'unit_sales_{day}d_ago'] = df['unit_sales'].shift(day) df[f'Discount_{day}d_ago'] = df['Discount'].shift(day) # 3. 生成次日的折扣(条件特征)和目标销量(要预测的y) df['next_day_Discount'] = df['Discount'].shift(-1) df['next_day_unit_sales'] = df['unit_sales'].shift(-1) # 4. 丢弃缺失值(前3天没有足够历史数据,最后一行没有次日数据) df_clean = df.dropna().reset_index(drop=True)
此时df_clean的每一行就是一个完整的样本:包含过去3天的销量、折扣,次日的折扣,以及要预测的次日销量。
方式2:直接生成LSTM所需的3D张量(适合直接喂入模型)
如果想跳过扁平列的步骤,直接生成模型需要的输入张量,可以写一个简单的函数:
import numpy as np def create_lstm_sequences(df, seq_length=3): # 提取时序特征、条件特征和目标变量 seq_features = df[['unit_sales', 'Discount']].values static_features = df['Discount'].shift(-1).values target = df['unit_sales'].shift(-1).values X_seq, X_static, y = [], [], [] # 遍历生成每个样本 for i in range(len(df) - seq_length): # 取过去seq_length天的时序特征 X_seq.append(seq_features[i:i+seq_length]) # 取对应样本的次日折扣 X_static.append(static_features[i+seq_length-1]) # 取对应样本的目标销量 y.append(target[i+seq_length-1]) # 转换为numpy数组,适配LSTM输入格式 X_seq = np.array(X_seq) X_static = np.array(X_static).reshape(-1, 1) # 转为(样本数, 1)的形状 y = np.array(y) return X_seq, X_static, y # 生成模型输入数据 X_seq, X_static, y = create_lstm_sequences(df)
X_seq是形状为(样本数, 3, 2)的时序输入,X_static是形状为(样本数, 1)的次日折扣特征,y是目标销量,刚好可以对应上面方式2的模型输入。
内容的提问来源于stack exchange,提问作者Aaron_Geng




