绘图范围小于 10 倍时的正确自动对数轴(Matplotlib)

Proper automatic logarithmic axes when plot range less than a factor 10 (Matplotlib)

当对数图的数据范围不包括完整因子 10 时,当前 (v3.5) Matplotlib 代码不会自动生成出版质量的数字(见下文)。如何纠正这一限制?

这里是生成具有对数轴的图形的最小代码示例,范围不包括绘制的主刻度线的完整因子 10。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0.2, 8, 100)
plt.semilogx(x, np.sin(x))

下图显示了一个勾号标签,难以阅读且不符合出版质量

使用 this 相关问题的建议,我可以绘制所有小刻度线并切换到非科学记数法,如下所示

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

fig, ax = plt.subplots()
x = np.linspace(0.2, 8, 100)
plt.semilogx(x, np.sin(x))

formatter = ticker.FuncFormatter(lambda y, _: f'{y:.3g}')
ax.xaxis.set_major_formatter(formatter)
ax.xaxis.set_minor_formatter(formatter)

这会生成下图,其中的小刻度线标签过于密集,也不是出版质量

我知道我可以手动编辑刻度标签,但我正在寻找一种方法来为具有不同轴范围的许多图形自动生成合适的刻度标签。

我正在寻找的刻度线标签与 Mathematica 在相同情况下很好地生成的标签类似,如下所示。是否有可能在 Matplotlib 中实现类似的东西?

为了澄清我的问题,作为一个实际测试,是否可以设计一个函数,当 运行 使用我的上述程序之一时,自动 生成正确的刻度标签以下三个向量 x:

x = np.linspace(0.2, 8, 100)
x = np.linspace(3, 60, 100)
x = np.linspace(0.06, 0.5, 100)

注意:通过为每个不同的案例输入不同的值来编辑刻度标签不是一个有用的解决方案,因为我的函数必须 运行 在一般过程中用于未知的 x 范围.

要重现与 Mathematica 情节接近的内容,您需要关闭次要刻度的标签,即被弄乱的部分。

那么您想要自定义主要报价的标签。这可以使用 FixedLocator.

来完成
import matplotlib.pyplot as plt
from matplotlib import ticker

import numpy as np

fig, ax = plt.subplots()
x = np.linspace(0.2, 8, 100)
plt.semilogx(x, np.sin(x))

formatter = ticker.FuncFormatter(lambda y, _: f'{y:.3g}')
ax.xaxis.set_major_formatter(formatter)
#ax.xaxis.set_minor_formatter(formatter) #comment this line out
ax.xaxis.set_major_locator(ticker.FixedLocator([0.2, 0.5, 1, 2, 5])) 

输出:

** 第一次更新:

对于自动间距,我建议只使用 matplotlib 原始 major_formatter,它仅在 log10 为整数的点上给出 x-label,即:

import matplotlib.pyplot as plt
from matplotlib import ticker

import numpy as np

fig, ax = plt.subplots()
x = np.linspace(0.2, 8, 100)
plt.semilogx(x, np.sin(x))

formatter = ticker.FuncFormatter(lambda y, _: f'{y:.3g}')
ax.xaxis.set_major_formatter(formatter)

这似乎为绘图目的的每个可能范围 x 提供了理想的标签间距,无论图形大小、零的数量等如何。

对于 x = np.linspace(0.2, 8, 100) 的情况,它给出了

另一个极端,如果x = np.linspace(0.000002, 800, 100)

生成这样的绘图后,如果有更多 xlabel(如 0,20.52520,则始终可以使用 FixedLocator 50,..) 是 needed/modified.

** 第二次更新:

对于只跨越两个数量级的三种情况,可以用xticksfinder函数自动输出xticks(主要ticks/the xlabels位置),不用太担心数量零和数字大小(这将导致 xlabels 重叠)。作为一般经验法则,人们希望指定 {1, 2, 5} 次 10^n,其中 n 是数量级。那么下面的xticksfinder函数会有所帮助

def order(num):
    return math.floor(math.log(num, 10))

def xticksfinder(x):
    x = x.tolist()
    xtickers = []
    for i in range(order(x[0]), order(x[-1])+1):
         xtickers.append(1*math.pow(10, i))
         xtickers.append(2*math.pow(10, i))
         xtickers.append(5*math.pow(10, i))
    xticks = [x[0]] + [item for item in xtickers if x[0] < item <= x[-1]]
    xticks = [int(k) if k >=1 else k for k in xticks]
    return xticks

然后就可以在FixedLocator中调用了:

import matplotlib.pyplot as plt
from matplotlib import ticker

import numpy as np

fig, ax = plt.subplots()
x = np.linspace(0.2, 8, 100)
plt.semilogx(x, np.sin(x))

formatter = ticker.FuncFormatter(lambda y, _: f'{y:.3g}')
ax.xaxis.set_major_formatter(formatter)
#ax.xaxis.set_minor_formatter(formatter) #comment this line out
ax.xaxis.set_major_locator(ticker.FixedLocator(xticksfinder(x)))

对于x = np.linspace(0.2, 8, 100),输出是:

对于x = np.linspace(3, 60, 100),输出为:

对于x = np.linspace(0.06, 0.5, 100),输出为: