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

Posted

技术标签:

【中文标题】Matplotlib 对数刻度刻度标签数字格式【英文标题】:Matplotlib log scale tick label number formatting 【发布时间】:2014-03-22 03:06:36 【问题描述】:

matplotlib 为轴指定对数刻度时,标记该轴的默认方法是使用 10 次方的数字,例如。 10^6。有没有一种简单的方法可以将所有这些标签更改为它们的完整数字表示?例如。 1、10、100等

请注意,我不知道幂的范围是多少,并且希望支持任意范围(包括负数)。

【问题讨论】:

【参考方案1】:

当然,只需更改格式化程序即可。

例如,如果我们有这样的情节:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 100000])
ax.loglog()

plt.show()

您可以手动设置刻度标签,但是当您缩放/平移/等时,刻度位置和标签将被固定。因此,最好更改格式化程序。默认情况下,对数刻度使用LogFormatter,它将以科学计数法格式化值。要将格式化程序更改为线性轴的默认值 (ScalarFormatter),请使用例如

from matplotlib.ticker import ScalarFormatter
for axis in [ax.xaxis, ax.yaxis]:
    axis.set_major_formatter(ScalarFormatter())

【讨论】:

如果我想将数字更改为 1、5、10、20 怎么办? @Joe Kington:我想在两者之间添加刻度,例如50,200, etc..,我该怎么做?我试过了,set_xticks[50.0,200.0] 但这似乎不起作用! 但是使用 ax.axis([1, 100, 1, 100]),ScalarFormatter 给出 1.0, 10.0, ... 这不是我想要的。我希望它给出整数... 我还必须使用set_powerlimits() 来避免使用科学记数法。 @DonKirkby:您可以使用.set_scientific(False) 而不是set_powerlimits() 来禁用科学记数法。【参考方案2】:

我发现如果您的所有刻度值都大于或等于 1,则使用 ScalarFormatter 非常好。但是,如果您在数字 <1 上有刻度,ScalarFormatter 会将刻度标签打印为0.

我们可以使用 matplotlib ticker 模块中的 FuncFormatter 来解决这个问题。最简单的方法是使用lambda 函数和g 格式说明符(感谢cmets 中的@lenz)。

import matplotlib.ticker as ticker

ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y, _: ':g'.format(y)))

请注意,在我的原始答案中,我没有使用 g 格式,而是想出了带有 FuncFormatterlambda 函数将数字 >= 1 设置为整数值,将数字 <1 设置为它们的十进制值,以及所需的最小小数位数(即0.1, 0.01, 0.001 等)。它假定您仅在 base10 值上设置刻度。

import matplotlib.ticker as ticker
import numpy as np

ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y,pos: (':.:1df'.format(int(np.maximum(-np.log10(y),0)))).format(y)))

为清楚起见,这里的 lambda 函数以更详细但也更易于理解的方式编写:

def myLogFormat(y,pos):
    # Find the number of decimal places required
    decimalplaces = int(np.maximum(-np.log10(y),0))     # =0 for numbers >=1
    # Insert that number into a format string
    formatstring = ':.:1df'.format(decimalplaces)
    # Return the formatted tick label
    return formatstring.format(y)

ax.yaxis.set_major_formatter(ticker.FuncFormatter(myLogFormat))

【讨论】:

这个跟进正是我所需要的,谢谢!一个建议:g 格式说明符不也能解决问题吗?使用 FuncFormatter(lambda y, _: ':g'.format(y)) 我得到了相同的输出。 是的,我认为你是对的。写完这个答案后,我了解了“g”格式化程序:)【参考方案3】:

关于这些问题

如果我想将数字更改为 1、5、10、20,该怎么办? – 阿罗哈 2015 年 7 月 10 日 13:26

我想在两者之间添加刻度,例如 50,200 等,我该怎么做 那?我试过了, set_xticks[50.0,200.0] 但这似乎不起作用! – 捕食者 2015 年 8 月 3 日 12:54

但是对于 ax.axis([1, 100, 1, 100]),ScalarFormatter 给出 1.0, 10.0, ... 这不是我想要的。我希望它给出整数... – CPBL 2015 年 12 月 7 日 20:22

您可以使用 MINOR 格式化程序解决这些问题:

ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
ax.set_yticks([0.00000025, 0.00000015, 0.00000035])

在我的应用程序中,我使用了这种格式方案,我认为它可以解决与日志标量格式相关的大多数问题;数据 > 1.0 或 x 轴格式也可以这样做:

