You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib散点图鼠标悬停显示额外数据失效问题求助(Jupyter)

解决Matplotlib散点图Jupyter Notebook中悬停提示失效问题

问题描述

需要在Matplotlib绘制的散点图上,鼠标悬停时显示数据框中未用于绘图的其他列数据,因依赖Matplotlib其他功能,不考虑使用Plotly。但编写的脚本在Jupyter Notebook中运行时,悬停功能无法正常生效。

原代码

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib widget
# Load sample data
diamonds = sns.load_dataset('diamonds')

# Create a scatter plot with hue and use matplotlib to display the specific column values when hovering over a datapoint
sns.scatterplot(x="carat", y="price", hue="cut", data=diamonds)

def hover(event):
    # Get the current mouse position
    ax = event.inaxes
    x, y = event.xdata, event.ydata

    # Find the nearest datapoint
    distances = ((diamonds['carat'] - x)**2 + (diamonds['price'] - y)**2)
    idx = distances.idxmin()
    
    # Display the specific column values in a pop-up window
    text = f"Cut: {diamonds.loc[idx, 'cut']}\n" \
           f"Clarity: {diamonds.loc[idx, 'clarity']}\n" \
           f"Color: {diamonds.loc[idx, 'color']}"
    plt.gcf().text(x, y, text, ha='left', va='bottom', fontsize=10, backgroundcolor='white', alpha=0.7)

# Connect the hover function to the figure
fig = plt.gcf()
fig.canvas.mpl_connect("motion_notify_event", hover)

# Show the plot
plt.show()

问题原因

  1. 每次鼠标移动都会新增文本元素,旧元素未被清除,导致提示重叠且无法正常更新
  2. 未判断鼠标是否处于坐标轴范围内,鼠标移出图区时会触发错误
  3. 使用plt.gcf().text()添加文本的方式不适合动态更新的悬停提示,缺乏对提示元素的可控性

修复方案

改用Matplotlib的Annotation对象来管理悬停提示,每次鼠标移动时更新该对象的内容和位置,同时处理鼠标移出图区的情况:

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib widget

# 加载示例数据
diamonds = sns.load_dataset('diamonds')

# 创建散点图
fig, ax = plt.subplots()
sns.scatterplot(x="carat", y="price", hue="cut", data=diamonds, ax=ax)

# 初始化注释对象,初始状态隐藏
annot = ax.annotate("", xy=(0,0), xytext=(10,10), textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="white", alpha=0.8),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)

def update_annot(idx):
    # 更新注释内容和位置
    pos = ax.transData.transform((diamonds.loc[idx, 'carat'], diamonds.loc[idx, 'price']))
    annot.xy = ax.transData.inverted().transform(pos)
    text = f"Cut: {diamonds.loc[idx, 'cut']}\n" \
           f"Clarity: {diamonds.loc[idx, 'clarity']}\n" \
           f"Color: {diamonds.loc[idx, 'color']}"
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(0.8)

def hover(event):
    vis = annot.get_visible()
    # 仅当鼠标在当前坐标轴内时处理
    if event.inaxes == ax:
        # 计算最近数据点
        distances = ((diamonds['carat'] - event.xdata)**2 + (diamonds['price'] - event.ydata)**2)
        idx = distances.idxmin()
        # 更新注释并显示
        update_annot(idx)
        annot.set_visible(True)
        fig.canvas.draw_idle()
    else:
        # 鼠标移出图区时隐藏注释
        if vis:
            annot.set_visible(False)
            fig.canvas.draw_idle()

# 绑定悬停事件
fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()

关键修改说明

  • 使用Annotation对象统一管理悬停提示,支持动态更新内容和位置,避免文本堆积
  • 添加鼠标位置判断,仅在鼠标处于图区内时触发提示更新,移出时隐藏提示
  • 通过draw_idle()触发画布重绘,确保提示实时更新
  • 优化提示框样式,添加箭头指向数据点,提升交互体验

内容的提问来源于stack exchange,提问作者Taiwo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 01:40:38