我想使用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)
在没有任何自定义标签的情况下正确执行的操作。)
换句话说:我需要为每个数据点设置特定标签,但在绘图轴上只显示少数标签,而不会丢失正确的分配。
答案 0 :(得分:1)
使用Locator
我们可以定义应生成多少刻度以及它们应放在何处。通过对MaxNLocator
进行子分类(这实际上是默认定位器),我们可以重复使用该功能,并简单地过滤掉不需要的滴答(例如标签范围之外的滴答)。我的方法在这一点上肯定可以改进,因为稀疏或非等距的x范围数据会破坏我的简单过滤解决方案。浮点值也可能是一个挑战,但我确定如果上述条件不适用,这样的数据范围总是可以映射到方便的整数范围。但这超出了这个问题的范围。
使用Formatter
,我们现在只需查找标签列表中的相应标签即可生成正确的刻度标签。为了找到最接近的匹配值,我们可以有效地利用bisect
模块(related question)。对于静态图,我们可以依赖于我们的Locator已经生成我们可以直接用于列表访问的索引的假设(避免不必要的bisect操作)。但是,动态视图(请参见屏幕截图中的左下角)使用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()