提示词

1
2
df['收盘价']
继续使用黄金期货行情DataFrame,需要做的工作是,首先进行特征工程,将新提取的特征保存在DataFrame的新列中,然后使用线性回归模型基于新的特征训练模型,仍然需要使用滚动时间序列交叉验证法,最后要输出预测结果的MAPE,并将真实值和预测值进行可视化对比,请给出示例代码。

答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import pandas as pd
import numpy as np
import talib
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_percentage_error, mean_squared_error
from tqdm import tqdm

# =====================
# 1. 特征工程(参考[1][7][8])
# =====================
def create_features(df, target_col='收盘价'):
"""
创建技术指标和滞后特征
参数:
df: 包含日期索引和收盘价的DataFrame
target_col: 价格列名
返回:
添加新特征的DataFrame
"""
# 基础价格特征
for lag in [1, 2, 3, 5, 7, 10]:
df[f'Lag_{lag}'] = df[target_col].shift(lag)

# 技术指标(参考[1][7])
df['MA5'] = talib.SMA(df[target_col], timeperiod=5)
df['MA20'] = talib.SMA(df[target_col], timeperiod=20)
df['RSI'] = talib.RSI(df[target_col], timeperiod=14)
df['MACD'], df['MACD_Signal'], _ = talib.MACD(df[target_col])

# 波动率特征(参考[8])
df['Volatility_5'] = df[target_col].rolling(5).std()
df['Volatility_20'] = df[target_col].rolling(20).std()

# 价格变化特征
df['Price_Change'] = df[target_col].pct_change() * 100 # 百分比变化

# 目标变量:次日收盘价(参考[1])
df['Target'] = df[target_col].shift(-1)

return df.dropna()

# 假设df是包含日期索引和'收盘价'的DataFrame
# 应用特征工程
df = create_features(df.copy())

# =====================
# 2. 滚动时间序列交叉验证(参考[9][11])
# =====================
WINDOW_SIZE = 250 # 使用1年交易日作为滚动窗口(约250天)
features = df.drop(['Target', '收盘价'], axis=1).columns.tolist()

predictions = []
actuals = []
dates = []

print("开始滚动交叉验证...")
for i in tqdm(range(WINDOW_SIZE, len(df)), desc="滚动验证进度"):
# 划分训练集和测试集(严格按时间顺序)
train = df.iloc[i-WINDOW_SIZE:i-1] # 窗口内除最后一天
test = df.iloc[i-1:i] # 最后一天用于预测次日价格

# 准备数据
X_train = train[features]
y_train = train['Target']
X_test = test[features]
y_actual = df.iloc[i]['Target'] # 实际值(下一天的价格)

# 创建并训练线性回归模型(参考[10])
model = LinearRegression()
model.fit(X_train, y_train)

# 预测并存储结果
pred = model.predict(X_test)[0]
predictions.append(pred)
actuals.append(y_actual)
dates.append(df.index[i])

# 转换为DataFrame
results = pd.DataFrame({
'Date': dates,
'Actual': actuals,
'Predicted': predictions
}).set_index('Date')

# =====================
# 3. 评估指标计算(参考[1][7])
# =====================
def safe_mape(actual, pred):
"""处理零值的MAPE计算"""
actual = np.array(actual)
pred = np.array(pred)
mask = actual != 0 # 避免除零错误
return np.mean(np.abs((actual[mask] - pred[mask]) / actual[mask])) * 100

mape = safe_mape(results['Actual'], results['Predicted'])
rmse = np.sqrt(mean_squared_error(results['Actual'], results['Predicted']))

print("\n模型评估结果:")
print(f"MAPE: {mape:.2f}%")
print(f"RMSE: {rmse:.2f}")

# =====================
# 4. 可视化分析(参考[1][5])
# =====================
plt.figure(figsize=(16, 10))

