matplotlib에서 특정 포인트 위로 마우스를 가져갈 때 레이블을 표시 할 수 있습니까?


148

matplotlib을 사용하여 산점도를 만들고 있습니다. 산점도의 각 점은 명명 된 객체와 연관됩니다. 해당 객체와 연관된 산점도의 점 위로 커서를 가져 가면 객체 이름을 볼 수 있기를 원합니다. 특히 특이 치인 점의 이름을 빠르게 볼 수 있다면 좋을 것입니다. 여기서 검색하는 동안 가장 가까운 것은 주석 명령이지만 줄거리에 고정 레이블을 만드는 것으로 보입니다. 불행히도, 내가 가지고있는 포인트의 수로, 각 포인트에 레이블을 지정하면 산점도를 읽을 수 없습니다. 커서가 해당 지점 근처에있을 때만 나타나는 레이블을 만드는 방법을 아는 사람이 있습니까?


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"]실제로는 커서 아래의 모든 포인트에 대한 인덱스 목록 이라는 것을 알았습니다 . 즉, 위의 코드는 실제로 최상위 지점뿐만 아니라 특정 위치의 모든 지점에 액세스 할 수 있음을 의미합니다. 예를 들어, 겹치는 지점이 두 개인 경우 텍스트가 읽히 1 2, B C거나 1 2 3, B C D3 개의 겹치는 지점이있는 경우 에도 텍스트를 읽을 수 있습니다.
Jvinniec

@Jvinniec 정확하게, 위의 도표 (x ~ 0.4의 녹색과 적색 점)에 의도적으로 그러한 경우가 있습니다. 마우스를 가져 가면이 표시됩니다 0 8, A I( 그림 참조 ).
ImportanceOfBeingErnest

@ImportanceOfBeingErnest 이것은 훌륭한 코드이지만 포인트를 가리 키거나 움직일 때 fig.canvas.draw_idle()여러 번 호출 합니다 (커서를 유휴 상태로 변경하기도 함). 나는 이전 색인을 저장하고 확인했다 ind["ind"][0] == prev_ind. 그런 다음 한 지점에서 다른 지점으로 이동하거나 (텍스트 업데이트) 호버링을 중지하거나 (주석이 보이지 않게) 호버링을 시작 (주석이 보이도록)하는 경우에만 업데이트하십시오. 이 변경으로 더 깨끗하고 효율적입니다.
Sembei Norimaki

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

5
흠-이것은 나에게 기본적으로 작동하지 않았습니다 (그래서 할 일이 거의 없습니다 matplotlib...)- ipython/ jupyter노트북에서 작동 합니까? 서브 플로트가 여러 개인 경우에도 작동합니까? 선 그래프가 아닌 막대 차트는 어떻습니까?
dwanderson

12
마우스를 가져 가면 라벨이 콘솔에 인쇄됩니다. 무엇에 대한 라벨이 사진에 나타나도록 유혹 할 때? 나는 그것이 질문이라는 것을 이해했다.
Nikana Reklawyks

@ mbernasocchi는 대단히 감사합니다. 히스토그램 (스 캐터의 각 지점마다 다른) 또는 더 나은 2D 히스토그램의 히트 맵을 보려면 gid 인수에 무엇을 공급해야합니까?
Amitai

@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()

이것은 내가 필요한 것을합니다. 감사합니다! 보너스로 구현하기 위해 동일한 그림에서 서로 다른 색상으로 두 개의 개별 산점도를 만들어 두 세트의 데이터를 나타내는 대신 포인트에 색상을 할당하는 예제 방법을 복사하도록 프로그램을 다시 작성했습니다. 이로 인해 내 프로그램을 조금 더 읽기 쉽고 코드는 줄였습니다. 이제 색상을 숫자로 변환하는 방법에 대한 가이드를 찾으십시오!
jdmcbr

1
이것은 산점도입니다. 선 그림은 어떻습니까? 나는 그것들을 작동 시키려고했지만 효과가 없습니다. 해결 방법이 있습니까?
Sohaib

@Sohaib 내 답변보기
texasflood

이것에 대한 질문이 있습니다. plt.scatter (X_reduced [y == i, 0], X_reduced [y == i, 1], c = c, label = target_name, picker = True)와 같이 i, c 및 target_name, 인덱스 순서가 엉망입니까? 그리고 더 이상 어떤 데이터 포인트를 찾을 수 없습니까?
Chris

ipython 5가 설치된 jupyter 5 노트북에서는 작동하지 않는 것 같습니다. 쉽게 해결할 수 있습니까? 이 print문장은 파이썬 3과의 호환성을 위해 parens를 사용해야합니다
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()

예제를 확인할 수 있습니다


샘플 코드를 포함시키고 컨텍스트 나 정보가없는 외부 소스에 연결하지 마십시오. 자세한 내용 은 도움말 센터 를 참조하십시오.
Joseph Farah

5
불행히도 mpld3는 더 이상 2017 년 7 월 현재 더 이상 적극적으로 관리되지 않습니다
Ben Lindsay

코드 샘플이로 실패합니다 TypeError: array([1.]) is not JSON serializable.
P-Gn

@ P-Gn은 여기 트릭을 따르십시오 stackoverflow.com/questions/48015030/mpld3-with-python-error MPLD3는 이것에 대한 간단한 해결책이며 위의 답변을 따르면 작동합니다.
Zalakain

1
@Zalakain 불행히도, mpl3d은 포기 된 것으로 보입니다 .
P-Gn

5

mplcursors가 나를 위해 일했습니다. mplcursors는 matplotlib에 대해 클릭 가능한 주석을 제공합니다. API가 훨씬 단순화 된 mpldatacursor ( https://github.com/joferkington/mpldatacursor ) 에서 많은 영향을 받았습니다.

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
이것에 대한 소스는 ( untributed
Victoria Stuart

Jupyter Lab 에서이 작업을 수행 할 수 없었습니다. Jupyter 랩톱에서는 작동하지만 Jupyter 랩에서는 작동하지 않습니까?
MD004

3

Jupyter 노트북을 사용하는 경우 솔루션은 다음과 같이 간단합니다.

%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.