刻度标签文本和Matplotlib图中的频率 [英] Tick label text and frequency in matplotlib plot

查看:94
本文介绍了刻度标签文本和Matplotlib图中的频率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to plot some data stored in a Pandas Dataframe using matplotlib. I want to put specific labels on x axis ticks. So, I set them with:

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

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

That works well, but it sets a tick label for each data point, making the plot unreadable, so I tried:

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

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

which reduces the number of ticks to 3, but the labels are not corresponding to correct data points (if labels are a,b,c,d,e ..., x,y,z I get labels a,b,c instead of a,m,z or something like that). My next idea was to set tick labels positions:

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

This is almost perfect. It fixes the ticks and labels, but when I zoom the plot (using the matplotlib interactive window) new labels are obviously not appearing. And what I need are readable ticks that adjust themselves depending on plot zoom (this is what ax.locator_params(axis='x', nbins=3) does correctly without any custom labels).

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

In other words: I need to set specific label for each data point but show only few of them on the plot axis ticks without losing the correct assignment.

推荐答案

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

Using Locator we can define how many ticks shall be produced and where they should be placed. By sub-classing MaxNLocator (this is essentially the default Locator) we can reuse the functionality and simply filter out unwanted ticks (e.g. ticks outside the label range). My approach could definitely be improved at this point, as sparse or non-equidistant x-range data would break my simple filtering solution. Also float values might be a challenge, but I'm certain such a data range could always be mapped to a convenient integer range if the above conditions do not apply. But this is beyond the scope of this question.

使用 Formatter ,我们现在可以简单地查找对应的标签列表中的标签以产生正确的刻度标签.为了找到最接近的匹配值,我们可以有效地利用 bisect 模块(相关问题).对于静态图,我们可以依靠这样的假设,即我们的定位器已经产生了可以直接用于列表访问的索引(避免了不必要的二等分运算).但是,动态视图(请参见屏幕快照的左下角)使用Formatter格式化非刻度位置标签.因此,使用bisect是更通用,更稳定的方法.

With Formatter we can now simply lookup the corresponding labels in our label list to produce the correct tick label. For finding the closest matching value, we can efficiently utilize the bisect module (related question). For static plots we could rely on the assumption that our Locator already produces indices we can directly use for our list access (avoiding unnecessary bisect operation). However, the dynamic view (see the bottom left corner in the screenshots) uses the Formatter to format non-tick position labels. Thus, using bisect is the more general and stable approach.

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

这篇关于刻度标签文本和Matplotlib图中的频率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