feat(utils_pack): 新增时间序列对比绘图脚本和数据集绘图脚本

- 新增 draw_line_compare.py 支持原始与扰动时间序列对比及局部放大
- 支持空格和逗号分隔格式数据加载,命令行参数灵活配置
- 新增 draw_all_dataset.py 用于绘制Excel中CPadv与CPsoadv数据对比折线图
- 设置全局字体为 Times New Roman,保证图表美观一致
- 新增 line_compare_command.txt 提供绘图脚本示例命令
- 删除无用的 print_results_path.py 文件,清理项目冗余代码
This commit is contained in:
Qihang Zhang 2025-04-20 23:00:31 +08:00
parent 754b173fcc
commit 1cd9841c1a
5 changed files with 214 additions and 0 deletions

View File

View File

@ -0,0 +1,58 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
# 设置全局字体为 Times New Roman
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['mathtext.fontset'] = 'stix' # 数学文本也使用与 Times New Roman 兼容的字体
# 读取Excel文件
file_path = "实验结果.xlsx" # 请替换为你的文件路径
sheet_name = "Sheet1"
df = pd.read_excel(file_path, sheet_name=sheet_name)
# 提取所需数据
# 注意: Excel范围F2:F42对应pandas的索引1:41
# 同样L2:L43对应索引1:42A2:A43对应索引1:42
dataset_names = df.iloc[1:42, 0].values # A2:A43 数据集名称
cp_adv = df.iloc[1:41, 5].values # F2:F42 CPadv数据
cp_soadv = df.iloc[1:42, 12].values # L2:L43 CPsoadv数据
# 计算均值
cp_adv_mean = np.mean(cp_adv)
cp_soadv_mean = np.mean(cp_soadv)
# 创建图表 - 减小图片尺寸
plt.figure(figsize=(10, 5)) # 从 (12, 6) 减小到 (10, 5)
# 绘制CPadv折线
plt.plot(dataset_names[:len(cp_adv)], cp_adv, 'b-', marker='o', label='CPadv')
# 绘制CPadv均值线
plt.axhline(y=cp_adv_mean, color='b', linestyle='--', alpha=0.7, label='Mean of CPadv')
# 绘制CPsoadv折线
plt.plot(dataset_names[:len(cp_soadv)], cp_soadv, 'r-', marker='s', label='CPsoadv')
# 绘制CPsoadv均值线
plt.axhline(y=cp_soadv_mean, color='r', linestyle='--', alpha=0.7, label='Mean of CPsoadv')
# 设置图表标题和标签
plt.ylabel('Queries', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
# 调整x轴标签
plt.xticks(rotation=45, ha='right')
plt.tight_layout()
# 保存为PDF矢量图
plt.savefig('CCPP_comparison.pdf', bbox_inches='tight')
# 同时也保存一份PNG格式如果需要
plt.savefig('CCPP_comparison.png', dpi=300, bbox_inches='tight')
plt.show()
# 打印均值结果
print(f"CPadv 均值: {cp_adv_mean:.2f}")
print(f"CPsoadv 均值: {cp_soadv_mean:.2f}")

View File

@ -0,0 +1,153 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import argparse
from matplotlib.patches import Rectangle
import matplotlib.transforms as transforms
# 设置全局字体为Times New Roman
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['mathtext.fontset'] = 'cm' # 确保数学公式也使用合适的字体
def load_data(file_path):
"""加载数据,支持空格分隔和逗号分隔"""
try:
# 尝试以空格分隔符读取
df = pd.read_csv(file_path, header=None, sep=r'\s+')
print(f"已使用空格分隔符读取文件: {file_path}")
return df
except Exception as e:
print(f"以空格分隔符读取失败,尝试以逗号分隔符读取: {e}")
try:
# 尝试以逗号分隔符读取
df = pd.read_csv(file_path, header=None)
print(f"已使用逗号分隔符读取文件: {file_path}")
return df
except Exception as e:
print(f"读取文件失败: {e}")
raise
def plot_time_series(original_file, perturbed_file, sequence_id, beta, zoom_start=None, zoom_end=None, sub_offset=0.1):
"""绘制指定序号的原始和扰动时间序列对比图,并添加局部放大图
参数:
original_file -- 原始时间序列文件路径
perturbed_file -- 扰动时间序列文件路径
sequence_id -- 要绘制的序列ID
beta -- beta参数值
zoom_start -- 放大区域的起始索引如果为None则不添加放大图
zoom_end -- 放大区域的结束索引如果为None则不添加放大图
"""
# 加载数据
original_data = load_data(original_file)
perturbed_data = load_data(perturbed_file)
# 确保序号是整数
sequence_id = int(sequence_id)
# 在两个文件中查找指定序号的行
original_row = original_data[original_data[0] == sequence_id]
perturbed_row = perturbed_data[perturbed_data[0] == sequence_id]
print(f"数据中的唯一序号: {original_data[0].unique()}")
if original_row.empty or perturbed_row.empty:
print(f"序号 {sequence_id} 在一个或两个文件中不存在!")
return
# 提取时间序列数据从第3列开始
original_series = original_row.iloc[0, 2:].values
perturbed_series = perturbed_row.iloc[0, 2:].values
# 创建x轴数据点
x = np.arange(len(original_series))
# 设置图形大小和布局
fig = plt.figure(figsize=(8, 5))
# 创建主图
ax_main = plt.subplot(111)
ax_main.plot(x, original_series, label='origin', color='blue', linewidth=2)
ax_main.plot(x, perturbed_series, label='perturbed', color='red', linewidth=2)
ax_main.legend(prop={'family': 'Times New Roman', 'size': 16})
ax_main.set_xlabel(r'$\beta=$' + f'{beta}', fontsize=24)
ax_main.grid(True, linestyle='--', alpha=0.7)
# 增大主图坐标轴刻度字体
ax_main.tick_params(axis='both', labelsize=20)
# 如果提供了放大区域,添加子图和矩形框
if zoom_start is not None and zoom_end is not None:
# 确保缩放范围有效
zoom_start = max(0, min(zoom_start, len(original_series)-1))
zoom_end = max(zoom_start+1, min(zoom_end, len(original_series)))
# 计算放大区域的y轴范围
min_y = min(min(original_series[zoom_start:zoom_end]), min(perturbed_series[zoom_start:zoom_end]))
max_y = max(max(original_series[zoom_start:zoom_end]), max(perturbed_series[zoom_start:zoom_end]))
y_margin = (max_y - min_y) * 0.1 # 10% 的边距
# 添加矩形框表示放大区域
rect = Rectangle((zoom_start, min_y - y_margin),
zoom_end - zoom_start,
(max_y - min_y) + 2 * y_margin,
fill=False, edgecolor='gray', linestyle='--', linewidth=1.5)
ax_main.add_patch(rect)
# 创建放大子图 - 调整位置到上方,缩小尺寸
# 对主图对象进行调整
box = ax_main.get_position()
ax_main.set_position([box.x0, box.y0, box.width, box.height * 0.9])
# 放大图放在上方,位置和大小调整
left = sub_offset # 右侧位置
bottom = 0.65 # 上方位置
width = 0.35 # 缩小宽度
height = 0.30 # 缩小高度
# 创建子图
ax_zoom = fig.add_axes([left, bottom, width, height])
ax_zoom.plot(x[zoom_start:zoom_end], original_series[zoom_start:zoom_end], color='blue', linewidth=2)
ax_zoom.plot(x[zoom_start:zoom_end], perturbed_series[zoom_start:zoom_end], color='red', linewidth=2)
ax_zoom.grid(True, linestyle='--', alpha=0.7)
# 隐藏子图的坐标轴刻度数字
ax_zoom.set_xticklabels([])
ax_zoom.set_yticklabels([])
# 调整子图显示范围,添加一点边距
ax_zoom.set_xlim(zoom_start, zoom_end)
ax_zoom.set_ylim(min_y - y_margin, max_y + y_margin)
plt.tight_layout()
# 保存图片
output_filename = f'time_series_comparison_{beta}_{sequence_id}.pdf'
plt.savefig(output_filename)
print(f"图像已保存为 {output_filename}")
# 显示图像
plt.show()
# python draw_line_compare.py --original /Users/catb/Library/CloudStorage/CloudMounter-B40-4/home/BJTU/project/CPadv/CCPP实验结果/factor/ECG200/0_02/ori_time_series0.txt --perturbed /Users/catb/Library/CloudStorage/CloudMounter-B40-4/home/BJTU/project/CPadv/CCPP实验结果/factor/ECG200/0_02/attack_time_series0.txt --id 2 --beta 0.02 --zoom-start 60 --zoom-end 80
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='绘制时间序列对比图')
parser.add_argument('--original', type=str, required=True, help='原始时间序列文件路径')
parser.add_argument('--perturbed', type=str, required=True, help='攻击/扰动时间序列文件路径')
parser.add_argument('--id', type=int, required=True, help='要绘制的时间序列序号')
parser.add_argument('--beta', type=float, default=0.01, help='beta参数值')
parser.add_argument('--zoom-start', type=int, help='放大区域的起始索引')
parser.add_argument('--zoom-end', type=int, help='放大区域的结束索引')
parser.add_argument('--sub_offset', type=float, help='放大子图向右偏移量')
args = parser.parse_args()
plot_time_series(
args.original,
args.perturbed,
args.id,
args.beta,
args.zoom_start,
args.zoom_end,
args.sub_offset
)

View File

@ -0,0 +1,3 @@
python draw_line_compare.py --original ..\CCPP实验结果\factor\Lightning7\0_01\ori_time_series0.txt --perturbed ..\CCPP实验结果\factor\Lightning7\0_01\attack_time_series0.txt --id 1 --beta 0.01 --zoom-start 200 --zoom-end 225 --sub_offset 0.35
python draw_line_compare.py --original ..\CCPP实验结果\factor\Lightning7\0_02\ori_time_series0.txt --perturbed ..\CCPP实验结果\factor\Lightning7\0_02\attack_time_series0.txt --id 1 --beta 0.02 --zoom-start 200 --zoom-end 225 --sub_offset 0.35
python draw_line_compare.py --original ..\CCPP实验结果\factor\Lightning7\0_04\ori_time_series0.txt --perturbed ..\CCPP实验结果\factor\Lightning7\0_04\attack_time_series0.txt --id 1 --beta 0.04 --zoom-start 200 --zoom-end 225 --sub_offset 0.35