# 1. 主价格曲线对比
plt.subplot(3, 1, 1)
plt.plot(results.index, results['Actual'], 'b-', label='实际价格', alpha=0.8, lw=1.5)
plt.plot(results.index, results['Predicted'], 'r--', label='预测价格', lw=1.2)
plt.fill_between(
results.index,
results['Predicted'] * 0.98,
results['Predicted'] * 1.02,
color='pink', alpha=0.3, label='±2%误差带'
)
plt.title(f'黄金期货价格预测对比 (MAPE={mape:.2f}%, RMSE={rmse:.2f})', fontsize=14)
plt.ylabel('价格', fontsize=12)
plt.legend(loc='upper left')
plt.grid(alpha=0.2)

# 2. 误差分布(百分比误差)
plt.subplot(3, 1, 2)
errors = (results['Predicted'] - results['Actual']) / results['Actual'] * 100
plt.bar(results.index, errors, color=np.where(errors >= 0, 'coral', 'lightseagreen'), alpha=0.7, width=0.8)
plt.axhline(0, color='black', linestyle='-', lw=0.8)
plt.title('每日预测误差分布(%)', fontsize=12)
plt.ylabel('百分比误差', fontsize=10)
plt.ylim(-15, 15) # 限制误差范围以便观察
plt.grid(alpha=0.2)

# 3. 累计误差分析
plt.subplot(3, 1, 3)
cumulative_error = (results['Predicted'].cumsum() - results['Actual'].cumsum()) / results['Actual'].cumsum() * 100
plt.plot(cumulative_error, 'g-', label='累计预测误差', lw=1.5)
plt.axhline(0, color='black', linestyle='--', lw=0.8)
plt.title('累计预测误差趋势', fontsize=12)
plt.ylabel('累计误差(%)', fontsize=10)
plt.xlabel('日期', fontsize=10)
plt.grid(alpha=0.2)
plt.legend()

plt.tight_layout()
plt.savefig('gold_price_forecast.png', dpi=300)
plt.show()

