【问题标题】:Tick label text and frequency in matplotlib plot在 matplotlib 图中勾选标签文本和频率
【发布时间】:2017-08-31 06:13:46
【问题描述】:

我想使用 matplotlib 绘制存储在 Pandas Dataframe 中的一些数据。我想在 x 轴刻度上放置特定标签。因此,我将它们设置为:

ax.xaxis.set_ticklabels(data_frame['labels'])

效果很好,但是它为每个数据点设置了一个刻度标签,使绘图不可读,所以我尝试了:

ax.locator_params(axis='x', nbins=3)

这将刻度数减少到 3,但标签不对应于正确的数据点(如果标签是 a,b,c,d,e ..., x,y,z 我得到标签 a,b ,c 而不是 a,m,z 或类似的东西)。我的下一个想法是设置刻度标签位置:

ax.xaxis.set_ticks(data_frame.index.values)

但它不起作用。

有效的是:

ax.xaxis.set_ticklabels(data_frame['labels'][::step])
ax.xaxis.set_ticks(data_frame.index.values[::step])

不设置任何locator_params

这几乎是完美的。它修复了刻度和标签,但是当我缩放绘图(使用 matplotlib 交互式窗口)时,新标签显然没有出现。我需要的是可读的刻度,它们会根据绘图缩放进行自我调整(这是ax.locator_params(axis='x', nbins=3) 在没有任何自定义标签的情况下正确执行的操作)。

换句话说:我需要为每个数据点设置特定的标签,但在绘图轴刻度上只显示其中的几个,而不会丢失正确的分配。

【问题讨论】:

  • 我无法理解您想要什么。当您实际上只想要数据点位置的标签时,为什么要出现新标签?这对我来说听起来很矛盾。
  • @michal-2am 我的回答回答了你的问题吗?

标签: python pandas matplotlib plot


【解决方案1】:

使用Locator,我们可以定义应该产生多少个刻度以及它们应该放置在哪里。通过子类化MaxNLocator(这本质上是默认的定位器),我们可以重用该功能并简单地过滤掉不需要的刻度(例如标签范围之外的刻度)。在这一点上,我的方法肯定可以改进,因为稀疏或非等距的 x 范围数据会破坏我的简单过滤解决方案。浮点值也可能是一个挑战,但我敢肯定,如果上述条件不适用,这样的数据范围总是可以映射到方便的整数范围。但这超出了这个问题的范围。

使用Formatter,我们现在可以简单地在标签列表中查找相应的标签以生成正确的刻度标签。为了找到最接近的匹配值,我们可以有效地利用bisect 模块(related question)。对于静态图,我们可以假设我们的定位器已经生成了可以直接用于列表访问的索引(避免不必要的平分操作)。但是,动态视图(请参见屏幕截图中的左下角)使用 Formatter 来格式化非刻度位置标签。因此,使用 bisect 是更通用和更稳定的方法。

import matplotlib.pyplot as plt
import numpy as np
import bisect
from matplotlib.ticker import Formatter
from matplotlib.ticker import MaxNLocator

x = np.arange(0, 100, 1)

y = np.sin(x)

# custom labels, could by anything
l = ["!{}!".format(v) for v in x]

plt.plot(x, y)
ax = plt.gca()

class LookupLocator(MaxNLocator):
    def __init__(self, valid_ticks, nbins='auto', min_n_ticks=0, integer=True):
        MaxNLocator.__init__(self, integer=integer, nbins=nbins, min_n_ticks=min_n_ticks)
        self._valid_ticks = valid_ticks
        self._integer = integer

    def is_tick_valid(self, t):
        if self._integer:
            return t.is_integer() and int(t) in self._valid_ticks
        return t in self._valid_ticks

    def tick_values(self, vmin, vmax):
        return filter(self.is_tick_valid, MaxNLocator.tick_values(self, vmin, vmax))


class LookupFormatter(Formatter):
    def __init__(self, tick_values, tick_labels):
        Formatter.__init__(self)
        self._tick_values = tick_values
        self._tick_labels = tick_labels

    def _find_closest(self, x):
        # https://stackoverflow.com/questions/12141150/from-list-of-integers-get-number-closest-to-a-given-value
        i = bisect.bisect_left(self._tick_values, x)
        if i == 0:
            return i
        if i == len(self._tick_values):
            return i - 1
        l, r = self._tick_values[i - 1], self._tick_values[i]
        if l - x < x - r:
            return i
        return i - 1

    def __call__(self, x, pos=None):
        return self._tick_labels[self._find_closest(x)]

ax.xaxis.set_major_locator(LookupLocator(x))
ax.xaxis.set_major_formatter(LookupFormatter(x, l))

plt.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 2013-06-12
    • 1970-01-01
    • 2018-01-05
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多