matplotlibのポイントにカーソルを合わせるとラベルが表示されるようにすることは可能ですか?


146

私はmatplotlibを使用して散布図を作成しています。散布図の各ポイントは、名前付きオブジェクトに関連付けられています。オブジェクトに関連付けられている散布図のポイントにカーソルを合わせると、オブジェクトの名前が表示されます。特に、異常値であるポイントの名前をすばやく確認できると便利です。ここで検索しているときに見つけた最も近いものはannotateコマンドですが、それはプロット上に固定ラベルを作成するように見えます。残念ながら、私が持っているポイントの数では、各ポイントにラベルを付けた場合、散布図は判読できません。カーソルがそのポイントの近くに移動したときにのみ表示されるラベルを作成する方法を誰かが知っていますか?


2
検索でここまでたどり着く人々もこの回答を確認したいと思うかもしれません。これはかなり複雑ですが、要件によっては適切な場合があります。
ImportanceOfBeingErnest

回答:


133

ここの他の答えは実際には質問に答えていないようです。したがって、スキャッターを使用し、スキャッターポイントにカーソルを合わせると注釈が表示されるコードを次に示します。

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(動作は少し異なります)。

誰かがツイン軸の線の解決策を探している場合は、「複数の軸のポイントにカーソルを合わせたときにラベルを表示する方法」を参照してください

誰かが棒グラフの解決策を探している場合は、たとえばこの回答を参照してください。


1
非常に素晴らしい!一つの注意、私はそれind["ind"]が実際にはカーソルの下のすべてのポイントのインデックスのリストであることに気づきました。つまり、上記のコードは、実際には、最上部のポイントだけでなく、特定の位置にあるすべてのポイントへのアクセスを提供します。たとえば、2つの重複するポイントがある場合、テキストは読み取れる可能性があり、3つの重複するポイントがある場合1 2, B Cでも1 2 3, B C Dです。
Jvinniec 2017年

@Jvinniecまさに、上のプロットには意図的にそのようなケースが1つあります(x〜0.4の緑と赤の点)。カーソルを合わせると、が表示されます0 8, A I画像を参照)。
ImportanceOfBeingErnest

@ImportanceOfBeingErnestこれは優れたコードですが、ポイントの上にホバーして移動すると、fig.canvas.draw_idle()何度も呼び出されます(カーソルがアイドルに変わることさえあります)。以前のインデックスを保存し、かどうかを確認して解決しましたind["ind"][0] == prev_ind。次に、あるポイントから別のポイントに移動する(テキストを更新する)、ホバリングを停止する(注釈を非表示にする)、またはホバリングを開始する(注釈を表示する)場合にのみ更新します。この変更により、よりクリーンで効率的になりました。
船平則巻

3
@Konstantinはい、このソリューションは%matplotlib notebookIPython / Jupyterノートブックで使用すると機能します。
ImportanceOfBeingErnest

1
@OriolAbril(および他のすべての人)、この回答からコードを変更するときに発生した問題がある場合は、それについて質問し、この回答にリンクして、試行したコードを表示してください。実際に確認することなく、各コードの何が問題なのかを知る方法はありません。
ImportanceOfBeingErnest

66

このソリューションは、クリックする必要なしにラインをホバーするときに機能します。

import matplotlib.pyplot as plt

# Need to create as global variable so our callback(on_plot_hover) can access
fig = plt.figure()
plot = fig.add_subplot(111)

# create some curves
for i in range(4):
    # Giving unique ids to each data member
    plot.plot(
        [i*1,i*2,i*3,i*4],
        gid=i)

def on_plot_hover(event):
    # Iterating over each data member plotted
    for curve in plot.get_lines():
        # Searching which data member corresponds to current mouse position
        if curve.contains(event)[0]:
            print "over %s" % curve.get_gid()

fig.canvas.mpl_connect('motion_notify_event', on_plot_hover)           
plt.show()

1
非常に便利な+1です。カーブ領域内のモーションに対してmotion_notify_eventが繰り返されるため、おそらくこれを「デバウンス」する必要があります。カーブオブジェクトが前のカーブと等しいことを確認するだけで機能するようです。
bvanlew 16

