如何使用Matplotlib在集合的每个矩形上添加文本?
利用Matplotlib的PatchCollection高效绘制带文本标注的矩形区域
核心思路
PatchCollection确实能让矩形批量绘制更高效,但Matplotlib没有原生支持给Collection内的Patch批量绑定文本的工具。不过可以通过向量化计算文本位置替代逐行循环的冗余操作,配合ax.texts.extend()一次性添加所有文本对象,减少底层交互开销,同时保持代码简洁性。
优化后的代码
import pandas as pd import matplotlib.pyplot as plt from matplotlib.collections import PatchCollection from matplotlib.patches import Rectangle # 示例数据 windows_df = pd.DataFrame( {'window_index_num': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, 'left_pulse_num': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, 'right_pulse_num': {0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 7, 6: 8, 7: 9, 8: 10, 9: 11}, 'idx_of_left_pulse': {0: 0, 1: 4036, 2: 4080, 3: 4107, 4: 4368, 5: 4491, 6: 4529, 7: 4624, 8: 4626, 9: 4639}, 'idx_of_right_pulse': {0: 4080, 1: 4107, 2: 4368, 3: 4491, 4: 4529, 5: 4624, 6: 4626, 7: 4639, 8: 4679, 9: 4781}, 'left_pulse_pos_in_E': {0: 10.002042118364418, 1: 40.29395464818188, 2: 41.19356816747343, 3: 41.76060061888303, 4: 47.90221207147802, 5: 51.27679395217831, 6: 52.39165780468267, 7: 55.37561818764979, 8: 55.47294132608167, 9: 55.99635666692289}, 'right_pulse_pos_in_E': {0: 41.19356816747343, 1: 41.76060061888303, 2: 47.90221207147802, 3: 51.27679395217831, 4: 52.39165780468267, 5: 55.37561818764979, 6: 55.47294132608167, 7: 55.99635666692289, 8: 57.33777021469516, 9: 60.984834434908144}, 'idx_window_left_border': {0: 0, 1: 3990, 2: 4058, 3: 4093, 4: 4237, 5: 4429, 6: 4510, 7: 4576, 8: 4625, 9: 4632}, 'idx_window_right_border': {0: 4094, 1: 4238, 2: 4430, 3: 4510, 4: 4577, 5: 4625, 6: 4633, 7: 4659, 8: 4730, 9: 4792}, 'left_win_pos_in_E': {0: 10.002042118364418, 1: 39.38459790393702, 2: 40.74003692229216, 3: 41.46513255508269, 4: 44.66179219947279, 5: 49.53272998148, 6: 51.82972979173252, 7: 53.82159300113625, 8: 55.40803086073492, 9: 55.76645477820397}, 'right_win_pos_in_E': {0: 41.48613320837913, 1: 44.6852679849016, 2: 49.56014983071213, 3: 51.82972979173252, 4: 53.85265044341121, 5: 55.40803086073492, 6: 55.79921126600202, 7: 56.66110947958804, 8: 59.119140585251095, 9: 61.39880967219205}, 'window_width': {0: 4095, 1: 249, 2: 373, 3: 418, 4: 341, 5: 197, 6: 124, 7: 84, 8: 106, 9: 161}, 'window_width_in_E': {0: 31.48409109001471, 1: 5.300670080964579, 2: 8.820112908419965, 3: 10.364597236649828, 4: 9.190858243938415, 5: 5.875300879254915, 6: 3.9694814742695, 7: 2.8395164784517917, 8: 3.7111097245161773, 9: 5.632354893988079}, 'sum_pulses_duration_in_E': {0: 0.5157099691135514, 1: 0.5408987779694527, 2: 0.6869248977656355, 3: 0.7304908951030242, 4: 0.7269657511683718, 5: 0.537271616198268, 6: 0.7609034761658222, 7: 0.6178183490930067, 8: 0.8269277926972265, 9: 0.5591109437337494}, 'sum_pulse_sq': {0: 3.7944375922206044, 1: 3.8756992116858715, 2: 2.9661915477796663, 3: 3.070559830941317, 4: 3.0597037730539385, 5: 10.2020204659669, 6: 45.77535573608872, 7: 45.87630607524008, 8: 39.10335270063814, 9: 3.437205923490125}, 'pulse_to_window_rate': {0: 0.01638001769335214, 1: 0.10204347180781788, 2: 0.07788164447530765, 3: 0.0704794290047244, 4: 0.0790966122938326, 5: 0.09144580460471718, 6: 0.1916883807363909, 7: 0.2175787158769594, 8: 0.22282493757444324, 9: 0.09926770493999569}, 'max_height_in_window': {0: 20.815950580921104, 1: 20.815950580921104, 2: 5.324888970962656, 3: 5.324888970962656, 4: 5.14075603114903, 5: 86.81228155905252, 6: 110.06755904473022, 7: 110.06755904473022, 8: 110.06755904473022, 9: 14.735092268739246}, 'min_height_in_window': {0: -0.011928180619527797, 1: 1.6172637244080776, 2: 1.6172637244080776, 3: 0.8658702248969847, 4: 0.8658702248969847, 5: 0.8658702248969847, 6: 1.8476229914953515, 7: 2.918666252051556, 8: 3.2397786967451707, 9: 2.4893555139463266}, 'windows_sq': {0: 655.3712842149647, 1: 110.33848645112575, 2: 46.96612194869083, 3: 55.19032951390669, 4: 47.24795994896218, 5: 510.0482741740266, 6: 436.911136546121, 7: 312.538647650477, 8: 408.4727887246568, 9: 82.9932690531994}} ) fig_w, axs_w = plt.subplots() axs_w.grid(color='grey', linestyle='--', linewidth=0.2) # 1. 批量创建矩形Patch并添加到Collection boxes = [ Rectangle( (row['left_win_pos_in_E'], row['min_height_in_window']), row['window_width_in_E'], row['max_height_in_window'] - row['min_height_in_window'] ) for _, row in windows_df.iterrows() ] pc = PatchCollection(boxes, facecolor='y', alpha=0.2, edgecolor='black') axs_w.add_collection(pc) # 2. 批量生成文本对象并一次性添加 all_texts = [] for idx, row in windows_df.iterrows(): # 起始位置标注 all_texts.append(axs_w.text( row['left_win_pos_in_E'], row['max_height_in_window'], str(idx), ha='center', va='center', fontsize=5 )) # 结束位置标注 all_texts.append(axs_w.text( row['right_win_pos_in_E'], row['max_height_in_window'] + 0.5 * row['min_height_in_window'], f"{idx}e", ha='center', va='center', fontsize=5 )) axs_w.texts.extend(all_texts) # 自动适配坐标轴范围 axs_w.autoscale() plt.show()
关键优化点
- 矩形渲染:保留PatchCollection批量渲染的优势,避免逐个添加Rectangle到轴上的重复开销。
- 文本添加:通过
ax.texts.extend()一次性将所有文本对象加入轴的文本列表,减少多次调用ax.text()带来的底层交互损耗。 - 超大数据量适配(可选):如果数据量极大,可以用Pandas向量化操作预计算所有文本的位置和内容,再批量生成文本对象,进一步压缩循环逻辑:
# 预计算所有文本参数 windows_df['start_x'] = windows_df['left_win_pos_in_E'] windows_df['start_y'] = windows_df['max_height_in_window'] windows_df['start_label'] = windows_df['window_index_num'].astype(str) windows_df['end_x'] = windows_df['right_win_pos_in_E'] windows_df['end_y'] = windows_df['max_height_in_window'] + 0.5 * windows_df['min_height_in_window'] windows_df['end_label'] = windows_df['window_index_num'].astype(str) + 'e' # 批量生成文本 all_texts = [] for _, row in windows_df.iterrows(): all_texts.append(axs_w.text(row['start_x'], row['start_y'], row['start_label'], ha='center', va='center', fontsize=5)) all_texts.append(axs_w.text(row['end_x'], row['end_y'], row['end_label'], ha='center', va='center', fontsize=5)) axs_w.texts.extend(all_texts)
注意事项
Matplotlib的Collection体系仅针对图形元素(如矩形、圆形)做批量优化,文本属于独立的Artist对象,无法直接整合到PatchCollection中
相关产品推荐
相关产品推荐

