带有稀疏刻度标签且没有科学记数法的 LogFormatter
LogFormatter with sparse tick labels and no scientific notation
我有一个双对数图,想使用 LogFormatter
的 minor_thresholds
选项来使用稀疏的次要刻度标签,同时避免使用科学记数法。我最好的尝试,使用与我的真实数据跨越相同范围的虚拟数据:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = np.array([1.2**e for e in range(-18, 13)])
y = 10 * x
plt.rcParams['axes.formatter.min_exponent'] = 3
fig = plt.figure()
ax = fig.add_subplot()
ax.scatter(x, y)
ax.set_xscale('log')
ax.set_yscale('log')
fmt = ticker.LogFormatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
ax.xaxis.set_minor_formatter(fmt)
ax.yaxis.set_minor_formatter(fmt)
这导致:
优点:
- 只有合理的次要刻度子集被标记
- 主要刻度的标签格式正确(参见“0.1”)
坏点:
- 小刻度的标签仍然使用科学记数法 - 它似乎忽略了
axes.formatter.min_exponent
选项
我该如何解决?
(我还想避免为不需要的数字添加小数点的固定表示法。)
编辑:
根据@tmdavison 的回答,我尝试制作一个考虑到 axes.formatter.min_exponent
的 _num_to_string
版本。最简单的形式是
def _num_to_string(self, x, vmin, vmax):
min_exp = mpl.rcParams['axes.formatter.min_exponent']
fx = math.log(x) / math.log(self._base)
if abs(fx) < min_exp:
return '%g' % x
else:
return self._pprint_val(x, vmax - vmin)
这是基于 _pprint_val
会处理其他情况的假设,这是不正确的 - _print_val
对 x
的唯一考虑是它使用 %d
如果是1e4以下的整数。否则,它的格式基于范围 (vmax - vmin
) .. 这对于基于日志的轴来说似乎主要是错误的。
此外,我观察到上述 _num_to_string
的一个非常奇怪的行为:该函数被每个轴调用 168 次,即使它只打印 12 个数字。例如,对于 x 轴 x
有 28 个值,从 0.002 到 6000(!),这个序列被调用了 6 次!这肯定是有原因的,但它看起来确实很奇怪......
这是因为在LogFormatter
中,有一些固定号码。我们可以看这里的代码:https://matplotlib.org/stable/_modules/matplotlib/ticker.html#LogFormatter
class中的相关函数是_num_to_string
函数,我将其粘贴在这里:
def _num_to_string(self, x, vmin, vmax):
if x > 10000:
s = '%1.0e' % x
elif x < 1:
s = '%1.0e' % x
else:
s = self._pprint_val(x, vmax - vmin)
return s
如您所见,对于任何大于 10000 或小于 1 的数字,格式都硬连接到 1.0e
。
这里的一个选择是子class LogFormatter
class 并编写我们自己的 _num_to_string
函数,我在下面的示例中已经完成了。对于 0.001 和 1 之间的数字,我使用 %g
格式,它只使用所需的小数位数。显然,您可以调整我在这里使用的限制或格式以满足您的需要。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = np.array([1.2**e for e in range(-18, 13)])
y = 10 * x
plt.rcParams['axes.formatter.min_exponent'] = 3
fig = plt.figure()
ax = fig.add_subplot()
ax.scatter(x, y)
ax.set_xscale('log')
ax.set_yscale('log')
class myformatter(ticker.LogFormatter):
def _num_to_string(self, x, vmin, vmax):
if x > 10000:
s = '%1.0e' % x
elif x < 1 and x >= 0.001:
s = '%g' % x
elif x < 0.001:
s = '%1.0e' % x
else:
s = self._pprint_val(x, vmax - vmin)
return s
# Create separate instances of formatter for each axis
xfmt = myformatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
yfmt = myformatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
ax.xaxis.set_minor_formatter(xfmt)
ax.yaxis.set_minor_formatter(yfmt)
plt.show()
我有一个双对数图,想使用 LogFormatter
的 minor_thresholds
选项来使用稀疏的次要刻度标签,同时避免使用科学记数法。我最好的尝试,使用与我的真实数据跨越相同范围的虚拟数据:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = np.array([1.2**e for e in range(-18, 13)])
y = 10 * x
plt.rcParams['axes.formatter.min_exponent'] = 3
fig = plt.figure()
ax = fig.add_subplot()
ax.scatter(x, y)
ax.set_xscale('log')
ax.set_yscale('log')
fmt = ticker.LogFormatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
ax.xaxis.set_minor_formatter(fmt)
ax.yaxis.set_minor_formatter(fmt)
这导致:
优点:
- 只有合理的次要刻度子集被标记
- 主要刻度的标签格式正确(参见“0.1”)
坏点:
- 小刻度的标签仍然使用科学记数法 - 它似乎忽略了
axes.formatter.min_exponent
选项
我该如何解决? (我还想避免为不需要的数字添加小数点的固定表示法。)
编辑:
根据@tmdavison 的回答,我尝试制作一个考虑到 axes.formatter.min_exponent
的 _num_to_string
版本。最简单的形式是
def _num_to_string(self, x, vmin, vmax):
min_exp = mpl.rcParams['axes.formatter.min_exponent']
fx = math.log(x) / math.log(self._base)
if abs(fx) < min_exp:
return '%g' % x
else:
return self._pprint_val(x, vmax - vmin)
这是基于 _pprint_val
会处理其他情况的假设,这是不正确的 - _print_val
对 x
的唯一考虑是它使用 %d
如果是1e4以下的整数。否则,它的格式基于范围 (vmax - vmin
) .. 这对于基于日志的轴来说似乎主要是错误的。
此外,我观察到上述 _num_to_string
的一个非常奇怪的行为:该函数被每个轴调用 168 次,即使它只打印 12 个数字。例如,对于 x 轴 x
有 28 个值,从 0.002 到 6000(!),这个序列被调用了 6 次!这肯定是有原因的,但它看起来确实很奇怪......
这是因为在LogFormatter
中,有一些固定号码。我们可以看这里的代码:https://matplotlib.org/stable/_modules/matplotlib/ticker.html#LogFormatter
class中的相关函数是_num_to_string
函数,我将其粘贴在这里:
def _num_to_string(self, x, vmin, vmax):
if x > 10000:
s = '%1.0e' % x
elif x < 1:
s = '%1.0e' % x
else:
s = self._pprint_val(x, vmax - vmin)
return s
如您所见,对于任何大于 10000 或小于 1 的数字,格式都硬连接到 1.0e
。
这里的一个选择是子class LogFormatter
class 并编写我们自己的 _num_to_string
函数,我在下面的示例中已经完成了。对于 0.001 和 1 之间的数字,我使用 %g
格式,它只使用所需的小数位数。显然,您可以调整我在这里使用的限制或格式以满足您的需要。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = np.array([1.2**e for e in range(-18, 13)])
y = 10 * x
plt.rcParams['axes.formatter.min_exponent'] = 3
fig = plt.figure()
ax = fig.add_subplot()
ax.scatter(x, y)
ax.set_xscale('log')
ax.set_yscale('log')
class myformatter(ticker.LogFormatter):
def _num_to_string(self, x, vmin, vmax):
if x > 10000:
s = '%1.0e' % x
elif x < 1 and x >= 0.001:
s = '%g' % x
elif x < 0.001:
s = '%1.0e' % x
else:
s = self._pprint_val(x, vmax - vmin)
return s
# Create separate instances of formatter for each axis
xfmt = myformatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
yfmt = myformatter(labelOnlyBase=False, minor_thresholds=(3, 0.5))
ax.xaxis.set_minor_formatter(xfmt)
ax.yaxis.set_minor_formatter(yfmt)
plt.show()