如何用adjust_text调整局部高低点标注:低点在下、高点在上?
解决方案
要强制局部最大值标注在曲线上方、最小值标注在曲线下方,核心是区分点的类型并调整标注的y坐标,再配合adjust_text的参数避免自动调整破坏位置。
步骤1:区分局部高点/低点
首先确保你能识别每个标注点是峰值(局部最大值)还是谷值(局部最小值)。如果graph_annotates没有现成的标识列,可以通过对比该点与前后数据的数值来判断:
# 为graph_annotates添加点类型标识 graph_annotates['point_type'] = 'peak' # 默认设为峰值 y_col = 'mentee_per_mentor' date_col = 'date' for idx in graph_annotates.index: current_date = graph_annotates[date_col].iloc[idx] current_val = graph_annotates[y_col].iloc[idx] # 获取当前点的前一个值(最近的更早日期) prev_rows = df_for_pred[df_for_pred[date_col] < current_date] prev_val = prev_rows[y_col].iloc[-1] if not prev_rows.empty else current_val # 获取当前点的后一个值(最近的更晚日期) next_rows = df_for_pred[df_for_pred[date_col] > current_date] next_val = next_rows[y_col].iloc[0] if not next_rows.empty else current_val # 判断是否为谷值 if current_val < prev_val and current_val < next_val: graph_annotates['point_type'].iloc[idx] = 'trough'
步骤2:调整标注位置并创建文本
基于点的类型,给标注添加相对数据范围的y轴偏移,同时设置垂直对齐方式让文本位置更合理:
# 计算相对偏移量(基于数据范围的5%,适配不同尺度) y_min = df_for_pred[y_col].min() y_max = df_for_pred[y_col].max() y_offset = (y_max - y_min) * 0.05 labels = [] for idx in graph_annotates.index: x = graph_annotates[date_col].iloc[idx] base_y = graph_annotates[y_col].iloc[idx] val_text = f"{base_y:.2f}" point_type = graph_annotates['point_type'].iloc[idx] # 调整y坐标:峰值上移,谷值下移 if point_type == 'peak': adjusted_y = base_y + y_offset vertical_align = 'bottom' # 文本底部对齐点的上方 else: adjusted_y = base_y - y_offset vertical_align = 'top' # 文本顶部对齐点的下方 # 创建标注文本 label = plt.text(x, adjusted_y, val_text, verticalalignment=vertical_align) labels.append(label)
步骤3:限制adjust_text的调整方向
默认的adjust_text可能会把标注拉回错误位置,所以限制它只在y轴方向微调,避免破坏上下布局:
adjust_text(labels, only_move={'points': 'y', 'texts': 'y'}, force_text=0.5)
完整代码整合
# graph plot plt.plot( df_for_pred['date'], df_for_pred['mentee_per_mentor'], color="r" ) plt.title('Mentee per mentor dynamic') plt.grid(False) # --- 区分高点/低点 --- graph_annotates['point_type'] = 'peak' y_col = 'mentee_per_mentor' date_col = 'date' for idx in graph_annotates.index: current_date = graph_annotates[date_col].iloc[idx] current_val = graph_annotates[y_col].iloc[idx] prev_rows = df_for_pred[df_for_pred[date_col] < current_date] prev_val = prev_rows[y_col].iloc[-1] if not prev_rows.empty else current_val next_rows = df_for_pred[df_for_pred[date_col] > current_date] next_val = next_rows[y_col].iloc[0] if not next_rows.empty else current_val if current_val < prev_val and current_val < next_val: graph_annotates['point_type'].iloc[idx] = 'trough' # --- 调整标注位置 --- y_min = df_for_pred[y_col].min() y_max = df_for_pred[y_col].max() y_offset = (y_max - y_min) * 0.05 labels = [] for idx in graph_annotates.index: x = graph_annotates[date_col].iloc[idx] base_y = graph_annotates[y_col].iloc[idx] val_text = f"{base_y:.2f}" point_type = graph_annotates['point_type'].iloc[idx] if point_type == 'peak': adjusted_y = base_y + y_offset va = 'bottom' else: adjusted_y = base_y - y_offset va = 'top' label = plt.text(x, adjusted_y, val_text, verticalalignment=va) labels.append(label) # --- 限制标注调整方向 --- adjust_text(labels, only_move={'points': 'y', 'texts': 'y'}, force_text=0.5) plt.show()
关键说明
- 相对偏移量:避免因数据尺度不同导致偏移过大/过小
- 垂直对齐参数:让文本与曲线的位置关系更自然,减少覆盖情况
only_move参数:防止adjust_text自动调整破坏标注的上下位置
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

