Possible to make labels appear when hovering over a point in matplotlib in stem plot?

Question:

I am new to matplotlib and I am looking to label stems in a stem plot with x,y co-od when mouse hovers over that point. When I searched everything was meant for scatter plot (Possible to make labels appear when hovering over a point in matplotlib? present code is like this:

def plot_matching(mzs,ints,matching,scan_num):
fig=p1.gcf()
fig.canvas.set_window_title('MS/MS Viewer')
rel_ints=relative_intensity(ints)
p1.xlim(min(mzs)-100,max(mzs)+100)
p1.ylim(min(rel_ints),max(rel_ints)+5)
p1.title('Scan Number:'+scan_num)
p1.xlabel('m/z')
p1.ylabel('Relative intensity')
mzs_rel=zip(mzs,rel_ints)    
for x,y in mzs_rel:
    x1=[]
    y1=[]
    x1.append(x)
    y1.append(y)
    markerline, stemlines, baseline=p1.stem(x1,y1)
    p1.setp(markerline, 'Marker', '')
    for m in matching:
        if x==m[1] and y>3.0:
            p1.setp(stemlines, linewidth=2, color='r')
            p1.text(x,y,m[0],fontsize=12)
            break
        else:
            p1.setp(stemlines,linewidth=2, color='g')
return p1

Will the scatter plot link for stem plot too?

Asked By: user2998764

||

Answers:

To make a hovering label, you need to hook up a function to handle motion_notify_events:

    plt.connect('motion_notify_event', some_function)

Below is some code showing one way to do it. The hovering label behavior is produced by

cursor = FollowDotCursor(ax, x, y)

where ax is the axis, x and y are lists of coordinates. Since you supply x and y, it does not matter if you are making a line plot or a stem plot or whatever. The labels appear when the mouse is moved near any point (xi, yi).

The code below uses scipy.spatial.cKDTree to locate the nearest point. Here is an older version of this code which does not require scipy.


import matplotlib.pyplot as plt
import scipy.spatial as spatial
import numpy as np
pi = np.pi
cos = np.cos

def fmt(x, y):
    return 'x: {x:0.2f}ny: {y:0.2f}'.format(x=x, y=y)

class FollowDotCursor(object):
    """Display the x,y location of the nearest data point.
    https://stackoverflow.com/a/4674445/190597 (Joe Kington)
    https://stackoverflow.com/a/13306887/190597 (unutbu)
    https://stackoverflow.com/a/15454427/190597 (unutbu)
    """
    def __init__(self, ax, x, y, tolerance=5, formatter=fmt, offsets=(-20, 20)):
        try:
            x = np.asarray(x, dtype='float')
        except (TypeError, ValueError):
            x = np.asarray(mdates.date2num(x), dtype='float')
        y = np.asarray(y, dtype='float')
        mask = ~(np.isnan(x) | np.isnan(y))
        x = x[mask]
        y = y[mask]
        self._points = np.column_stack((x, y))
        self.offsets = offsets
        y = y[np.abs(y-y.mean()) <= 3*y.std()]
        self.scale = x.ptp()
        self.scale = y.ptp() / self.scale if self.scale else 1
        self.tree = spatial.cKDTree(self.scaled(self._points))
        self.formatter = formatter
        self.tolerance = tolerance
        self.ax = ax
        self.fig = ax.figure
        self.ax.xaxis.set_label_position('top')
        self.dot = ax.scatter(
            [x.min()], [y.min()], s=130, color='green', alpha=0.7)
        self.annotation = self.setup_annotation()
        plt.connect('motion_notify_event', self)

    def scaled(self, points):
        points = np.asarray(points)
        return points * (self.scale, 1)

    def __call__(self, event):
        ax = self.ax
        # event.inaxes is always the current axis. If you use twinx, ax could be
        # a different axis.
        if event.inaxes == ax:
            x, y = event.xdata, event.ydata
        elif event.inaxes is None:
            return
        else:
            inv = ax.transData.inverted()
            x, y = inv.transform([(event.x, event.y)]).ravel()
        annotation = self.annotation
        x, y = self.snap(x, y)
        annotation.xy = x, y
        annotation.set_text(self.formatter(x, y))
        self.dot.set_offsets(np.column_stack((x, y)))
        bbox = self.annotation.get_window_extent()
        self.fig.canvas.blit(bbox)
        self.fig.canvas.draw_idle()

    def setup_annotation(self):
        """Draw and hide the annotation box."""
        annotation = self.ax.annotate(
            '', xy=(0, 0), ha = 'right',
            xytext = self.offsets, textcoords = 'offset points', va = 'bottom',
            bbox = dict(
                boxstyle='round,pad=0.5', fc='yellow', alpha=0.75),
            arrowprops = dict(
                arrowstyle='->', connectionstyle='arc3,rad=0'))
        return annotation

    def snap(self, x, y):
        """Return the value in self.tree closest to x, y."""
        dist, idx = self.tree.query(self.scaled((x, y)), k=1, p=1)
        try:
            return self._points[idx]
        except IndexError:
            # IndexError: index out of bounds
            return self._points[0]

fig, ax = plt.subplots()
x = np.linspace(0.1, 2*pi, 10)
y = cos(x)
markerline, stemlines, baseline = ax.stem(x, y, '-.')
plt.setp(markerline, 'markerfacecolor', 'b')
plt.setp(baseline, 'color','r', 'linewidth', 2)
cursor = FollowDotCursor(ax, x, y, tolerance=20)
plt.show()

enter image description here

Answered By: unutbu

There is a great plugin: https://github.com/anntzer/mplcursors
very simple to install. No neet to implement on your own.

Answered By: Yunjin
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.