5
うーん-これは私にとっては最初からうまくいかなかった(それでいくつかのことはmatplotlib...)-これはipython/ jupyterノートブックで動作しますか?複数のサブプロットがある場合にも機能しますか?線グラフではなく棒グラフではどうですか?
dwanderson 2017年

12
これにより、ホバリング時にコンソールにラベルが印刷されます。何についてのラベルが画面上に現れる作るホバリング時に?それが問題だと理解しました。
Nikana Reklawyks 2017

@mbernasocchiは感謝します。ヒストグラム(散布図の各ポイントごとに異なるもの)を表示したい場合、gid引数に何を入力する必要がありますか。さらに良いのは、2Dヒストグラムのヒートマップです。
アミタイ2017年

@NikanaReklawyks私は実際に質問に答える答えを追加しまし
ImportanceOfBeingErnest

37

http://matplotlib.sourceforge.net/examples/event_handling/pick_event_demo.htmlから:

from matplotlib.pyplot import figure, show
import numpy as npy
from numpy.random import rand


if 1: # picking on a scatter plot (matplotlib.collections.RegularPolyCollection)

    x, y, c, s = rand(4, 100)
    def onpick3(event):
        ind = event.ind
        print('onpick3 scatter:', ind, npy.take(x, ind), npy.take(y, ind))

    fig = figure()
    ax1 = fig.add_subplot(111)
    col = ax1.scatter(x, y, 100*s, c, picker=True)
    #fig.savefig('pscoll.eps')
    fig.canvas.mpl_connect('pick_event', onpick3)

show()

これは私が必要としていることだけを行います、ありがとう!おまけとして、それを実装するために、同じ図上に異なる色で2つの個別の散布図を作成して2つのデータセットを表すのではなく、例に色を点に割り当てる方法をコピーするようにプログラムを書き直しました。これにより、プログラムが少し読みやすくなり、コードが少なくなりました。色を数値に変換するためのガイドを見つけましょう!
jdmcbr 2011年

1
これは散布図用です。ラインプロットはどうですか?私はそれを彼らに働かせようとしたがそれはしなかった。回避策はありますか?
Sohaib 2014

@Sohaib私の答えをご覧ください
texasflood

これについて質問があります。私のポイントを次のように散布図すると:plt.scatter(X_reduced [y == i、0]、X_reduced [y == i、1]、c = c、label = target_name、picker = True)とzip i、c、target_nameの場合、インデックスの順序が乱れていますか?そして、それがどのデータポイントに属しているのかもう調べることができませんか?
クリス

これはipython 5のjupyter 5ノートブックでは機能しないようです。簡単に修正する方法はありますか?print声明はまたのpython 3との互換性のために括弧を使用する必要があります
nealmcb

14

http://matplotlib.org/users/shell.htmlで提供されている例を少し編集します。

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click on points')

line, = ax.plot(np.random.rand(100), '-', picker=5)  # 5 points tolerance


def onpick(event):
    thisline = event.artist
    xdata = thisline.get_xdata()
    ydata = thisline.get_ydata()
    ind = event.ind
    print('onpick points:', *zip(xdata[ind], ydata[ind]))


fig.canvas.mpl_connect('pick_event', onpick)

plt.show()

これは、Sohaibが求めていたように、直線プロットをプロットします


5

mpld3は私のためにそれを解決します。編集(コード追加):

import matplotlib.pyplot as plt
import numpy as np
import mpld3

fig, ax = plt.subplots(subplot_kw=dict(axisbg='#EEEEEE'))
N = 100

scatter = ax.scatter(np.random.normal(size=N),
                 np.random.normal(size=N),
                 c=np.random.random(size=N),
                 s=1000 * np.random.random(size=N),
                 alpha=0.3,
                 cmap=plt.cm.jet)
ax.grid(color='white', linestyle='solid')

ax.set_title("Scatter Plot (with tooltips!)", size=20)

labels = ['point {0}'.format(i + 1) for i in range(N)]
tooltip = mpld3.plugins.PointLabelTooltip(scatter, labels=labels)
mpld3.plugins.connect(fig, tooltip)

mpld3.show()

この例を確認できます


サンプルコードを含めてください。コンテキストや情報のない外部ソースにリンクするだけではありません。詳しくはヘルプセンターをご覧ください。
ジョセフファラー2017年

5
残念ながら、mpld3は2017
Ben Lindsay

