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

前端 未结 1 1726
悲哀的现实
悲哀的现实 2020-12-16 07:14

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

相关标签:
1条回答
  • 2020-12-16 07:39

    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

    0 讨论(0)
提交回复
热议问题