1. ホーム
  2. パイソン

[解決済み】matplotlibでホバーリングアノテーションを追加する方法

2022-04-11 02:49:58

質問

matplotlibを使って散布図を作っています。散布図上の各点は、名前の付いたオブジェクトと関連付けられています。オブジェクトに関連付けられた散布図上の点の上にカーソルを置くと、そのオブジェクトの名前が表示されるようにしたいのですが、どうすればよいでしょうか?特に、外れ値である点の名前を素早く見ることができればいいと思います。ここで検索している間、最も近いものはannotateコマンドですが、それはプロット上に固定ラベルを作成するようです。残念ながら、私が持っている点の数では、各点にラベルを付けると散布図が読めなくなります。どなたか、カーソルをその点の近くに置いたときだけ表示されるラベルを作成する方法をご存じないでしょうか?

どのように解決するのですか?

他のどの回答も、実際にその質問に答えていないようです。そこで、ここでは スキャッター を表示し アノテーション に対して ホバリング を散布点上に表示します。

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)

x = np.random.rand(15)
y = np.random.rand(15)
names = np.array(list("ABCDEFGHIJKLMNO"))
c = np.random.randint(1,5,size=15)

norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn

fig,ax = plt.subplots()
sc = plt.scatter(x,y,c=c, s=100, cmap=cmap, norm=norm)

annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="w"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)

def update_annot(ind):

    pos = sc.get_offsets()[ind["ind"][0]]
    annot.xy = pos
    text = "{}, {}".format(" ".join(list(map(str,ind["ind"]))), 
                           " ".join([names[n] for n in ind["ind"]]))
    annot.set_text(text)
    annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
    annot.get_bbox_patch().set_alpha(0.4)


def hover(event):
    vis = annot.get_visible()
    if event.inaxes == ax:
        cont, ind = sc.contains(event)
        if cont:
            update_annot(ind)
            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()

この解決策を行に使用したい人もいるからです。 plot 散布図ではなく、次のような解決策になります。 plot (若干動作が異なりますが)。

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)

x = np.sort(np.random.rand(15))
y = np.sort(np.random.rand(15))
names = np.array(list("ABCDEFGHIJKLMNO"))

norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn

fig,ax = plt.subplots()
line, = plt.plot(x,y, marker="o")

annot = ax.annotate("", xy=(0,0), xytext=(-20,20),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="w"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)

def update_annot(ind):
    x,y = line.get_data()
    annot.xy = (x[ind["ind"][0]], y[ind["ind"][0]])
    text = "{}, {}".format(" ".join(list(map(str,ind["ind"]))), 
                           " ".join([names[n] for n in ind["ind"]]))
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(0.4)


def hover(event):
    vis = annot.get_visible()
    if event.inaxes == ax:
        cont, ind = line.contains(event)
        if cont:
            update_annot(ind)
            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()

2軸の線に関する解決策をお探しの場合は、以下を参照してください。 複数軸でポイントにカーソルを合わせるとラベルが表示されるようにするには?

棒グラフの解決策を探している人がいたら、以下を参照してください。 この回答 .