I am using matplotlib to make scatter plots. Each point on the scatter plot is associated with a named object. I would like to be able to see the name of an object when I hover my cursor over the point on the scatter plot associated with that object. In particular, it would be nice to be able to quickly see the names of the points that are outliers. The closest thing I have been able to find while searching here is the annotate command, but that appears to create a fixed label on the plot. Unfortunately, with the number of points that I have, the scatter plot would be unreadable if I labeled each point. Does anyone know of a way to create labels that only appear when the cursor hovers in the vicinity of that point?
问题:
回答1:
It seems none of the other answers here actually answer the question. So here is a code that uses a scatter and shows an annotation upon hovering over the scatter points.
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()
Because people suddenly also want to use this solution for a line plot
instead of a scatter, the following would be the same solution for plot
(which works slightly differently).
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()
In case someone is looking for a solution for bar plots, please refer to e.g. this answer.
回答2:
I know it\'s an old question, but I kept on arriving here while looking for a solution to hover (not click on) a line.
import matplotlib.pyplot as plt
fig = plt.figure()
plot = fig.add_subplot(111)
# create some curves
for i in range(4):
plot.plot(
[i*1,i*2,i*3,i*4],
gid=i)
def on_plot_hover(event):
for curve in plot.get_lines():
if curve.contains(event)[0]:
print \"over %s\" % curve.get_gid()
fig.canvas.mpl_connect(\'motion_notify_event\', on_plot_hover)
plt.show()
回答3:
From 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()
- This recipe draws an annotation on picking a data point: http://scipy-cookbook.readthedocs.io/items/Matplotlib_Interactive_Plotting.html .
- This recipe draws a tooltip, but it requires wxPython: Point and line tooltips in matplotlib?
回答4:
A slight edit on an example provided in 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()
This plots a straight line plot, as Sohaib was asking
回答5:
mpld3 solve it for me. EDIT (CODE ADDED):
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()
You can check this example
回答6:
mplcursors worked for me. mplcursors provides clickable annotation for matplotlib. It is heavily inspired from mpldatacursor (https://github.com/joferkington/mpldatacursor), with a much simplified 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()