J'utilise matplotlib pour réaliser des diagrammes de dispersion. Chaque point du nuage de points est associé à un objet nommé. J'aimerais pouvoir voir le nom d'un objet lorsque je passe mon curseur sur le point du nuage de points associé à cet objet. En particulier, il serait agréable de pouvoir voir rapidement le nom des points qui sont des valeurs aberrantes. La chose la plus proche que j'ai pu trouver en cherchant ici est la commande annoter, mais elle semble créer une étiquette fixe sur le graphique. Malheureusement, avec le nombre de points que j'ai, le nuage de points serait illisible si j'étiquetais chaque point. Quelqu'un connaît-il un moyen de créer des étiquettes qui n'apparaissent que lorsque le curseur se trouve à proximité du point en question ?
Réponses
Trop de publicités?Il semble qu'aucune des autres réponses ici ne réponde réellement à la question. Voici donc un code qui utilise un se disperser et montre un annotation sur en vol stationnaire sur les points de dispersion.
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()
Parce que les gens veulent aussi utiliser cette solution pour une ligne plot
au lieu d'une diffusion, ce qui suit serait la même solution pour plot
(qui fonctionne de manière légèrement différente).
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()
Au cas où quelqu'un chercherait une solution pour les lignes dans les axes jumelés, se référer à Comment faire apparaître les étiquettes lors du survol d'un point dans un axe multiple ?
Si quelqu'un cherche une solution pour les diagrammes en bâtons, veuillez vous référer, par exemple, à l'article suivant : "Les diagrammes en bâtons". cette réponse .
Cette solution fonctionne lors du survol d'une ligne sans qu'il soit nécessaire de la cliquer :
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()
De 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()
- Cette recette dessine une annotation lors de la sélection d'un point de données : http://scipy-cookbook.readthedocs.io/items/Matplotlib_Interactive_Plotting.html .
- Cette recette dessine une infobulle, mais elle nécessite wxPython : Infobulles pour les points et les lignes dans matplotlib ?
Une légère modification d'un exemple fourni dans 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()
Ce tracé est une ligne droite, comme le demandait Sohaib.
- Réponses précédentes
- Plus de réponses