2013-10-08 82 views
2

我使用matplotlib創建loglog圖。從下圖中可以看出,默認的滴答被選擇得很差(最好);正確的y軸甚至沒有任何東西(它的線性等價),並且兩個x軸都只有一個。Matplotlib loglog(雙軸)的壞蜱/標籤

loglog plot with bad default ticks

有沒有辦法讓蜱帶標籤的合理數量,沒有手工指定他們爲每一個情節?


編輯:確切的代碼太長,但這裏的問題的一個簡單的例子:

x = linspace(4, 18, 20) 
y = 1/(x ** 4) 
fig = figure() 
ax = fig.add_axes([.1, .1, .8, .8]) 
ax.loglog(x, y) 
ax.set_xlim([4, 18]) 
ax2 = ax.twiny() 
ax2.set_xlim([4/3., 18/3.]) 
ax2.set_xscale('log') 
show() 
+0

默認情況下,僅在幾十年內發出滴答聲(而且您還不到十年)您能向我們展示您用來生成此代碼的代碼嗎? – tacaswell

+0

[對數刻度設置刻度]可能的副本(http://stackoverflow.com/questions/14530113/set-ticks-with-logarithmic-scale) – tacaswell

+0

在更多的看,這看起來像你正在做你的所有在hartee/bohr軸上繪圖,使用'twinx'和'twiny'來繪製eV和angstrom軸,但從不繪製任何東西。您需要明確設置其限制以匹配其他軸上的限制(正確轉換)。 – tacaswell

回答

1

我一直在爭取的東西像什麼你看(只有一個主刻度軸範圍)。 matplotlib刻度盤格式化程序都不滿足我,所以我使用matplotlib.ticker.FuncFormatter來實現我想要的。我還沒有用雙軸進行測試,但我的感覺是,它應該無論如何工作。

import matplotlib.pyplot as plt 
from matplotlib import ticker 
import numpy as np 

#@Mark: thanks for the suggestion :D 
mi, ma, conv = 4, 8, 1./3. 
x = np.linspace(mi, ma, 20) 
y = 1/(x ** 4) 

fig, ax = plt.subplots() 

ax.plot(x, y) # plot the lines 
ax.set_xscale('log') #convert to log 
ax.set_yscale('log') 

ax.set_xlim([0.2, 1.8]) #large enough, but should show only 1 tick 

def ticks_format(value, index): 
    """ 
    This function decompose value in base*10^{exp} and return a latex string. 
    If 0<=value<99: return the value as it is. 
    if 0.1<value<0: returns as it is rounded to the first decimal 
    otherwise returns $base*10^{exp}$ 
    I've designed the function to be use with values for which the decomposition 
    returns integers 
    """ 
    exp = np.floor(np.log10(value)) 
    base = value/10**exp 
    if exp == 0 or exp == 1: 
     return '${0:d}$'.format(int(value)) 
    if exp == -1: 
     return '${0:.1f}$'.format(value) 
    else: 
     return '${0:d}\\times10^{{{1:d}}}$'.format(int(base), int(exp)) 

# here specify which minor ticks per decate you want 
# likely all of them give you a too crowed axis 
subs = [1., 3., 6.] 
# set the minor locators 
ax.xaxis.set_minor_locator(ticker.LogLocator(subs=subs)) 
ax.yaxis.set_minor_locator(ticker.LogLocator(subs=subs)) 
# remove the tick labels for the major ticks: 
# if not done they will be printed with the custom ones (you don't want it) 
# plus you want to remove them to avoid font missmatch: the above function 
# returns latex string, and I don't know how matplotlib does exponents in labels 
ax.xaxis.set_major_formatter(ticker.NullFormatter()) 
ax.yaxis.set_major_formatter(ticker.NullFormatter()) 
# set the desired minor tick labels using the above function 
ax.xaxis.set_minor_formatter(ticker.FuncFormatter(ticks_format)) 
ax.yaxis.set_minor_formatter(ticker.FuncFormatter(ticks_format)) 

的身影,我得到的是以下enter image description here

當然,你可以爲X和Y軸設定不同的次定位器,你可以換一切從ticks_format到結束到接受一個函數軸實例axsubssubsxsubsy作爲輸入參數。

我希望這可以幫助你

+0

它對於接近0的約一個數量級變化起作用,我認爲這是一個常見的問題區域。如果你需要在較小的域上使用日誌量表,次要的蜱沒有多大幫助,但這可能是非常特殊的...此外格式化很酷! – Mark

0

最後,這是我能拿出來與這裏其他的答案的幫助下,最好elsewere是這樣的:

enter image description here

在左邊,x和y僅在一個數量級的一部分上變化,標籤工作得很好。在左邊,x在1和2個數量級之間變化。它工作正常,但方法達到了極限。 y值變化很多數量級,標準標籤會自動使用。

from matplotlib import ticker 
from numpy import linspace, logspace, log10, floor 
from warnings import warn 

def round_to_n(x, n): 
    ''' http://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python ''' 
    return round(x, -int(floor(log10(abs(x)))) + (n - 1)) 

def ticks_log_format(value, index): 
    ''' http://stackoverflow.com/questions/19239297/matplotlib-bad-ticks-labels-for-loglog-twin-axis ''' 
    pwr = floor(log10(value)) 
    base = value/(10 ** pwr) 
    if pwr == 0 or pwr == 1: 
     return '${0:d}$'.format(int(value)) 
    if -3 <= pwr < 0: 
     return '${0:.3g}$'.format(value) 
    if 0 < pwr <= 3: 
     return '${0:d}$'.format(int(value)) 
    else: 
     return '${0:d}\\times10^{{{1:d}}}$'.format(int(base), int(pwr)) 

def calc_ticks(domain, tick_count, equidistant): 
    if equidistant: 
     ticks = logspace(log10(domain[0]), log10(domain[1]), num = tick_count, base = 10) 
    else: 
     ticks = linspace(domain[0], domain[1], num = tick_count) 
    for n in range(1, 6): 
     if len(set(round_to_n(tick, n) for tick in ticks)) == tick_count: 
      break  
    return list(round_to_n(tick, n) for tick in ticks) 

''' small domain log ticks ''' 
def sdlt_x(ax, domain, tick_count = 4, equidistant = True): 
    ''' http://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python ''' 
    if min(domain) <= 0: 
     warn('domain %g-%g contains values lower than 0' % (domain[0], domain[1])) 
     domain = [max(value, 0.) for value in domain] 
    ax.set_xscale('log') 
    ax.set_xlim(domain) 
    ax.xaxis.set_major_formatter(ticker.FuncFormatter(ticks_log_format)) 
    if log10(max(domain)/min(domain)) > 1.7: 
     return 
    ticks = calc_ticks(domain, tick_count = tick_count, equidistant = equidistant) 
    ax.set_xticks(ticks) 

''' any way to prevent this code duplication? ''' 
def sdlt_y(ax, domain, tick_count = 5, equidistant = True): 
    ''' http://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python ''' 
    if min(domain) <= 0: 
     warn('domain %g-%g contains values lower than 0' % (domain[0], domain[1])) 
     domain = [max(value, 1e-8) for value in domain] 
    ax.set_yscale('log') 
    ax.set_ylim(domain) 
    ax.yaxis.set_major_formatter(ticker.FuncFormatter(ticks_log_format)) 
    if log10(max(domain)/min(domain)) > 1.7: 
     return 
    ticks = calc_ticks(domain, tick_count = tick_count, equidistant = equidistant) 
    ax.set_yticks(ticks) 

''' demo ''' 
fig, (ax1, ax2,) = plt.subplots(1, 2) 
for mi, ma, ax in ((100, 130, ax1,), (10, 400, ax2,),): 
    x = np.linspace(mi, ma, 50) 
    y = 1/((x + random(50) * 0.1 * (ma - mi)) ** 4) 
    ax.scatter(x, y) 
    sdlt_x(ax, (mi, ma,)) 
    sdlt_y(ax, (min(y), max(y),)) 
show() 

編輯:一個選項更新,以使標籤等距(這樣的值是對數,但可見的位置是等距)。