コードサンプルはで失敗しますTypeError: array([1.]) is not JSON serializable
P-Gn 2018

@ P-Gnはちょうどここのトリックに従っstackoverflow.com/questions/48015030/mpld3-with-python-error MPLD3は、このためのシンプルなソリューションであり、上記の回答が続いていると、それが動作します。
ザラカイン

1
@Zalakain残念ながら、mpl3dは放棄されているようです。
P-Gn

5

mplcursorsは私のために働いた。mplcursorsは、matplotlibにクリック可能な注釈を提供します。mpldatacursor(https://github.com/joferkington/mpldatacursor)から大きく影響を受け、APIが大幅に簡略化されています

import matplotlib.pyplot as plt
import numpy as np
import mplcursors

data = np.outer(range(10), range(1, 5))

fig, ax = plt.subplots()
lines = ax.plot(data)
ax.set_title("Click somewhere on a line.\nRight-click to deselect.\n"
             "Annotations can be dragged.")

mplcursors.cursor(lines) # or just mplcursors.cursor()

plt.show()

私はこれを自分で使用しています。急いでいる人にとっては、はるかに簡単な解決策です。私は70のラベルをプロットしmatplotlib、10行ごとに同じ色にしました。mplcursorsそれを整理します。
ajsp

5

他の回答では、Jupyterインラインmatplotlibの最新バージョンの図でツールチップを適切に表示する必要性に対処できませんでした。これはうまくいきます:

import matplotlib.pyplot as plt
import numpy as np
import mplcursors
np.random.seed(42)

fig, ax = plt.subplots()
ax.scatter(*np.random.random((2, 26)))
ax.set_title("Mouse over a point")
crs = mplcursors.cursor(ax,hover=True)

crs.connect("add", lambda sel: sel.annotation.set_text(
    'Point {},{}'.format(sel.target[0], sel.target[1])))
plt.show()

マウスでポイントの上を移動すると、次のような画像が表示されます。 ここに画像の説明を入力してください


3
このソース(未配布
Victoria Stuart

私はjupyter labでこれを機能させることができませんでした。それはおそらくjupyterノートブックでは機能しますが、jupyterラボでは機能しませんか?
MD004

3

jupyter Notebookを使用している場合、私の解決策は次のように簡単です。

%pylab
import matplotlib.pyplot as plt
import mplcursors
plt.plot(...)
mplcursors.cursor(hover=True)
plt.show()

あなたは次のようなものを得ることができます ここに画像の説明を入力してください


断然最良の解決策は、わずか数行のコードだけが、OPが求めていることを正確に実行することです
Tim Johnsen

0

追加する複数行注釈システムを作成しました:https : //stackoverflow.com/a/47166787/10302020。最新バージョンの場合:https : //github.com/AidenBurgess/MultiAnnotationLineGraph

下のセクションのデータを変更するだけです。

import matplotlib.pyplot as plt


def update_annot(ind, line, annot, ydata):
    x, y = line.get_data()
    annot.xy = (x[ind["ind"][0]], y[ind["ind"][0]])
    # Get x and y values, then format them to be displayed
    x_values = " ".join(list(map(str, ind["ind"])))
    y_values = " ".join(str(ydata[n]) for n in ind["ind"])
    text = "{}, {}".format(x_values, y_values)
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(0.4)


def hover(event, line_info):
    line, annot, ydata = line_info
    vis = annot.get_visible()
    if event.inaxes == ax:
        # Draw annotations if cursor in right position
        cont, ind = line.contains(event)
        if cont:
            update_annot(ind, line, annot, ydata)
            annot.set_visible(True)
            fig.canvas.draw_idle()
        else:
            # Don't draw annotations
            if vis:
                annot.set_visible(False)
                fig.canvas.draw_idle()


def plot_line(x, y):
    line, = plt.plot(x, y, marker="o")
    # Annotation style may be changed here
    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)
    line_info = [line, annot, y]
    fig.canvas.mpl_connect("motion_notify_event",
                           lambda event: hover(event, line_info))


# Your data values to plot
x1 = range(21)
y1 = range(0, 21)
x2 = range(21)
y2 = range(0, 42, 2)
# Plot line graphs
fig, ax = plt.subplots()
plot_line(x1, y1)
plot_line(x2, y2)
plt.show()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.