¿Es posible hacer que aparezcan tags al pasar sobre un punto en matplotlib?

Estoy usando matplotlib para hacer diagtwigs de dispersión. Cada punto en el diagtwig de dispersión está asociado con un objeto nombrado. Me gustaría poder ver el nombre de un objeto cuando muevo el cursor sobre el punto en el diagtwig de dispersión asociado con ese objeto. En particular, sería bueno poder ver rápidamente los nombres de los puntos que son valores atípicos. Lo más cercano que he podido encontrar al buscar aquí es el comando de anotación, pero parece que se crea una etiqueta fija en el gráfico. Desafortunadamente, con la cantidad de puntos que tengo, el diagtwig de dispersión sería ilegible si rotulara cada punto. ¿Alguien sabe de una manera de crear tags que solo aparezcan cuando el cursor se desplaza cerca de ese punto?

Parece que ninguna de las otras respuestas aquí realmente responde la pregunta. Así que aquí hay un código que utiliza una dispersión y muestra una anotación al pasar el ratón sobre los puntos de dispersión.

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

introduzca la descripción de la imagen aquí

Debido a que las personas de repente también quieren usar esta solución para una plot líneas en lugar de una dispersión, la siguiente sería la misma solución para la plot (que funciona de manera ligeramente diferente).

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

Sé que es una pregunta antigua, pero seguí llegando aquí mientras buscaba una solución para pasar (no hacer clic en) una línea.

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

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

Una pequeña edición en un ejemplo proporcionado en 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() 

Esto traza un gráfico de línea recta, como Sohaib estaba preguntando

mpld3 resuélvelo por mi. EDITAR (CÓDIGO AÑADIDO):

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

Puedes comprobar este ejemplo

mplcursors trabajaron para mi mplcursors proporciona anotaciones cliqueables para matplotlib. Está muy inspirado en mpldatacursor ( https://github.com/joferkington/mpldatacursor ), con una API muy simplificada

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

Las otras respuestas no respondían a mi necesidad de mostrar información sobre herramientas correctamente en una versión reciente de Jupyter inline matplotlib figure. Este funciona sin embargo:

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

Conduciendo a algo como la siguiente imagen al pasar sobre un punto con el mouse: introduzca la descripción de la imagen aquí

He hecho un sistema de anotación multilínea para agregar a: https://stackoverflow.com/a/47166787/10302020 . para la versión más actualizada: https://github.com/AidenBurgess/MultiAnnotationLineGraph

Simplemente cambia los datos en la sección inferior.

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