一、功能概述
本工具专为实测高频、预测低频 的时序数据对比场景设计,可自动处理 4 组模型输出 Excel 文件,完成数据清洗、时间对齐与趋势可视化。工具能智能识别文件中「实测」「预测」两列数据,过滤空值、字符串等无效内容,解决数据长度不一致问题。基于1 秒实测、1 分钟预测的时间规律,将预测值按分钟精准填充到对应时间区间,实现两类数据完全对齐。
自动批量生成高清对比曲线图,采用蓝色细曲线展示实测高频细节,红色粗曲线突出预测整体趋势,界面简洁美观。支持隐藏拥挤的横轴刻度,保留网格、图例与标题,便于直观观察拟合效果。所有处理结果自动保存至原文件夹,包含对齐后的标准 Excel 数据表与高清趋势图,无需手动调整,一键完成从数据预处理到可视化输出全流程,高效稳定适配 Windows 环境与低版本 Python 库。
python
# -*- coding: utf-8 -*-
import os
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')
# ========== 全局配置 ==========
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字体
plt.rcParams['axes.unicode_minus'] = False # 负号正常显示
FOLDER_PATH = r'C:\Users\wayso\Desktop\ht' # 文件所在路径
SAMPLING_RATIO = 60 # 1分钟=60秒(实测1秒/个,预测1分钟/个)
# ========== 工具函数:过滤非数值数据 ==========
def filter_numeric_data(series):
"""
过滤Series中的非数值数据,仅保留数字(处理空字符串/空格/符号)
:param series: 原始列数据
:return: 仅包含数值的Series
"""
# 步骤1:将空字符串、空格转为NaN
series = series.replace(['', ' ', '-', '无', 'NaN'], np.nan)
# 步骤2:尝试转换为数值型(无法转换的转为NaN)
try:
series = pd.to_numeric(series, errors='coerce')
except:
series = np.nan
# 步骤3:过滤NaN,重置索引
return series.dropna().reset_index(drop=True)
# ========== 核心函数:兼容低版本pandas的Excel读取 ==========
def read_excel_universal(file_path):
"""万能Excel读取函数(适配pandas<1.3.0)"""
# 方案1:openpyxl引擎(优先读取.xlsx)
try:
return pd.read_excel(
file_path,
sheet_name='Sheet1',
engine='openpyxl',
header=0, # 第一行作为列名
na_filter=False # 不自动过滤空值,避免解析错误
)
except Exception as e1:
# 方案2:xlrd引擎(兼容旧版.xls/.xlsx)
try:
return pd.read_excel(
file_path,
sheet_name='Sheet1',
engine='xlrd',
header=0,
na_filter=False
)
except Exception as e2:
# 方案3:终极兜底(忽略列名,手动处理)
try:
df = pd.read_excel(
file_path,
sheet_name='Sheet1',
engine='openpyxl',
header=None # 不指定列名,避免列名解析错误
)
# 手动设置列名(适配实测/预测列)
if len(df.columns) >= 2:
df.columns = ['实测', '预测'] # 强制设置列名
else:
print(f"⚠️ 文件{file_path}列数异常:{len(df.columns)}列")
return df
except Exception as e3:
raise Exception(f"读取失败:{str(e3)}(openpyxl/xlrd均失败)")
# ========== 数据对齐函数(按时间维度) ==========
def align_data_by_time(actual_series, predict_series):
"""
对齐实测(1秒)和预测(1分钟)数据:
- 过滤非数值数据
- 实测数据保留1秒粒度
- 预测数据按分钟填充到对应60行
"""
# 核心修复:过滤非数值数据(空字符串/符号/中文)
actual_clean = filter_numeric_data(actual_series)
predict_clean = filter_numeric_data(predict_series)
# 检查过滤后是否有数据
if len(actual_clean) == 0:
raise Exception("实测列无有效数值数据")
if len(predict_clean) == 0:
raise Exception("预测列无有效数值数据")
# 获取实测数据总长度(总秒数)
total_seconds = len(actual_clean)
# 初始化预测数据为NaN(浮点型,避免类型错误)
predict_aligned = np.full(total_seconds, np.nan, dtype=np.float64)
# 按分钟填充预测值(1个预测值对应60个实测值)
for i in range(len(predict_clean)):
start_pos = i * SAMPLING_RATIO
if start_pos < total_seconds:
end_pos = min((i+1)*SAMPLING_RATIO, total_seconds)
# 确保赋值的是浮点数
predict_value = float(predict_clean.iloc[i])
predict_aligned[start_pos:end_pos] = predict_value
# 实测数据对齐(保持原样,长度一致)
actual_aligned = actual_clean.reindex(range(total_seconds))
return actual_aligned, predict_aligned
# ========== 绘图函数(实测vs预测对比) ==========
def plot_comparison(actual, predict, file_name, save_path):
"""绘制高清对比走势图(隐藏横轴刻度)"""
plt.figure(figsize=(15, 8))
# 绘制实测曲线(蓝色细线条,体现1秒粒度细节)
plt.plot(actual.index, actual.values, 'b-', label='实测值(1秒/个)', linewidth=0.8, alpha=0.8)
# 绘制预测曲线(橙色粗线条,突出1分钟粒度趋势)
plt.plot(predict.index, predict.values, 'r-', label='预测值(1分钟/个)', linewidth=2, alpha=0.9)
# 图表美化
plt.title(f'{file_name}\n实测(1秒粒度)vs 预测(1分钟粒度)对比', fontsize=16, pad=20)
plt.xlabel('时间(秒)', fontsize=12)
plt.ylabel('数值', fontsize=12)
plt.legend(fontsize=12, loc='best')
plt.grid(True, alpha=0.3, linestyle='--')
# ========== 核心修改:隐藏横轴刻度和标签 ==========
ax = plt.gca() # 获取当前坐标轴对象
ax.set_xticks([]) # 隐藏横轴刻度标签
ax.xaxis.set_ticks_position('none') # 隐藏横轴刻度线
# 保存高清图片
plt.tight_layout() # 自适应布局,避免标签被截断
plt.savefig(save_path, dpi=300, bbox_inches='tight')
plt.close()
print(f'✅ 对比图已保存:{save_path}')
# ========== 主程序:批量处理所有Excel文件 ==========
if __name__ == '__main__':
# 遍历文件夹中的Excel文件(兼容.xlsx/.xls)
file_list = [
f for f in os.listdir(FOLDER_PATH)
if f.endswith(('.xlsx', '.xls')) and '创建时间' in f
]
# 检查文件数量
if not file_list:
print('❌ 未找到目标Excel文件!请检查路径:', FOLDER_PATH)
else:
print(f'✅ 找到{len(file_list)}个目标文件,开始处理...\n')
# 逐个处理文件
for file_name in file_list:
file_path = os.path.join(FOLDER_PATH, file_name)
print(f'----------------------------------------')
print(f'正在处理:{file_name}')
# 1. 读取Excel文件(兼容低版本pandas)
try:
df = read_excel_universal(file_path)
except Exception as e:
print(f'❌ 读取文件失败:{e}')
continue
# 2. 检查并匹配「实测」「预测」列(兼容列名变体)
actual_col, predict_col = None, None
for col in df.columns:
col_str = str(col).strip() # 去除空格,兼容列名含空格的情况
if '实测' in col_str:
actual_col = col
elif '预测' in col_str:
predict_col = col
if not actual_col or not predict_col:
print(f'❌ 文件缺少「实测」或「预测」列,列名列表:{df.columns.tolist()}')
continue
# 3. 按时间维度对齐数据(包含非数值过滤)
try:
actual_aligned, predict_aligned = align_data_by_time(df[actual_col], df[predict_col])
except Exception as e:
print(f'❌ 数据对齐失败:{e}')
continue
# 4. 保存对齐后的数据(Excel)
aligned_df = pd.DataFrame({
'时间(秒)': range(len(actual_aligned)),
'实测值': actual_aligned,
'预测值': predict_aligned
})
excel_save_path = os.path.join(FOLDER_PATH, f'对齐后_{file_name}')
aligned_df.to_excel(excel_save_path, index=False, engine='openpyxl')
print(f'✅ 对齐后数据已保存:{excel_save_path}')
# 5. 绘制并保存对比图
img_save_path = os.path.join(FOLDER_PATH, f'{os.path.splitext(file_name)[0]}_对比图.png')
try:
plot_comparison(
actual=pd.Series(actual_aligned),
predict=pd.Series(predict_aligned),
file_name=file_name,
save_path=img_save_path
)
except Exception as e:
print(f'❌ 绘图失败:{e}')
continue
# 处理完成提示
print('\n========================================')
print('🎉 所有文件处理完成!结果文件已保存到原文件夹。')
二、关键修改说明
表格
| 代码行 | 作用 |
|---|---|
ax = plt.gca() |
获取当前图表的坐标轴对象,用于精细控制样式 |
ax.set_xticks([]) |
清空横轴的刻度标签(比如 "0 分""1 分" 等文字) |
ax.xaxis.set_ticks_position('none') |
隐藏横轴的刻度线(底部的小短线) |
注释掉原有plt.xticks代码 |
彻底移除原有的刻度生成逻辑,避免冲突 |
三、进阶可选:仅隐藏刻度线,保留 x 轴标签
如果你想保留 "时间(秒)" 这个 x 轴标题,只隐藏刻度(当前代码已保留标题);如果想连 x 轴标题也隐藏,只需添加:
python
运行
ax.set_xlabel('') # 隐藏x轴标题
总结
- 核心实现 :通过
set_xticks([])和set_ticks_position('none')完全隐藏横轴的刻度标签和刻度线; - 视觉效果:图表仅保留曲线、图例、标题和 y 轴,整体更简洁美观,无横轴刻度干扰;
- 兼容性:修改仅针对绘图样式,不影响数据读取和对齐逻辑,原有功能完全保留。
运行修改后的代码,生成的对比图将不再显示横轴底部的任何刻度信息,完美满足你的美观需求。