【问题标题】:Matplotlib cursor value with two axes具有两个轴的 Matplotlib 光标值
【发布时间】:2014-02-05 17:16:04
【问题描述】:

我使用twinx() 在绘图上获得两个 y 轴。但是,我希望导航栏报告第一个轴的 y 坐标。默认情况下,它似乎将位置报告给第二个轴。如何将其更改为报告第一个轴,或者更好的是,同时报告两者?

【问题讨论】:

标签: python matplotlib


【解决方案1】:

在 tcaswell 的回答(herehere)的帮助下,您可以修改跟踪器以显示相对于两个轴的坐标,如下所示:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

def make_format(current, other):
    # current and other are axes
    def format_coord(x, y):
        # x, y are data coordinates
        # convert to display coords
        display_coord = current.transData.transform((x,y))
        inv = other.transData.inverted()
        # convert back to data coords with respect to ax
        ax_coord = inv.transform(display_coord)
        coords = [ax_coord, (x, y)]
        return ('Left: {:<40}    Right: {:<}'
                .format(*['({:.3f}, {:.3f})'.format(x, y) for x,y in coords]))
    return format_coord

np.random.seed(6)
numdata = 100
t = np.linspace(0.05, 0.11, numdata)
y1 = np.cumsum(np.random.random(numdata) - 0.5) * 40000
y2 = np.cumsum(np.random.random(numdata) - 0.5) * 0.002

fig = plt.figure()

ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()

ax2.format_coord = make_format(ax2, ax1)

ax1.plot(t, y1, 'r-', label='y1')
ax2.plot(t, y2, 'g-', label='y2')

plt.show()

或者,如果你有 scipy,你可以使用FollowDotCursor,它会标注离光标最近的数据点。通过这种方式,用户的眼睛不必从图形上移开很远就能找到数据坐标。此外,它可以应用于两个以上的艺术家(只需为每条线添加一个 FollowDotCursor、散点图、条形图等)。 它也更准确,因为注释窗口显示最近数据点的值,而不仅仅是数据坐标中的光标坐标。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import scipy.spatial as spatial

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/20637433/190597 (unutbu)
    """
    def __init__(self, ax, x, y, 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.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((x, y))
        event.canvas.draw()

    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]


np.random.seed(6)
numdata = 100
t = np.linspace(0.05, 0.11, numdata)
y1 = np.cumsum(np.random.random(numdata) - 0.5) * 40000
y2 = np.cumsum(np.random.random(numdata) - 0.5) * 0.002

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()

ax1.plot(t, y1, 'r-', label='y1')
ax2.plot(t, y2, 'g-', label='y2')

cursor1 = FollowDotCursor(ax1, t, y1)
cursor2 = FollowDotCursor(ax2, t, y2)
plt.show()

【讨论】:

  • 谢谢,这很好用(除了格式问题,但我可以解决)。
  • @Doug:关于格式问题:我希望我知道如何右对齐右边的坐标。唉,还有一件事我不知道该怎么做……
  • 我指的是“左”和“右”之间的换行符。在 mpl 1.2.1 中,它看起来不像您的屏幕截图,它在一行中。扩大窗口不会改变任何事情。
  • 一个很棒的解决方案。我将它用于股票图表以显示日期、价格和数量。谢谢!
【解决方案2】:

导航栏显示位于顶部的轴的坐标。因此,您所要做的就是使右轴的zorder 低于左轴的zorder

ax2.set_zorder(-100)

这也会改变图形的外观。也就是说,绘制在右侧轴上的所有内容都将落后于绘制在左侧轴上的所有内容。在许多情况下,这几乎没有区别。

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-15
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 2013-07-19
    相关资源
    最近更新 更多