Matplotlib 半对数图:当范围很大时,次要刻度线消失了

Posted

技术标签:

【中文标题】Matplotlib 半对数图:当范围很大时,次要刻度线消失了【英文标题】:Matplotlib semi-log plot: minor tick marks are gone when range is large 【发布时间】:2017-10-20 01:51:34 【问题描述】:

在制作半对数图(y 为对数)时,y 轴上的次要刻度线(十年中的 8 个)会自动出现,但似乎当轴范围超过 10**10 时,它们会消失。我尝试了很多方法来强迫他们回来,但都无济于事。可能是为了避免人满为患,他们会离开大范围,但应该有选择吗?

【问题讨论】:

【参考方案1】:

matplotlib 解决方案 >= 2.0.2

让我们考虑下面的例子

这是由这段代码产生的:

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

y = np.arange(12)
x = 10.0**y

fig, ax=plt.subplots()
ax.plot(x,y)
ax.set_xscale("log")
plt.show()

次要刻度标签确实消失了,显示它们的常用方法(如plt.tick_params(axis='x', which='minor'))失败了。

然后第一步是在轴上显示 10 的所有幂,

locmaj = matplotlib.ticker.LogLocator(base=10,numticks=12) 
ax.xaxis.set_major_locator(locmaj)

诀窍是将numticks 设置为等于或大于刻度数的数字(在这种情况下为 12 或更高)。

然后,我们可以添加小标签为

locmin = matplotlib.ticker.LogLocator(base=10.0,subs=(0.2,0.4,0.6,0.8),numticks=12)
ax.xaxis.set_minor_locator(locmin)
ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())

请注意,我将其限制为每十年包含 4 个次要刻度(使用 8 个同样可能,但在此示例中会使轴过度拥挤)。另请注意,numticks 再次(非常不直观)是 12 或更大。

最后,我们需要为次要刻度使用NullFormatter(),以免出现任何刻度标签。

matplotlib 2.0.0 解决方案

以下在 matplotlib 2.0.0 或更低版本中有效,但在 matplotlib 2.0.2 中无效。

让我们考虑下面的例子

这是由这段代码产生的:

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

y = np.arange(12)
x = 10.0**y

fig, ax=plt.subplots()
ax.plot(x,y)
ax.set_xscale("log")
plt.show()

次要刻度标签确实消失了,显示它们的常用方法(如plt.tick_params(axis='x', which='minor'))失败了。

然后第一步是在轴上显示 10 的所有幂,

locmaj = matplotlib.ticker.LogLocator(base=10.0, subs=(0.1,1.0, ))
ax.xaxis.set_major_locator(locmaj)

然后,我们可以添加小标签为

locmin = matplotlib.ticker.LogLocator(base=10.0, subs=(0.1,0.2,0.4,0.6,0.8,1,2,4,6,8,10 )) 
ax.xaxis.set_minor_locator(locmin)
ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())

请注意,我将其限制为每十年包含 4 个次要刻度(使用 8 个同样可能,但在此示例中会使轴过度拥挤)。另请注意 - 这可能是这里的关键 - subs 参数给出了放置刻度的基数的整数幂的倍数(请参阅documentation),给出了一个跨越两个十年的列表,而不是一个。

最后,我们需要为次要刻度使用NullFormatter(),以免出现任何刻度标签。

【讨论】:

在这两种情况下subs 包含的条目太多,主要只需(1.0 ,),次要应为subs=np.arange(2, 10)*.1 @tacaswell 这里的解决方案需要在二十年内使用subs。这基本上是让它发挥作用的整个想法。现在,似乎在 2.0.0 和 2.0.2 版本之间发生了一些变化,因此这里介绍的这种解决方法不再起作用,而是在十年内使用 subs 的更直观的方法只能再次起作用。 @BrandonDube 我用 matplotlib 2.1.2 测试了“matplotlib 2.0.2 或更高版本的解决方案”,效果很好。 @ImportanceOfBeingErnest 你是从 pip 还是 Conda 获得 MPL 的?我发现来自 Conda 的 MPL 被 Qt 后端破坏了,所以它可能是连续的变化。改成 pip 的 MPL 后我没有再尝试过。 @BrandonDube 这不应该取决于你从哪里得到 mpl。如果您对 mpl 和 qt 有疑问,则与此处的问题无关。【参考方案2】:

带有空标签的主要刻度将生成刻度但没有标签。

ax.set_yticks([1.E-6,1.E-5,1.E-4,1.E-3,1.E-2,1.E-1,1.E0,1.E1,1.E2,1.E3,1.E4,1.E5,])

ax.set_yticklabels(['$10^-6$','','','$10^-3$','','','$1$','','','$10^3$','',''])

【讨论】:

【参考方案3】:

将importanceofbeingernest 中的excellent answer 用于matplotlib >= 2.0.2 包装成一个函数:

import matplotlib.pyplot as plt
from typing import Optional


def restore_minor_ticks_log_plot(
    ax: Optional[plt.Axes] = None, n_subticks=9
) -> None:
    """For axes with a logrithmic scale where the span (max-min) exceeds
    10 orders of magnitude, matplotlib will not set logarithmic minor ticks.
    If you don't like this, call this function to restore minor ticks.

    Args:
        ax:
        n_subticks: Number of Should be either 4 or 9.

    Returns:
        None
    """
    if ax is None:
        ax = plt.gca()
    # Method from SO user importanceofbeingernest at
    # https://***.com/a/44079725/5972175
    locmaj = mpl.ticker.LogLocator(base=10, numticks=1000)
    ax.xaxis.set_major_locator(locmaj)
    locmin = mpl.ticker.LogLocator(
        base=10.0, subs=np.linspace(0, 1.0, n_subticks + 2)[1:-1], numticks=1000
    )
    ax.xaxis.set_minor_locator(locmin)
    ax.xaxis.set_minor_formatter(mpl.ticker.NullFormatter())

这个函数可以简单地调用

plt.plot(x,y)
plt.xscale("log")
restore_minor_ticks_log_plot()

或更明确地

_, ax = plt.subplots()
ax.plot(x, y)
ax.set_xscale("log")
restore_minor_ticks_log_plot(ax)

【讨论】:

以上是关于Matplotlib 半对数图:当范围很大时,次要刻度线消失了的主要内容,如果未能解决你的问题,请参考以下文章

使用 seaborn 向 matplotlib 图添加次要网格线

Matplotlib 对数刻度刻度标签数字格式

在 R 中的 x 轴上显示次要对数刻度

使用 Qwt 绘制半对数图

matplotlib:通过用于为散点图着色的对数颜色条值对 2D 线进行着色

Matplotlib可视化散点图配置X轴为对数坐标并使用线条(line)连接散点图中的数据点(Simple Line Plot with Data points in Matplotlib)