石油焦日度预测调试

This commit is contained in:
jingboyitiji 2025-03-20 16:29:25 +08:00
parent 313e9e229d
commit 7cf3cda87a
2 changed files with 533 additions and 220 deletions

View File

@ -3,7 +3,7 @@
from lib.dataread import * from lib.dataread import *
from config_shiyoujiao_lvyong import * from config_shiyoujiao_lvyong import *
from lib.tools import SendMail, exception_logger from lib.tools import SendMail, exception_logger
from models.nerulforcastmodels import ex_Model, model_losss, model_losss_juxiting, brent_export_pdf, tansuanli_export_pdf, pp_export_pdf, model_losss_juxiting from models.nerulforcastmodels import model_losss, shiyoujiao_lvyong_export_pdf
import datetime import datetime
import torch import torch
torch.set_float32_matmul_precision("high") torch.set_float32_matmul_precision("high")
@ -173,228 +173,228 @@ def predict_main():
返回: 返回:
None None
""" """
end_time = global_config['end_time'] # end_time = global_config['end_time']
# 获取数据 # # 获取数据
if is_eta: # if is_eta:
logger.info('从eta获取数据...') # logger.info('从eta获取数据...')
signature = BinanceAPI(APPID, SECRET) # signature = BinanceAPI(APPID, SECRET)
etadata = EtaReader(signature=signature, # etadata = EtaReader(signature=signature,
classifylisturl=global_config['classifylisturl'], # classifylisturl=global_config['classifylisturl'],
classifyidlisturl=global_config['classifyidlisturl'], # classifyidlisturl=global_config['classifyidlisturl'],
edbcodedataurl=global_config['edbcodedataurl'], # edbcodedataurl=global_config['edbcodedataurl'],
edbcodelist=global_config['edbcodelist'], # edbcodelist=global_config['edbcodelist'],
edbdatapushurl=global_config['edbdatapushurl'], # edbdatapushurl=global_config['edbdatapushurl'],
edbdeleteurl=global_config['edbdeleteurl'], # edbdeleteurl=global_config['edbdeleteurl'],
edbbusinessurl=global_config['edbbusinessurl'], # edbbusinessurl=global_config['edbbusinessurl'],
classifyId=global_config['ClassifyId'], # classifyId=global_config['ClassifyId'],
) # )
df_zhibiaoshuju, df_zhibiaoliebiao = etadata.get_eta_api_shiyoujiao_lvyong_data( # df_zhibiaoshuju, df_zhibiaoliebiao = etadata.get_eta_api_shiyoujiao_lvyong_data(
data_set=data_set, dataset=dataset) # 原始数据,未处理 # data_set=data_set, dataset=dataset) # 原始数据,未处理
if is_market: # if is_market:
logger.info('从市场信息平台获取数据...') # logger.info('从市场信息平台获取数据...')
try: # try:
# 如果是测试环境最高价最低价取excel文档 # # 如果是测试环境最高价最低价取excel文档
if server_host == '192.168.100.53': # if server_host == '192.168.100.53':
logger.info('从excel文档获取最高价最低价') # logger.info('从excel文档获取最高价最低价')
df_zhibiaoshuju = get_high_low_data(df_zhibiaoshuju) # df_zhibiaoshuju = get_high_low_data(df_zhibiaoshuju)
else: # else:
logger.info('从市场信息平台获取数据') # logger.info('从市场信息平台获取数据')
df_zhibiaoshuju = get_market_data( # df_zhibiaoshuju = get_market_data(
end_time, df_zhibiaoshuju) # end_time, df_zhibiaoshuju)
except: # except:
logger.info('最高最低价拼接失败') # logger.info('最高最低价拼接失败')
# 保存到xlsx文件的sheet表 # # 保存到xlsx文件的sheet表
with pd.ExcelWriter(os.path.join(dataset, data_set)) as file: # with pd.ExcelWriter(os.path.join(dataset, data_set)) as file:
df_zhibiaoshuju.to_excel(file, sheet_name='指标数据', index=False) # df_zhibiaoshuju.to_excel(file, sheet_name='指标数据', index=False)
df_zhibiaoliebiao.to_excel(file, sheet_name='指标列表', index=False) # df_zhibiaoliebiao.to_excel(file, sheet_name='指标列表', index=False)
# 数据处理 # # 数据处理
df = datachuli(df_zhibiaoshuju, df_zhibiaoliebiao, y=global_config['y'], dataset=dataset, add_kdj=add_kdj, is_timefurture=is_timefurture, # df = datachuli(df_zhibiaoshuju, df_zhibiaoliebiao, y=global_config['y'], dataset=dataset, add_kdj=add_kdj, is_timefurture=is_timefurture,
end_time=end_time) # end_time=end_time)
else: # else:
# 读取数据 # # 读取数据
logger.info('读取本地数据:' + os.path.join(dataset, data_set)) # logger.info('读取本地数据:' + os.path.join(dataset, data_set))
df, df_zhibiaoliebiao = getdata(filename=os.path.join(dataset, data_set), y=y, dataset=dataset, add_kdj=add_kdj, # df, df_zhibiaoliebiao = getdata(filename=os.path.join(dataset, data_set), y=y, dataset=dataset, add_kdj=add_kdj,
is_timefurture=is_timefurture, end_time=end_time) # 原始数据,未处理 # is_timefurture=is_timefurture, end_time=end_time) # 原始数据,未处理
# 更改预测列名称 # # 更改预测列名称
df.rename(columns={y: 'y'}, inplace=True) # df.rename(columns={y: 'y'}, inplace=True)
if is_edbnamelist: # if is_edbnamelist:
df = df[edbnamelist] # df = df[edbnamelist]
df.to_csv(os.path.join(dataset, '指标数据.csv'), index=False) # df.to_csv(os.path.join(dataset, '指标数据.csv'), index=False)
# 保存最新日期的y值到数据库 # # 保存最新日期的y值到数据库
# 取第一行数据存储到数据库中 # # 取第一行数据存储到数据库中
first_row = df[['ds', 'y']].tail(1) # first_row = df[['ds', 'y']].tail(1)
# 判断y的类型是否为float # # 判断y的类型是否为float
if not isinstance(first_row['y'].values[0], float): # if not isinstance(first_row['y'].values[0], float):
logger.info(f'{end_time}预测目标数据为空,跳过') # logger.info(f'{end_time}预测目标数据为空,跳过')
return None # return None
# 将最新真实值保存到数据库 # # 将最新真实值保存到数据库
if not sqlitedb.check_table_exists('trueandpredict'): # if not sqlitedb.check_table_exists('trueandpredict'):
first_row.to_sql('trueandpredict', sqlitedb.connection, index=False) # first_row.to_sql('trueandpredict', sqlitedb.connection, index=False)
else: # else:
for row in first_row.itertuples(index=False): # for row in first_row.itertuples(index=False):
row_dict = row._asdict() # row_dict = row._asdict()
config.logger.info(f'要保存的真实值:{row_dict}') # config.logger.info(f'要保存的真实值:{row_dict}')
# 判断ds是否为字符串类型,如果不是则转换为字符串类型 # # 判断ds是否为字符串类型,如果不是则转换为字符串类型
if isinstance(row_dict['ds'], (pd.Timestamp, datetime.datetime)): # if isinstance(row_dict['ds'], (pd.Timestamp, datetime.datetime)):
row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d') # row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d')
elif not isinstance(row_dict['ds'], str): # elif not isinstance(row_dict['ds'], str):
try: # try:
row_dict['ds'] = pd.to_datetime( # row_dict['ds'] = pd.to_datetime(
row_dict['ds']).strftime('%Y-%m-%d') # row_dict['ds']).strftime('%Y-%m-%d')
except: # except:
logger.warning(f"无法解析的时间格式: {row_dict['ds']}") # logger.warning(f"无法解析的时间格式: {row_dict['ds']}")
# row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d') # # row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d')
# row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d %H:%M:%S') # # row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d %H:%M:%S')
check_query = sqlitedb.select_data( # check_query = sqlitedb.select_data(
'trueandpredict', where_condition=f"ds = '{row.ds}'") # 'trueandpredict', where_condition=f"ds = '{row.ds}'")
if len(check_query) > 0: # if len(check_query) > 0:
set_clause = ", ".join( # set_clause = ", ".join(
[f"{key} = '{value}'" for key, value in row_dict.items()]) # [f"{key} = '{value}'" for key, value in row_dict.items()])
sqlitedb.update_data( # sqlitedb.update_data(
'trueandpredict', set_clause, where_condition=f"ds = '{row.ds}'") # 'trueandpredict', set_clause, where_condition=f"ds = '{row.ds}'")
continue # continue
sqlitedb.insert_data('trueandpredict', tuple( # sqlitedb.insert_data('trueandpredict', tuple(
row_dict.values()), columns=row_dict.keys()) # row_dict.values()), columns=row_dict.keys())
# 更新accuracy表的y值 # # 更新accuracy表的y值
if not sqlitedb.check_table_exists('accuracy'): # if not sqlitedb.check_table_exists('accuracy'):
pass # pass
else: # else:
update_y = sqlitedb.select_data( # update_y = sqlitedb.select_data(
'accuracy', where_condition="y is null") # 'accuracy', where_condition="y is null")
if len(update_y) > 0: # if len(update_y) > 0:
logger.info('更新accuracy表的y值') # logger.info('更新accuracy表的y值')
# 找到update_y 中ds且df中的y的行 # # 找到update_y 中ds且df中的y的行
update_y = update_y[update_y['ds'] <= end_time] # update_y = update_y[update_y['ds'] <= end_time]
logger.info(f'要更新y的信息{update_y}') # logger.info(f'要更新y的信息{update_y}')
# try: # # try:
for row in update_y.itertuples(index=False): # for row in update_y.itertuples(index=False):
try: # try:
row_dict = row._asdict() # row_dict = row._asdict()
yy = df[df['ds'] == row_dict['ds']]['y'].values[0] # yy = df[df['ds'] == row_dict['ds']]['y'].values[0]
LOW = df[df['ds'] == row_dict['ds']]['Brentzdj'].values[0] # LOW = df[df['ds'] == row_dict['ds']]['Brentzdj'].values[0]
HIGH = df[df['ds'] == row_dict['ds']]['Brentzgj'].values[0] # HIGH = df[df['ds'] == row_dict['ds']]['Brentzgj'].values[0]
sqlitedb.update_data( # sqlitedb.update_data(
'accuracy', f"y = {yy},LOW_PRICE = {LOW},HIGH_PRICE = {HIGH}", where_condition=f"ds = '{row_dict['ds']}'") # 'accuracy', f"y = {yy},LOW_PRICE = {LOW},HIGH_PRICE = {HIGH}", where_condition=f"ds = '{row_dict['ds']}'")
except: # except:
logger.info(f'更新accuracy表的y值失败{row_dict}') # logger.info(f'更新accuracy表的y值失败{row_dict}')
# except Exception as e: # # except Exception as e:
# logger.info(f'更新accuracy表的y值失败{e}') # # logger.info(f'更新accuracy表的y值失败{e}')
# 判断当前日期是不是周一 # # 判断当前日期是不是周一
is_weekday = datetime.datetime.now().weekday() == 0 # is_weekday = datetime.datetime.now().weekday() == 0
if is_weekday: # if is_weekday:
logger.info('今天是周一,更新预测模型') # logger.info('今天是周一,更新预测模型')
# 计算最近60天预测残差最低的模型名称 # # 计算最近60天预测残差最低的模型名称
model_results = sqlitedb.select_data( # model_results = sqlitedb.select_data(
'trueandpredict', order_by="ds DESC", limit="60") # 'trueandpredict', order_by="ds DESC", limit="60")
# 删除空值率为90%以上的列 # # 删除空值率为90%以上的列
if len(model_results) > 10: # if len(model_results) > 10:
model_results = model_results.dropna( # model_results = model_results.dropna(
thresh=len(model_results)*0.1, axis=1) # thresh=len(model_results)*0.1, axis=1)
# 删除空行 # # 删除空行
model_results = model_results.dropna() # model_results = model_results.dropna()
modelnames = model_results.columns.to_list()[2:-1] # modelnames = model_results.columns.to_list()[2:-1]
for col in model_results[modelnames].select_dtypes(include=['object']).columns: # for col in model_results[modelnames].select_dtypes(include=['object']).columns:
model_results[col] = model_results[col].astype(np.float32) # model_results[col] = model_results[col].astype(np.float32)
# 计算每个预测值与真实值之间的偏差率 # # 计算每个预测值与真实值之间的偏差率
for model in modelnames: # for model in modelnames:
model_results[f'{model}_abs_error_rate'] = abs( # model_results[f'{model}_abs_error_rate'] = abs(
model_results['y'] - model_results[model]) / model_results['y'] # model_results['y'] - model_results[model]) / model_results['y']
# 获取每行对应的最小偏差率值 # # 获取每行对应的最小偏差率值
min_abs_error_rate_values = model_results.apply( # min_abs_error_rate_values = model_results.apply(
lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].min(), axis=1) # lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].min(), axis=1)
# 获取每行对应的最小偏差率值对应的列名 # # 获取每行对应的最小偏差率值对应的列名
min_abs_error_rate_column_name = model_results.apply( # min_abs_error_rate_column_name = model_results.apply(
lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].idxmin(), axis=1) # lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].idxmin(), axis=1)
# 将列名索引转换为列名 # # 将列名索引转换为列名
min_abs_error_rate_column_name = min_abs_error_rate_column_name.map( # min_abs_error_rate_column_name = min_abs_error_rate_column_name.map(
lambda x: x.split('_')[0]) # lambda x: x.split('_')[0])
# 取出现次数最多的模型名称 # # 取出现次数最多的模型名称
most_common_model = min_abs_error_rate_column_name.value_counts().idxmax() # most_common_model = min_abs_error_rate_column_name.value_counts().idxmax()
logger.info(f"最近60天预测残差最低的模型名称{most_common_model}") # logger.info(f"最近60天预测残差最低的模型名称{most_common_model}")
# 保存结果到数据库 # # 保存结果到数据库
if not sqlitedb.check_table_exists('most_model'): # if not sqlitedb.check_table_exists('most_model'):
sqlitedb.create_table( # sqlitedb.create_table(
'most_model', columns="ds datetime, most_common_model TEXT") # 'most_model', columns="ds datetime, most_common_model TEXT")
sqlitedb.insert_data('most_model', (datetime.datetime.now().strftime( # sqlitedb.insert_data('most_model', (datetime.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S'), most_common_model,), columns=('ds', 'most_common_model',)) # '%Y-%m-%d %H:%M:%S'), most_common_model,), columns=('ds', 'most_common_model',))
try: # try:
if is_weekday: # if is_weekday:
# if True: # # if True:
logger.info('今天是周一,发送特征预警') # logger.info('今天是周一,发送特征预警')
# 上传预警信息到数据库 # # 上传预警信息到数据库
warning_data_df = df_zhibiaoliebiao.copy() # warning_data_df = df_zhibiaoliebiao.copy()
warning_data_df = warning_data_df[warning_data_df['停更周期'] > 3][[ # warning_data_df = warning_data_df[warning_data_df['停更周期'] > 3][[
'指标名称', '指标id', '频度', '更新周期', '指标来源', '最后更新时间', '停更周期']] # '指标名称', '指标id', '频度', '更新周期', '指标来源', '最后更新时间', '停更周期']]
# 重命名列名 # # 重命名列名
warning_data_df = warning_data_df.rename(columns={'指标名称': 'INDICATOR_NAME', '指标id': 'INDICATOR_ID', '频度': 'FREQUENCY', # warning_data_df = warning_data_df.rename(columns={'指标名称': 'INDICATOR_NAME', '指标id': 'INDICATOR_ID', '频度': 'FREQUENCY',
'更新周期': 'UPDATE_FREQUENCY', '指标来源': 'DATA_SOURCE', '最后更新时间': 'LAST_UPDATE_DATE', '停更周期': 'UPDATE_SUSPENSION_CYCLE'}) # '更新周期': 'UPDATE_FREQUENCY', '指标来源': 'DATA_SOURCE', '最后更新时间': 'LAST_UPDATE_DATE', '停更周期': 'UPDATE_SUSPENSION_CYCLE'})
from sqlalchemy import create_engine # from sqlalchemy import create_engine
import urllib # import urllib
global password # global password
if '@' in password: # if '@' in password:
password = urllib.parse.quote_plus(password) # password = urllib.parse.quote_plus(password)
engine = create_engine( # engine = create_engine(
f'mysql+pymysql://{dbusername}:{password}@{host}:{port}/{dbname}') # f'mysql+pymysql://{dbusername}:{password}@{host}:{port}/{dbname}')
warning_data_df['WARNING_DATE'] = datetime.date.today().strftime( # warning_data_df['WARNING_DATE'] = datetime.date.today().strftime(
"%Y-%m-%d %H:%M:%S") # "%Y-%m-%d %H:%M:%S")
warning_data_df['TENANT_CODE'] = 'T0004' # warning_data_df['TENANT_CODE'] = 'T0004'
# 插入数据之前查询表数据然后新增id列 # # 插入数据之前查询表数据然后新增id列
existing_data = pd.read_sql(f"SELECT * FROM {table_name}", engine) # existing_data = pd.read_sql(f"SELECT * FROM {table_name}", engine)
if not existing_data.empty: # if not existing_data.empty:
max_id = existing_data['ID'].astype(int).max() # max_id = existing_data['ID'].astype(int).max()
warning_data_df['ID'] = range( # warning_data_df['ID'] = range(
max_id + 1, max_id + 1 + len(warning_data_df)) # max_id + 1, max_id + 1 + len(warning_data_df))
else: # else:
warning_data_df['ID'] = range(1, 1 + len(warning_data_df)) # warning_data_df['ID'] = range(1, 1 + len(warning_data_df))
warning_data_df.to_sql( # warning_data_df.to_sql(
table_name, con=engine, if_exists='append', index=False) # table_name, con=engine, if_exists='append', index=False)
if is_update_warning_data: # if is_update_warning_data:
upload_warning_info(len(warning_data_df)) # upload_warning_info(len(warning_data_df))
except: # except:
logger.info('上传预警信息到数据库失败') # logger.info('上传预警信息到数据库失败')
if is_corr: # if is_corr:
df = corr_feature(df=df) # df = corr_feature(df=df)
df1 = df.copy() # 备份一下后面特征筛选完之后加入ds y 列用 # df1 = df.copy() # 备份一下后面特征筛选完之后加入ds y 列用
logger.info(f"开始训练模型...") # logger.info(f"开始训练模型...")
row, col = df.shape # row, col = df.shape
now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # now = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
ex_Model(df, # ex_Model(df,
horizon=global_config['horizon'], # horizon=global_config['horizon'],
input_size=global_config['input_size'], # input_size=global_config['input_size'],
train_steps=global_config['train_steps'], # train_steps=global_config['train_steps'],
val_check_steps=global_config['val_check_steps'], # val_check_steps=global_config['val_check_steps'],
early_stop_patience_steps=global_config['early_stop_patience_steps'], # early_stop_patience_steps=global_config['early_stop_patience_steps'],
is_debug=global_config['is_debug'], # is_debug=global_config['is_debug'],
dataset=global_config['dataset'], # dataset=global_config['dataset'],
is_train=global_config['is_train'], # is_train=global_config['is_train'],
is_fivemodels=global_config['is_fivemodels'], # is_fivemodels=global_config['is_fivemodels'],
val_size=global_config['val_size'], # val_size=global_config['val_size'],
test_size=global_config['test_size'], # test_size=global_config['test_size'],
settings=global_config['settings'], # settings=global_config['settings'],
now=now, # now=now,
etadata=global_config['etadata'], # etadata=global_config['etadata'],
modelsindex=global_config['modelsindex'], # modelsindex=global_config['modelsindex'],
data=data, # data=data,
is_eta=global_config['is_eta'], # is_eta=global_config['is_eta'],
end_time=global_config['end_time'], # end_time=global_config['end_time'],
) # )
logger.info('模型训练完成') # logger.info('模型训练完成')
logger.info('训练数据绘图ing') logger.info('训练数据绘图ing')
model_results3 = model_losss(sqlitedb, end_time=end_time) model_results3 = model_losss(sqlitedb, end_time=end_time)
@ -403,15 +403,15 @@ def predict_main():
# 模型报告 # 模型报告
logger.info('制作报告ing') logger.info('制作报告ing')
title = f'{settings}--{end_time}-预测报告' # 报告标题 title = f'{settings}--{end_time}-预测报告' # 报告标题
reportname = f'Brent原油大模型日度预测--{end_time}.pdf' # 报告文件名 reportname = f'石油焦铝用大模型日度预测--{end_time}.pdf' # 报告文件名
reportname = reportname.replace(':', '-') # 替换冒号 reportname = reportname.replace(':', '-') # 替换冒号
brent_export_pdf(dataset=dataset, num_models=5 if is_fivemodels else 22, time=end_time, shiyoujiao_lvyong_export_pdf(dataset=dataset, num_models=5 if is_fivemodels else 22, time=end_time,
reportname=reportname, sqlitedb=sqlitedb), reportname=reportname, sqlitedb=sqlitedb),
logger.info('制作报告end') logger.info('制作报告end')
logger.info('模型训练完成') logger.info('模型训练完成')
push_market_value() # push_market_value()
# # LSTM 单变量模型 # # LSTM 单变量模型
# ex_Lstm(df,input_seq_len=input_size,output_seq_len=horizon,is_debug=is_debug,dataset=dataset) # ex_Lstm(df,input_seq_len=input_size,output_seq_len=horizon,is_debug=is_debug,dataset=dataset)

View File

@ -866,7 +866,7 @@ def model_losss_yongan(sqlitedb, end_time, table_name_prefix):
plt.text(i, j, str(j), ha='center', va='bottom') plt.text(i, j, str(j), ha='center', va='bottom')
# 当前日期画竖虚线 # 当前日期画竖虚线
plt.axvline(x=df['ds'].iloc[-horizon], color='r', linestyle='--') plt.axvline(x=df['ds'].iloc[-config.horizon], color='r', linestyle='--')
plt.legend() plt.legend()
plt.xlabel('日期') plt.xlabel('日期')
plt.ylabel('价格') plt.ylabel('价格')
@ -881,8 +881,8 @@ def model_losss_yongan(sqlitedb, end_time, table_name_prefix):
ax.axis('off') # 关闭坐标轴 ax.axis('off') # 关闭坐标轴
# 数值保留2位小数 # 数值保留2位小数
df = df.round(2) df = df.round(2)
df = df[-horizon:] df = df[-config.horizon:]
df['Day'] = [f'Day_{i}' for i in range(1, horizon+1)] df['Day'] = [f'Day_{i}' for i in range(1, config.horizon+1)]
# Day列放到最前面 # Day列放到最前面
df = df[['Day'] + list(df.columns[:-1])] df = df[['Day'] + list(df.columns[:-1])]
table = ax.table(cellText=df.values, table = ax.table(cellText=df.values,
@ -1297,7 +1297,7 @@ def model_losss(sqlitedb, end_time):
# plt.plot(df['ds'], df[model], label=model,marker='o') # plt.plot(df['ds'], df[model], label=model,marker='o')
plt.plot(df['ds'], df[most_model_name], label=model, marker='o') plt.plot(df['ds'], df[most_model_name], label=model, marker='o')
# 当前日期画竖虚线 # 当前日期画竖虚线
plt.axvline(x=df['ds'].iloc[-horizon], color='r', linestyle='--') plt.axvline(x=df['ds'].iloc[-config.horizon], color='r', linestyle='--')
plt.legend() plt.legend()
plt.xlabel('日期') plt.xlabel('日期')
# 设置横轴日期格式为年-月-日 # 设置横轴日期格式为年-月-日
@ -1338,7 +1338,7 @@ def model_losss(sqlitedb, end_time):
plt.text(i, j, str(j), ha='center', va='bottom') plt.text(i, j, str(j), ha='center', va='bottom')
# 当前日期画竖虚线 # 当前日期画竖虚线
plt.axvline(x=df['ds'].iloc[-horizon], color='r', linestyle='--') plt.axvline(x=df['ds'].iloc[-config.horizon], color='r', linestyle='--')
plt.legend() plt.legend()
plt.xlabel('日期') plt.xlabel('日期')
# 自动设置横轴日期显示 # 自动设置横轴日期显示
@ -1357,8 +1357,8 @@ def model_losss(sqlitedb, end_time):
ax.axis('off') # 关闭坐标轴 ax.axis('off') # 关闭坐标轴
# 数值保留2位小数 # 数值保留2位小数
df = df.round(2) df = df.round(2)
df = df[-horizon:] df = df[-config.horizon:]
df['Day'] = [f'Day_{i}' for i in range(1, horizon+1)] df['Day'] = [f'Day_{i}' for i in range(1, config.horizon+1)]
# Day列放到最前面 # Day列放到最前面
df = df[['Day'] + list(df.columns[:-1])] df = df[['Day'] + list(df.columns[:-1])]
table = ax.table(cellText=df.values, table = ax.table(cellText=df.values,
@ -1388,10 +1388,10 @@ def model_losss(sqlitedb, end_time):
bbox_inches='tight') bbox_inches='tight')
plt.close() plt.close()
# _plt_predict_ture(df_combined3) _plt_predict_ture(df_combined3)
# _plt_modeltopten_predict_ture(df_combined4) # _plt_modeltopten_predict_ture(df_combined4)
# _plt_predict_table(df_combined3) _plt_predict_table(df_combined3)
# _plt_model_results3() _plt_model_results3()
return model_results3 return model_results3
@ -2461,6 +2461,319 @@ def brent_export_pdf(num_indicators=475, num_models=21, num_dayindicator=202, in
print(f"请求超时: {e}") print(f"请求超时: {e}")
@exception_logger
def shiyoujiao_lvyong_export_pdf(num_indicators=475, num_models=21, num_dayindicator=202, inputsize=5, dataset='dataset', time='2024-07-30', reportname='report.pdf', sqlitedb='jbsh_yuanyou.db'):
global y
# 创建内容对应的空列表
content = list()
# 获取特征的近一月值
import pandas as pd
feature_data_df = pd.read_csv(os.path.join(
config.dataset,'指标数据添加时间特征.csv'), parse_dates=['ds']).tail(60)
def draw_feature_trend(feature_data_df, features):
# 画特征近60天的趋势图
feature_df = feature_data_df[['ds', 'y']+features]
# 遍历X每一列和yy画散点图
for i, col in enumerate(features):
# try:
print(f'正在绘制第{i+1}个特征{col}与价格散点图...')
if col not in ['ds', 'y']:
fig, ax1 = plt.subplots(figsize=(10, 6))
# 在第一个坐标轴上绘制数据
sns.lineplot(data=feature_df, x='ds', y='y', ax=ax1, color='b')
ax1.set_xlabel('日期')
ax1.set_ylabel('y', color='b')
ax1.tick_params('y', colors='b')
# 在 ax1 上添加文本显示值,添加一定的偏移避免值与曲线重叠
for j in range(1, len(feature_df), 2):
value = feature_df['y'].iloc[j]
date = feature_df['ds'].iloc[j]
offset = 1.001
ax1.text(date, value * offset, str(round(value, 2)),
ha='center', va='bottom', color='b', fontsize=10)
# 创建第二个坐标轴
ax2 = ax1.twinx()
# 在第二个坐标轴上绘制数据
sns.lineplot(data=feature_df, x='ds', y=col, ax=ax2, color='r')
ax2.set_ylabel(col, color='r')
ax2.tick_params('y', colors='r')
# 在 ax2 上添加文本显示值,添加一定的偏移避免值与曲线重叠
for j in range(0, len(feature_df), 2):
value = feature_df[col].iloc[j]
date = feature_df['ds'].iloc[j]
offset = 1.0003
ax2.text(date, value * offset, str(round(value, 2)),
ha='center', va='bottom', color='r', fontsize=10)
# 添加标题
plt.title(col)
# 设置横坐标为日期格式并自动调整
locator = mdates.AutoDateLocator()
formatter = mdates.AutoDateFormatter(locator)
ax1.xaxis.set_major_locator(locator)
ax1.xaxis.set_major_formatter(formatter)
# 文件名特殊字符处理
col = col.replace('*', '-')
col = col.replace(':', '-')
col = col.replace(r'/', '-')
plt.savefig(os.path.join(config.dataset, f'{col}与价格散点图.png'))
content.append(Graphs.draw_img(
os.path.join(config.dataset, f'{col}与价格散点图.png')))
plt.close()
# except Exception as e:
# print(f'绘制第{i+1}个特征{col}与价格散点图时出错:{e}')
# 添加标题
content.append(Graphs.draw_title(f'{config.y}{time}预测报告'))
# 预测结果
content.append(Graphs.draw_little_title('一、预测结果:'))
# 添加历史走势及预测价格的走势图片
content.append(Graphs.draw_img(os.path.join(config.dataset, '历史价格-预测值.png')))
# 波动率画图逻辑
content.append(Graphs.draw_text('图示说明:'))
content.append(Graphs.draw_text(
' 确定置信区间:设置残差置信阈值,以每周最佳模型为基准,选取在置信区间的预测值作为置信区间;'))
# 取df中y列为空的行
import pandas as pd
df = pd.read_csv(os.path.join(config.dataset, 'predict.csv'), encoding='gbk')
df_true = pd.read_csv(os.path.join(
config.dataset,'指标数据添加时间特征.csv'), encoding='utf-8') # 获取预测日期对应的真实值
df_true = df_true[['ds', 'y']]
eval_df = pd.read_csv(os.path.join(
config.dataset,'model_evaluation.csv'), encoding='utf-8')
# 按评估指标排序,取前五
fivemodels_list = eval_df['模型(Model)'].values # 列表形式,后面当作列名索引使用
# 取 fivemodels_list 和 ds 列
df = df[['ds'] + fivemodels_list.tolist()]
# 拼接预测日期对应的真实值
df = pd.merge(df, df_true, on='ds', how='left')
# 删除全部为nan的列
df = df.dropna(how='all', axis=1)
# 选择除 'ds' 列外的数值列,并进行类型转换和四舍五入
num_cols = [col for col in df.columns if col !=
'ds' and pd.api.types.is_numeric_dtype(df[col])]
for col in num_cols:
df[col] = df[col].astype(float).round(2)
# 添加最大值、最小值、平均值三列
df['平均值'] = df[num_cols].mean(axis=1).round(2)
df['最大值'] = df[num_cols].max(axis=1)
df['最小值'] = df[num_cols].min(axis=1)
# df转置
df = df.T
# df重置索引
df = df.reset_index()
# 添加预测值表格
data = df.values.tolist()
col_width = 500/len(df.columns)
content.append(Graphs.draw_table(col_width, *data))
content.append(Graphs.draw_little_title('二、上一预测周期偏差率分析:'))
df = pd.read_csv(os.path.join(
config.dataset,'testandpredict_groupby.csv'), encoding='utf-8')
df4 = df.copy() # 计算偏差率使用
# 去掉created_dt 列
df4 = df4.drop(columns=['created_dt'])
# 计算模型偏差率
# 计算各列对于y列的差值百分比
df3 = pd.DataFrame() # 存储偏差率
# 删除有null的行
df4 = df4.dropna()
df3['ds'] = df4['ds']
for col in fivemodels_list:
df3[col] = round(abs(df4[col] - df4['y']) / df4['y'] * 100, 2)
# 找出决定系数前五的偏差率
df3 = df3[['ds']+fivemodels_list.tolist()][-inputsize:]
# 找出上一预测区间的时间
stime = df3['ds'].iloc[0]
etime = df3['ds'].iloc[-1]
# 添加偏差率表格
fivemodels = ''.join(eval_df['模型(Model)'].values[:5]) # 字符串形式,后面写入字符串使用
content.append(Graphs.draw_text(
f'预测使用了{num_models}个模型进行训练使用评估结果MAE前五的模型分别是 {fivemodels} ,模型上一预测区间 {stime} -- {etime}的偏差率(%)分别是:'))
# # 添加偏差率表格
df3 = df3.T
df3 = df3.reset_index()
data = df3.values.tolist()
col_width = 500/len(df3.columns)
content.append(Graphs.draw_table(col_width, *data))
content.append(Graphs.draw_little_title('上一周预测准确率:'))
df4 = sqlitedb.select_data('accuracy_rote', order_by='结束日期 desc', limit=1)
df4 = df4.T
df4 = df4.reset_index()
df4 = df4.T
data = df4.values.tolist()
col_width = 500/len(df4.columns)
content.append(Graphs.draw_table(col_width, *data))
content.append(Graphs.draw_little_title('三、预测过程解析:'))
# 特征、模型、参数配置
content.append(Graphs.draw_little_title('模型选择:'))
content.append(Graphs.draw_text(
f'本次预测使用了一个专门收集时间序列的NeuralForecast库中的{num_models}个模型:'))
content.append(Graphs.draw_text(f'使用40天的数据预测未来{inputsize}天的数据。'))
content.append(Graphs.draw_little_title('指标情况:'))
with open(os.path.join(config.dataset, '特征频度统计.txt'), encoding='utf-8') as f:
for line in f.readlines():
content.append(Graphs.draw_text(line))
data = pd.read_csv(os.path.join(config.dataset, '指标数据添加时间特征.csv'),
encoding='utf-8') # 计算相关系数用
df_zhibiaofenlei = loadcsv(os.path.join(
config.dataset,'特征处理后的指标名称及分类.csv')) # 气泡图用
df_zhibiaoshuju = data.copy() # 气泡图用
# 绘制特征相关气泡图
grouped = df_zhibiaofenlei.groupby('指标分类')
grouped_corr = pd.DataFrame(columns=['指标分类', '指标数量', '相关性总和'])
content.append(Graphs.draw_little_title('按指标分类分别与预测目标进行皮尔逊相关系数分析:'))
content.append(Graphs.draw_text('''皮尔逊相关系数说明:'''))
content.append(Graphs.draw_text('''衡量两个特征之间的线性相关性。'''))
content.append(Graphs.draw_text('''
相关系数为1表示两个变量之间存在完全正向的线性关系即当一个变量增加时另一个变量也相应增加且变化是完全一致的'''))
content.append(Graphs.draw_text(
'''相关系数为-1表示两个变量之间存在完全负向的线性关系即当一个变量增加时另一个变量会相应减少且变化是完全相反的'''))
content.append(Graphs.draw_text(
'''相关系数接近0表示两个变量之间不存在线性关系即它们的变化不会随着对方的变化而变化。'''))
for name, group in grouped:
cols = group['指标名称'].tolist()
config.logger.info(f'开始绘制{name}类指标的相关性直方图')
cols_subset = cols
feature_names = ['y'] + cols_subset
correlation_matrix = df_zhibiaoshuju[feature_names].corr()['y']
# 绘制特征相关性直方分布图
plt.figure(figsize=(10, 8))
sns.histplot(correlation_matrix.values.flatten(),
bins=20, kde=True, color='skyblue')
plt.title(f'{name}类指标(共{len(cols_subset)}个)相关性直方分布图')
plt.xlabel('相关系数')
plt.ylabel('频数')
plt.savefig(os.path.join(
config.dataset,f'{name}类指标相关性直方分布图.png'), bbox_inches='tight')
plt.close()
content.append(Graphs.draw_img(
os.path.join(config.dataset, f'{name}类指标相关性直方分布图.png')))
content.append(Graphs.draw_text(
f'{name}类指标(共{len(cols_subset)}个)的相关性直方分布图如上所示。'))
# 相关性大于0的特征
positive_corr_features = correlation_matrix[correlation_matrix > 0].sort_values(
ascending=False).index.tolist()[1:]
print(f'{name}下正相关的特征值有:', positive_corr_features)
if len(positive_corr_features) > 5:
positive_corr_features = positive_corr_features[0:5]
content.append(Graphs.draw_text(
f'{name}类指标中与预测目标y正相关前五的特征有{positive_corr_features}'))
draw_feature_trend(feature_data_df, positive_corr_features)
elif len(positive_corr_features) == 0:
pass
else:
positive_corr_features = positive_corr_features
content.append(Graphs.draw_text(
f'其中与预测目标y正相关的特征有{positive_corr_features}'))
draw_feature_trend(feature_data_df, positive_corr_features)
# 相关性小于0的特征
negative_corr_features = correlation_matrix[correlation_matrix < 0].sort_values(
ascending=True).index.tolist()
print(f'{name}下负相关的特征值有:', negative_corr_features)
if len(negative_corr_features) > 5:
negative_corr_features = negative_corr_features[:5]
content.append(Graphs.draw_text(
f'与预测目标y负相关前五的特征有{negative_corr_features}'))
draw_feature_trend(feature_data_df, negative_corr_features)
elif len(negative_corr_features) == 0:
pass
else:
content.append(Graphs.draw_text(
f'{name}类指标中与预测目标y负相关的特征有{negative_corr_features}'))
draw_feature_trend(feature_data_df, negative_corr_features)
# 计算correlation_sum 第一行的相关性的绝对值的总和
correlation_sum = correlation_matrix.abs().sum()
config.logger.info(f'{name}类指标的相关性总和为:{correlation_sum}')
# 分组的相关性总和拼接到grouped_corr
goup_corr = pd.DataFrame(
{'指标分类': [name], '指标数量': [len(cols_subset)], '相关性总和': [correlation_sum]})
grouped_corr = pd.concat(
[grouped_corr, goup_corr], axis=0, ignore_index=True)
# 绘制相关性总和的气泡图
config.logger.info(f'开始绘制相关性总和的气泡图')
plt.figure(figsize=(10, 10))
sns.scatterplot(data=grouped_corr, x='相关性总和', y='指标数量', size='相关性总和', sizes=(
grouped_corr['相关性总和'].min()*5, grouped_corr['相关性总和'].max()*5), hue='指标分类', palette='viridis')
plt.title('指标分类相关性总和的气泡图')
plt.ylabel('数量')
plt.savefig(os.path.join(config.dataset, '指标分类相关性总和的气泡图.png'),
bbox_inches='tight')
plt.close()
content.append(Graphs.draw_img(os.path.join(config.dataset, '指标分类相关性总和的气泡图.png')))
content.append(Graphs.draw_text(
'气泡图中,横轴为指标分类,纵轴为指标分类下的特征数量,气泡的面积越大表示该分类中特征的相关系数和越大。'))
config.logger.info(f'绘制相关性总和的气泡图结束')
content.append(Graphs.draw_little_title('模型选择:'))
content.append(Graphs.draw_text(
f'预测使用了{num_models}个模型进行训练拟合通过评估指标MAE从小到大排列前5个模型的简介如下'))
# 读取模型简介
with open(os.path.join(config.dataset, 'model_introduction.txt'), 'r', encoding='utf-8') as f:
for line in f:
line_split = line.strip().split('--')
if line_split[0] in fivemodels_list:
for introduction in line_split:
content.append(Graphs.draw_text(introduction))
content.append(Graphs.draw_little_title('模型评估:'))
df = pd.read_csv(os.path.join(
config.dataset,'model_evaluation.csv'), encoding='utf-8')
# 判断 df 的数值列转为float
for col in eval_df.columns:
if col not in ['模型(Model)']:
eval_df[col] = eval_df[col].astype(float)
eval_df[col] = eval_df[col].round(3)
# 筛选 fivemodels_list.tolist() 的行
eval_df = eval_df[eval_df['模型(Model)'].isin(fivemodels_list)]
# df转置
eval_df = eval_df.T
# df重置索引
eval_df = eval_df.reset_index()
eval_df = eval_df.T
# # 添加表格
data = eval_df.values.tolist()
col_width = 500/len(eval_df.columns)
content.append(Graphs.draw_table(col_width, *data))
content.append(Graphs.draw_text('评估指标释义:'))
content.append(Graphs.draw_text(
'1. 均方根误差(RMSE):均方根误差是衡量预测值与实际值之间误差的一种方法,取值越小,误差越小,预测效果越好。'))
content.append(Graphs.draw_text(
'2. 平均绝对误差(MAE):平均绝对误差是衡量预测值与实际值之间误差的一种方法,取值越小,误差越小,预测效果越好。'))
content.append(Graphs.draw_text(
'3. 平均平方误差(MSE):平均平方误差是衡量预测值与实际值之间误差的一种方法,取值越小,误差越小,预测效果越好。'))
content.append(Graphs.draw_text('模型拟合:'))
# 添加图片
content.append(Graphs.draw_img(os.path.join(config.dataset, '预测值与真实值对比图.png')))
# 生成pdf文件
doc = SimpleDocTemplate(os.path.join(config.dataset, reportname), pagesize=letter)
doc.build(content)
# pdf 上传到数字化信息平台
try:
if config.is_update_report:
with open(os.path.join(config.dataset, reportname), 'rb') as f:
base64_data = base64.b64encode(f.read()).decode('utf-8')
upload_data["data"]["fileBase64"] = base64_data
upload_data["data"]["fileName"] = reportname
token = get_head_auth_report()
upload_report_data(token, upload_data)
except TimeoutError as e:
print(f"请求超时: {e}")
@exception_logger @exception_logger
def pp_export_pdf(num_indicators=475, num_models=21, num_dayindicator=202, inputsize=5, dataset='dataset', time='2024-07-30', reportname='report.pdf', sqlitedb='jbsh_yuanyou.db'): def pp_export_pdf(num_indicators=475, num_models=21, num_dayindicator=202, inputsize=5, dataset='dataset', time='2024-07-30', reportname='report.pdf', sqlitedb='jbsh_yuanyou.db'):
# 创建内容对应的空列表 # 创建内容对应的空列表