2017-04-05 56 views
2

我想繪製一些使用matplotlib存儲在熊貓數據框中的數據。我想把特定的標籤放在x軸刻度上。所以,我把他們:在matplotlib圖中勾選標籤文本和頻率

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

行之有效,但它爲每個數據點蜱標籤,使得情節不可讀,所以我嘗試:

ax.locator_params(axis='x', nbins=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

我有問題,理解你想要什麼。爲什麼當你實際上只想要數據點位置的標籤時,你想要出現新的標籤?這聽起來與我矛盾。 – ImportanceOfBeingErnest

+0

@ michal-2am我的回答是否回答您的問題? – mhoff

回答

1

使用Locator我們可以定義應該生成多少滴答以及應該放置多少滴答。通過對MaxNLocator進行分類(這實質上是默認定位器),我們可以重新使用該功能並簡單地過濾不需要的刻度(例如,標籤範圍外的刻度)。由於稀疏或非等距的x範圍數據會破壞我簡單的過濾解決方案,因此我的方法在這一點上肯定可以改進。浮點值也可能是一個挑戰,但我確信如果上述條件不適用,這樣的數據範圍總是可以映射到一個方便的整數範圍。但這超出了這個問題的範圍。

使用Formatter我們現在可以簡單地在我們的標籤列表中查找相應的標籤以產生正確的刻度標籤。爲了找到最接近的匹配值,我們可以高效地利用bisect模塊(related question)。對於靜態圖,我們可以依賴於我們的定位器已經生成了我們可以直接用於我們的列表訪問(避免不必要的平分操作)的索引的假設。但是,動態視圖(請參閱屏幕截圖的左下角)使用格式化程序來格式化非刻度位置標籤。因此,使用平分是更一般和穩定的方法。

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