跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Matplotlib图表区域事件驱动型背景着色教程

本教程详细介绍了如何在Matplotlib图表中根据特定事件数据为图表的不同区域进行背景着色。通过识别数据系列中的事件发生点,并利用axvspan函数,我们可以为事件发生前、发生中和发生后的区域应用不同的颜色,从而增强 数据可视化 效果,突出关键时间段。教程提供了详细的代码示例和注意事项,帮助用户实现精确的区域着色。 引言 在数据分析和可视化中,我们经常需要突出图表中的特定时间段或数据区域,以标记事件、异常值或重要阶段。matplotlib提供了强大的绘图功能,其中axvspan函数是实现垂直区域着色的关键工具。本教程将指导您如何根据一个独立的事件序列(例如,一个表示事件发生与否的二进制序列)来动态地为图表的不同部分着色,特别地,我们将实现事件发生前、发生中和发生后三个阶段的不同颜色标记。 核心概念:axvspan函数 matplotlib.axes.Axes.axvspan(xmin, xmax, ymin=0, ymax=1, **kwargs) 函数用于在坐标轴上绘制一个垂直的矩形区域。 xmin, xmax: 定义了矩形区域的水平范围。 ymin, ymax: 定义了矩形区域的垂直范围,默认是0到1,表示覆盖整个y轴范围。 facecolor: 矩形区域的填充颜色。 alpha: 矩形区域的透明度,值介于0(完全透明)和1(完全不透明)之间。 通过多次调用axvspan并指定不同的xmin、xmax和facecolor,我们可以创建多个自定义着色区域。 准备数据 首先,我们需要模拟一些数据,包括主数据系列和用于触发着色事件的事件序列。事件序列通常是一个二进制值,例如0表示无事件,1表示事件发生。
import numpy as np import matplotlib.pyplot as plt import pandas as pd # 设置随机种子以便结果可复现 np.random.seed(42) # 生成事件数据,初始为0 data_length = 56 event = pd.DataFrame(np.zeros(data_length, dtype=int), columns=['event_status']) # 模拟事件发生 # 事件1:从索引10到13(即[10:14]) event.iloc[10:14, 0] = 1 # 事件2:从索引24到35(即[24:36]) event.iloc[24:36, 0] = 1 # 生成主图表数据 data_series_1 = pd.DataFrame(np.random.randint(200, 300, size=(data_length, 1)), columns=['Series1']) data_series_2 = pd.DataFrame(np.random.randint(0, 3, size=(data_length, 1)), columns=['Series2']) data_series_3 = pd.DataFrame(np.random.randint(300, 400, size=(data_length, 1)), columns=['Series3']) data_series_4 = pd.DataFrame(np.random.randint(0, 5, size=(data_length, 1)), columns=['Series4'])
识别事件周期 为了实现精确着色,我们需要从事件序列中识别出所有连续的事件发生周期(即event值为1的连续区间)。
def find_event_periods(event_series): """ 识别事件序列中值为1的连续周期。 返回一个列表,每个元素是一个元组 (start_index, end_index), 其中end_index是该周期的结束索引(不包含)。 """ event_periods = [] in_event = False start_idx = -1 for i in range(len(event_series)): if event_series.iloc[i] == 1 and not in_event: start_idx = i in_event = True elif event_series.iloc[i] == 0 and in_event: event_periods.append((start_idx, i)) in_event = False # 处理事件持续到序列末尾的情况 if in_event: event_periods.append((start_idx, len(event_series))) return event_periods event_periods = find_event_periods(event['event_status']) print(f"识别到的事件周期: {event_periods}")
输出示例:识别到的事件周期: [(10, 14), (24, 36)] elasticsearch-9.4.0 win PHP中文网提供Elasticsearch 9.4.0 Windows 版本压缩包下载解压即用,内置 OpenJDK 无需额外配置 Java 环境。该版本重磅推出原生 Prometheus 支持、正式版 Elastic Workflows 以及基于 NVIDIA cuVS 的 GPU 加速向量索引,大幅提升 AI 检索与可观测性能力。建议前往官网下载,快速体验极速搜索与强大的数据分析功能。 下载 实现区域着色逻辑 现在,我们将根据识别到的事件周期,为每个周期定义三个着色区域: 事件前区域 (Pre-event): 事件开始前一个索引到事件开始的区域。 事件中区域 (During-event): 事件发生期间的区域。 事件后区域 (Post-event): 事件结束后两个索引的区域。 我们将定义这些区域的颜色和透明度。
# 定义着色方案 color_pre_event = 'skyblue' # 事件前区域颜色 color_during_event = 'lightcoral' # 事件中区域颜色 color_post_event = 'lightgreen' # 事件后区域颜色 alpha_level = 0.2 # 透明度
完整代码示例 以下是整合了数据生成、事件识别和区域着色逻辑的完整Matplotlib绘图代码。它将使用原始问题中定义的双轴子图结构。
import numpy as np import matplotlib.pyplot as plt import pandas as pd # --- 1. 数据准备 --- np.random.seed(42) # 确保结果可复现 data_length = 56 event = pd.DataFrame(np.zeros(data_length, dtype=int), columns=['event_status']) event.iloc[10:14, 0] = 1 # 事件1 event.iloc[24:36, 0] = 1 # 事件2 # 主图表数据 data_series_1 = pd.DataFrame(np.random.randint(200, 300, size=(data_length, 1)), columns=['Series1']) data_series_2 = pd.DataFrame(np.random.randint(0, 3, size=(data_length, 1)), columns=['Series2']) data_series_3 = pd.DataFrame(np.random.randint(300, 400, size=(data_length, 1)), columns=['Series3']) data_series_4 = pd.DataFrame(np.random.randint(0, 5, size=(data_length, 1)), columns=['Series4']) # --- 2. 事件周期识别函数 --- def find_event_periods(event_series): event_periods = [] in_event = False start_idx = -1 for i in range(len(event_series)): if event_series.iloc[i] == 1 and not in_event: start_idx = i in_event = True elif event_series.iloc[i] == 0 and in_event: event_periods.append((start_idx, i)) in_event = False if in_event: event_periods.append((start_idx, len(event_series))) return event_periods event_periods = find_event_periods(event['event_status']) # --- 3. 定义着色方案 --- color_pre_event = 'blue' # 事件前区域颜色 color_during_event = 'red' # 事件中区域颜色 color_post_event = 'green' # 事件后区域颜色 alpha_level = 0.2 # 透明度 # --- 4. 绘图部分 --- plt.figure(figsize=(18, 8)) # 调整图表大小以适应内容和布局 # 第一个子图 (2行2列的第1个) ax1 = plt.subplot(1, 2, 1) # 调整为1行2列,便于展示 ax2 = ax1.twinx() # 创建第二个y轴 # 绘制主数据系列 ax1.plot(data_series_1, label='Series 1', color='g') ax1.plot(data_series_2, label='Series 2', color='r') ax2.plot(event, label='Event Status', color='k', linestyle='--', linewidth=1) # 事件状态曲线 # 应用区域着色 for start, end in event_periods: # 事件前区域: 从 max(0, start-1) 到 start pre_event_xmin = max(0, start - 1) pre_event_xmax = start if pre_event_xmin < pre_event_xmax: # 确保区域有效 ax1.axvspan(pre_event_xmin, pre_event_xmax, facecolor=color_pre_event, alpha=alpha_level, label='Pre-Event' if start == event_periods[0][0] else "") # 事件中区域: 从 start 到 end ax1.axvspan(start, end, facecolor=color_during_event, alpha=alpha_level, label='During-Event' if start == event_periods[0][0] else "") # 事件后区域: 从 end 到 min(data_length, end+2) post_event_xmin = end post_event_xmax = min(data_length, end + 2) if post_event_xmin < post_event_xmax: # 确保区域有效 ax1.axvspan(post_event_xmin, post_event_xmax, facecolor=color_post_event, alpha=alpha_level, label='Post-Event' if start == event_periods[0][0] else "") # 设置标签和标题 ax1.set_ylabel('Value (m)', fontsize=12) ax2.set_ylabel('Event Status (t)', color='k', fontsize=12) ax1.set_title('图表 0: 事件驱动背景着色示例', fontsize=14) ax1.tick_params(axis='y', labelsize=10) ax1.tick_params(axis='x', labelsize=10) ax2.tick_params(axis='y', labelsize=10) # 合并图例,避免重复标签 lines, labels = ax1.get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() # 过滤掉axvspan的重复标签,只保留第一次出现的 unique_labels = {} for line, label in zip(lines + lines2, labels + labels2): if label not in unique_labels: unique_labels[label] = line ax1.legend(unique_labels.values(), unique_labels.keys(), loc='upper left', prop={'size': 10}) # 第二个子图 (2行2列的第2个) - 结构与第一个类似,但使用不同的数据 ax3 = plt.subplot(1, 2, 2) ax4 = ax3.twinx() ax3.plot(data_series_3, label='Series 3', color='purple') ax3.plot(data_series_4, label='Series 4', color='orange') ax4.plot(event, label='Event Status', color='k', linestyle='--', linewidth=1) # 应用区域着色 (与第一个子图逻辑相同) for start, end in event_periods: pre_event_xmin = max(0, start - 1) pre_event_xmax = start if pre_event_xmin < pre_event_xmax: ax3.axvspan(pre_event_xmin, pre_event_xmax, facecolor=color_pre_event, alpha=alpha_level) ax3.axvspan(start, end, facecolor=color_during_event, alpha=alpha_level) post_event_xmin = end post_event_xmax = min(data_length, end + 2) if post_event_xmin < post_event_xmax: ax3.axvspan(post_event_xmin, post_event_xmax, facecolor=color_post_event, alpha=alpha_level) ax3.set_ylabel('Value (m)', fontsize=12) ax4.set_ylabel('Event Status (t)', color='k', fontsize=12) ax3.set_title('图表 1: 事件驱动背景着色示例', fontsize=14) ax3.tick_params(axis='y', labelsize=10) ax3.tick_params(axis='x', labelsize=10) ax4.tick_params(axis='y', labelsize=10) # 合并图例 lines, labels = ax3.get_legend_handles_labels() lines2, labels2 = ax4.get_legend_handles_labels() unique_labels = {} for line, label in zip(lines + lines2, labels + labels2): if label not in unique_labels: unique_labels[label] = line ax3.legend(unique_labels.values(), unique_labels.keys(), loc='upper left', prop={'size': 10}) plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域 plt.show()
代码解析与注意事项 数据生成与事件定义: event DataFrame用于存储事件状态,其中0表示无事件,1表示事件发生。 iloc用于精确设置事件发生的区间。 find_event_periods函数: 此函数通过遍历event_series来识别连续的1值块。它返回一个元组列表,每个元组(start, end)表示一个事件周期的起始和结束索引(end是排他性的)。 这种方法比简单地查找1的索引更健壮,因为它能正确处理连续的事件块。 着色方案定义: color_pre_event, color_during_event, color_post_event定义了三个阶段的颜色。 alpha_level控制着色区域的透明度,通常设置为0.1到0.3之间,以便背景色不遮盖主要数据线。 axvspan调用: 对于每个识别到的事件周期(start, end),我们计算并调用三次axvspan: 事件前: xmin为max(0, start - 1),xmax为start。max(0, ...)确保区域不会超出图表左边界。 事件中: xmin为start,xmax为end。 事件后: xmin为end,xmax为`min(data_length, end +

相关文章