# =====================
# 5. 模型系数分析(参考[10])
# =====================
# 使用最后一次训练的模型分析特征重要性
coef_df = pd.DataFrame({
'Feature': features,
'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)

print("\nTop 10特征重要性:")
print(coef_df.head(10))

# 可视化特征系数
plt.figure(figsize=(12, 6))
plt.barh(coef_df['Feature'][:15], coef_df['Coefficient'][:15], color='teal')
plt.title('线性回归模型特征系数排名 (绝对值Top 15)', fontsize=14)
plt.xlabel('系数值', fontsize=12)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()

图像:

MAPE达到了0.9%,看来引入了不必要的特征。

添加滞后特征并训练模型

前面其实ai也直接添加了[1, 2, 3, 5, 7, 10],我们简化下提示词

提示词

1
2
3
4
5
6
7
8
# Python代码
# gold_df是黄金期货行情DataFrame
gold_df = pd.read_excel('黄金连续.xlsx')

#将日期设置为index,并只保留收盘价
gold_df.set_index('日期', inplace=True)

继续使用黄金期货行情DataFrame,需要做的工作是,首先进行特征工程,这次添加前5个交易日的收盘价作为滞后特征,将新提取的特征保存在DataFrame的新列中,然后基于新的特征训练线性回归模型,仍然需要使用滚动时间序列交叉验证法,最后要输出预测结果的MAPE,并将真实值和预测值进行可视化对比,请给出示例代码。

答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_percentage_error, mean_squared_error
from tqdm import tqdm

# =====================
# 1. 数据准备与特征工程
# =====================
# 假设gold_df已加载并设置日期索引
gold_df = pd.read_excel('黄金连续.xlsx')

#将日期设置为index,并只保留收盘价
gold_df.set_index('日期', inplace=True)
gold_df = gold_df[['收盘价']]
print("原始数据示例:")
print(gold_df.head())

# 创建滞后特征(前1-5个交易日的收盘价)
for lag in range(1, 6):
gold_df[f'Lag_{lag}'] = gold_df['收盘价'].shift(lag)

# 添加其他技术指标特征
gold_df['MA5'] = gold_df['收盘价'].rolling(window=5).mean() # 5日移动平均
gold_df['MA20'] = gold_df['收盘价'].rolling(window=20).mean() # 20日移动平均
gold_df['Price_Change'] = gold_df['收盘价'].pct_change() * 100 # 价格变化率

# 定义目标变量:下一天的收盘价
gold_df['Target'] = gold_df['收盘价'].shift(-1)

# 删除包含NaN的行
gold_df_clean = gold_df.dropna().copy()
print("\n特征工程后的数据示例:")
print(gold_df_clean.head())

# =====================
# 2. 滚动时间序列交叉验证
# =====================
WINDOW_SIZE = 250 # 使用1年交易日作为滚动窗口
features = ['收盘价', 'Lag_1', 'Lag_2', 'Lag_3', 'Lag_4', 'Lag_5', 'MA5', 'MA20', 'Price_Change']

predictions = []
actuals = []
dates = []

print("\n开始滚动交叉验证...")
for i in tqdm(range(WINDOW_SIZE, len(gold_df_clean)-1), desc="滚动预测进度"):
# 划分训练集和测试集(严格按时间顺序)
train_data = gold_df_clean.iloc[i-WINDOW_SIZE:i] # 窗口内的250个交易日
test_data = gold_df_clean.iloc[i:i+1] # 窗口外的下一个交易日

# 准备数据
X_train = train_data[features]
y_train = train_data['Target']
X_test = test_data[features]

# 创建并训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)

# 预测并存储结果
pred = model.predict(X_test)[0]
predictions.append(pred)
actuals.append(gold_df_clean.iloc[i+1]['收盘价']) # 实际值是下一天的收盘价
dates.append(gold_df_clean.index[i+1])

# =====================
# 3. 评估指标计算
# =====================
def safe_mape(actual, pred):
"""处理零值的MAPE计算"""
actual = np.array(actual)
pred = np.array(pred)
mask = actual != 0 # 避免除零错误
return np.mean(np.abs((actual[mask] - pred[mask]) / actual[mask])) * 100

mape = safe_mape(actuals, predictions)
rmse = np.sqrt(mean_squared_error(actuals, predictions))
print(f"\n模型评估结果:")
print(f"MAPE: {mape:.2f}%")
print(f"RMSE: {rmse:.2f}")

# =====================
# 4. 可视化分析
# =====================
plt.figure(figsize=(15, 8))

# 主价格曲线对比
plt.subplot(2, 1, 1)
plt.plot(dates, actuals, 'b-', label='实际价格', alpha=0.8, lw=1.5)
plt.plot(dates, predictions, 'r--', label='预测价格', lw=1.2)
plt.fill_between(dates,
np.array(predictions) * 0.98,
np.array(predictions) * 1.02,
color='pink', alpha=0.3, label='±2%误差带')
plt.title(f'黄金期货价格预测对比 (MAPE={mape:.2f}%)', fontsize=14)
plt.ylabel('价格', fontsize=12)
plt.legend(loc='upper left')
plt.grid(alpha=0.2)

# 误差分布分析
plt.subplot(2, 1, 2)
errors = [(p - a)/a * 100 for p, a in zip(predictions, actuals)]
plt.bar(dates, errors, color=np.where(np.array(errors) >= 0, 'tomato', 'limegreen'), alpha=0.6)
plt.axhline(0, color='black', linestyle='-', lw=0.8)
plt.title('每日预测误差分布(%)', fontsize=12)
plt.ylabel('百分比误差', fontsize=10)
plt.ylim(-15, 15) # 限制误差范围
plt.grid(alpha=0.2)

plt.tight_layout()
plt.savefig('gold_price_forecast.png', dpi=300)
plt.show()

# =====================
# 5. 模型系数分析
# =====================
# 使用最后一次训练的模型分析特征重要性
feature_importance = pd.Series(
model.coef_,
index=features
).sort_values(key=abs, ascending=False)

print("\n特征重要性排名:")
print(feature_importance)

# 可视化特征系数
plt.figure(figsize=(12, 6))
feature_importance.plot(kind='barh', color='teal')
plt.title('线性回归模型特征系数', fontsize=14)
plt.xlabel('系数值', fontsize=12)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()

结果:

MAPE 0.62% 稍微好些了。