在matplotlib图中勾选标签文本和频率

时间:2017-04-05 14:41:30

标签: python pandas matplotlib plot

我想使用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)在没有任何自定义标签的情况下正确执行的操作。)

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

1 个答案:

答案 0 :(得分:1)

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

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

Unzoomed

Zoomed

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