我使用matplotlib的symlog标度来覆盖向正方向和负方向延伸的大范围参数。不幸的是,symlog规模不是很直观,也可能不常用。因此,我想通过在主要刻度之间放置小刻度来使用过的缩放更明显。在比例的日志部分,我想在[2,3,...,9] * 10 ^ e处设置蜱,其中e
是附近的主要蜱。此外,0到0.1之间的范围应覆盖均匀放置的次要刻度,相隔0.01。我尝试使用matplotlib.ticker API使用以下代码来达到这样的滴答:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator, AutoLocator
x = np.linspace(-5, 5, 100)
y = x
plt.plot(x, y)
plt.yscale('symlog', linthreshy=1e-1)
yaxis = plt.gca().yaxis
yaxis.set_minor_locator(LogLocator(subs=np.arange(2, 10)))
plt.show()
不幸的是,这不会产生我想要的东西:
请注意,0附近有许多次要刻度,这可能是由LogLocator
引起的。此外,负轴上没有小的嘀嗒声。
如果我使用AutoLocator
代替,则不会出现小的滴答声。 AutoMinorLocator
仅支持均匀缩放的轴。那么我的问题是如何实现所需的刻度位置?
答案 0 :(得分:13)
深入研究这个问题,我注意到很难找到一个通用的解决方案。幸运的是,我可以假设我的数据受到一些限制,因此定制的类足以解决问题:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import Locator
class MinorSymLogLocator(Locator):
"""
Dynamically find minor tick positions based on the positions of
major ticks for a symlog scaling.
"""
def __init__(self, linthresh):
"""
Ticks will be placed between the major ticks.
The placement is linear for x between -linthresh and linthresh,
otherwise its logarithmically
"""
self.linthresh = linthresh
def __call__(self):
'Return the locations of the ticks'
majorlocs = self.axis.get_majorticklocs()
# iterate through minor locs
minorlocs = []
# handle the lowest part
for i in xrange(1, len(majorlocs)):
majorstep = majorlocs[i] - majorlocs[i-1]
if abs(majorlocs[i-1] + majorstep/2) < self.linthresh:
ndivs = 10
else:
ndivs = 9
minorstep = majorstep / ndivs
locs = np.arange(majorlocs[i-1], majorlocs[i], minorstep)[1:]
minorlocs.extend(locs)
return self.raise_if_exceeds(np.array(minorlocs))
def tick_values(self, vmin, vmax):
raise NotImplementedError('Cannot get tick locations for a '
'%s type.' % type(self))
x = np.linspace(-5, 5, 100)
y = x
plt.plot(x, y)
plt.yscale('symlog', linthreshy=1e-1)
yaxis = plt.gca().yaxis
yaxis.set_minor_locator(MinorSymLogLocator(1e-1))
plt.show()
这会产生
请注意,此方法仅在主要刻度之间放置刻度。如果您缩放并平移图像,这将变得明显。此外,线性阈值必须明确地提供给类,因为我发现无法从轴本身轻松而稳健地读取它。
答案 1 :(得分:1)
OPs解决方案效果很好,但如果它们不是线性阈值的倍数,则不会在轴的边缘产生刻度线。我已经攻击了OP MinorSymLogLocator()
类以提供以下内容(通过在设置次要刻度locatoin时添加临时主要刻度位置来填充边缘):
class MinorSymLogLocator(Locator):
"""
Dynamically find minor tick positions based on the positions of
major ticks for a symlog scaling.
"""
def __init__(self, linthresh, nints=10):
"""
Ticks will be placed between the major ticks.
The placement is linear for x between -linthresh and linthresh,
otherwise its logarithmically. nints gives the number of
intervals that will be bounded by the minor ticks.
"""
self.linthresh = linthresh
self.nintervals = nints
def __call__(self):
# Return the locations of the ticks
majorlocs = self.axis.get_majorticklocs()
if len(majorlocs) == 1:
return self.raise_if_exceeds(np.array([]))
# add temporary major tick locs at either end of the current range
# to fill in minor tick gaps
dmlower = majorlocs[1] - majorlocs[0] # major tick difference at lower end
dmupper = majorlocs[-1] - majorlocs[-2] # major tick difference at upper end
# add temporary major tick location at the lower end
if majorlocs[0] != 0. and ((majorlocs[0] != self.linthresh and dmlower > self.linthresh) or (dmlower == self.linthresh and majorlocs[0] < 0)):
majorlocs = np.insert(majorlocs, 0, majorlocs[0]*10.)
else:
majorlocs = np.insert(majorlocs, 0, majorlocs[0]-self.linthresh)
# add temporary major tick location at the upper end
if majorlocs[-1] != 0. and ((np.abs(majorlocs[-1]) != self.linthresh and dmupper > self.linthresh) or (dmupper == self.linthresh and majorlocs[-1] > 0)):
majorlocs = np.append(majorlocs, majorlocs[-1]*10.)
else:
majorlocs = np.append(majorlocs, majorlocs[-1]+self.linthresh)
# iterate through minor locs
minorlocs = []
# handle the lowest part
for i in xrange(1, len(majorlocs)):
majorstep = majorlocs[i] - majorlocs[i-1]
if abs(majorlocs[i-1] + majorstep/2) < self.linthresh:
ndivs = self.nintervals
else:
ndivs = self.nintervals - 1.
minorstep = majorstep / ndivs
locs = np.arange(majorlocs[i-1], majorlocs[i], minorstep)[1:]
minorlocs.extend(locs)
return self.raise_if_exceeds(np.array(minorlocs))
def tick_values(self, vmin, vmax):
raise NotImplementedError('Cannot get tick locations for a '
'%s type.' % type(self))
答案 2 :(得分:0)
我找到了一种更简单的方法,可能适用:
我使用了ax.set_xticks方法和下面函数的输出
def gen_tick_positions(scale_start=100, scale_max=10000):
start, finish = np.floor(np.log10((scale_start, scale_max)))
finish += 1
majors = [10 ** x for x in np.arange(start, finish)]
minors = []
for idx, major in enumerate(majors[:-1]):
minor_list = np.arange(majors[idx], majors[idx+1], major)
minors.extend(minor_list[1:])
return minors, majors
对于OP的例子,你可以从ax.get_yticks()中推断出线性区域(即大约为0且不是因子10的值不同于0-1 / 10)
y_ticks = ax.get_yticks()
total_scale = list(y_ticks)
zero_point = total_scale.index(0.0)
post_zeroes = np.log10(total_scale[zero_point+1:])
first_log = []
for idx, value in enumerate(post_zeroes[:-1]):
if 1.005 > post_zeroes[idx+1] - value > 0.995:
first_log = total_scale[idx + zero_point]
这会为您提供一个开始值,以便放入上面的函数,scale_max
是您喜欢的任何内容,例如total_scale[-1]
。
您可以使用first_log的正负区域生成线性刻度,然后合并列表。
lin_ticks = list(np.linspace(first_log * -1, first_log, 21))
pos_log_ticks_minors, pos_log_ticks_majors = gen_tick_positions(first_log, scale_max)
neg_log_ticks_minors = [x * -1 for x in pos_log_ticks_minors]
neg_log_ticks_majors = [x * -1 for x in pos_log_ticks_majors]
final_scale_minors = neg_log_ticks_minors + lin_ticks + pos_log_ticks_minors
The merged list can then be passed into e.g.
ax.set_yticks(final_scale_minors, minor=True)
虽然我确实发现你不需要从绘图或轴上读取线性阈值,因为当你应用“symlog”时它被指定为参数。