plt.ylabel('LOGARITHMIC PRICE SCALE')
plt.yscale('log')
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
#####################################################
#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        yd.append((line.get_ydata()).tolist())
yd = [item for sublist in yd for item in sublist]
ymin, ymax = np.min(yd), np.max(yd)
ax.set_ylim([0.9*ymin, 1.1*ymax])
#####################################################
z = []
for i in [0.0000001, 0.00000015, 0.00000025, 0.00000035,
          0.000001, 0.0000015, 0.0000025, 0.0000035,
          0.00001,  0.000015, 0.000025, 0.000035,
          0.0001, 0.00015, 0.00025, 0.00035,
          0.001, 0.0015, 0.0025, 0.0035,
          0.01, 0.015, 0.025, 0.035,
          0.1, 0.15, 0.25, 0.35]:

    if ymin<i<ymax:
        z.append(i)
        ax.set_yticks(z)                

有关“强制自动缩放”的 cmets,请参阅: Python matplotlib logarithmic autoscale

产生:

然后创建一个通用机器:

# user controls
#####################################################
sub_ticks = [10,11,12,14,16,18,22,25,35,45] # fill these midpoints
sub_range = [-8,8] # from 100000000 to 0.000000001
format = "%.8f" # standard float string formatting

# set scalar and string format floats
#####################################################
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter(format))
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter(format))

#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        yd.append((line.get_ydata()).tolist())
yd = [item for sublist in yd for item in sublist]
ymin, ymax = np.min(yd), np.max(yd)
ax.set_ylim([0.9*ymin, 1.1*ymax])

# add sub minor ticks
#####################################################
set_sub_formatter=[]
for i in sub_ticks:
    for j in range(sub_range[0],sub_range[1]):
        set_sub_formatter.append(i*10**j)
k = []
for l in set_sub_formatter:
    if ymin<l<ymax:
        k.append(l)
ax.set_yticks(k)
#####################################################

产量:

【讨论】:

【参考方案4】:

我发现Joe's 和Tom's 的答案非常有帮助,但是这些答案的 cmets 中有很多有用的细节。以下是这两种情况的总结:

范围大于 1

下面是类似 Joe 的示例代码,但范围更大:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 1000000])
ax.loglog()

plt.show()

这显示了这样的情节,使用科学记数法:

正如乔的回答一样,我使用ScalarFormatter,但我也调用set_scientific(False)。当规模达到 1000000 或以上时,这是必要的。

import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter

fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
    formatter = ScalarFormatter()
    formatter.set_scientific(False)
    axis.set_major_formatter(formatter)

plt.show()

范围低于 1

正如汤姆的回答,当范围低于 1 时会发生以下情况:

import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter

fig, ax = plt.subplots()
ax.axis([0.01, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
    formatter = ScalarFormatter()
    formatter.set_scientific(False)
    axis.set_major_formatter(formatter)

plt.show()

将 x 轴上的前两个刻度显示为零。

切换到FuncFormatter 可以解决这个问题。同样,我遇到了数字 1000000 或更高的问题,但是为格式字符串添加精度解决了它。

import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

fig, ax = plt.subplots()
ax.axis([0.01, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
    formatter = FuncFormatter(lambda y, _: ':.16g'.format(y))
    axis.set_major_formatter(formatter)

plt.show()

【讨论】:

我发现您的回答非常有帮助,我已将其 in this partial answer 用于我自己的问题。但是我的问题是问为什么我得到了一个特定的行为。你对此有什么想法吗?谢谢! 需要添加axis.set_minor_formatter(formatter) 以及小刻度。此外,16g 有时会导致像0.700000000000001 这样的标签。它有助于减少位数。【参考方案5】:

已接受答案中概述的机器效果很好,但有时简单的手动覆盖更容易。例如,要获取 1、10、100、1000 的刻度,您可以说:

ticks = 10**np.arange(4)
plt.xticks(ticks, ticks)

请注意,指定位置和标签至关重要,否则 matplotlib 将忽略您。

此机制可用于获取任意格式。例如:

plt.xticks(ticks, [ f"x:.0f" for x in ticks ])

plt.xticks(ticks, [ f"10^int(np.log10(x))" for x in ticks ])

plt.xticks(ticks, [ romannumerals(x) for x in ticks ])

(其中romannumerals 是一个想象的函数,将其参数转换为罗马数字)。

顺便说一句,如果您希望以任意间隔进行刻度,此技术也适用,例如,

ticks = [1, 2, 5, 10, 20, 50, 100]

等等

【讨论】:

以上是关于Matplotlib 对数刻度刻度标签数字格式的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib 颜色条刻度标签格式

Matplotlib:指定刻度标签的浮点格式

matplotlib 等高线图:对数刻度中的比例颜色条级别

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

Python matplotlib可视化:自定义轴标签格式化函数(在轴刻度上添加自定义的数值以及符号形式)使用自定义函数在Matplotlib中为坐标轴刻度添加自定义符号(例如,货币符号¥$等)

Matplotlib - 如何使用对数刻度设置线图的颜色条