AI协助进行特征工程
编辑
0
2025-06-27

提示词
df['收盘价']
继续使用黄金期货行情DataFrame,需要做的工作是,首先进行特征工程,将新提取的特征保存在DataFrame的新列中,然后使用线性回归模型基于新的特征训练模型,仍然需要使用滚动时间序列交叉验证法,最后要输出预测结果的MAPE,并将真实值和预测值进行可视化对比,请给出示例代码。
答案
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],我们简化下提示词
提示词
# Python代码
# gold_df是黄金期货行情DataFrame
gold_df = pd.read_excel('黄金连续.xlsx')
#将日期设置为index,并只保留收盘价
gold_df.set_index('日期', inplace=True)
继续使用黄金期货行情DataFrame,需要做的工作是,首先进行特征工程,这次添加前5个交易日的收盘价作为滞后特征,将新提取的特征保存在DataFrame的新列中,然后基于新的特征训练线性回归模型,仍然需要使用滚动时间序列交叉验证法,最后要输出预测结果的MAPE,并将真实值和预测值进行可视化对比,请给出示例代码。
答案
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% 稍微好些了。
- 0
- 0
-
赞助
支付宝
微信
-
分享