Matplotlib mathtext:刻度标签中的字形错误
Matplotlib mathtext: Glyph errors in tick labels
我在 matplotlib 2.0.2 中使用默认 mathtext as opposed to the LaTeX 数学渲染引擎渲染数学时发现错误。数学文本似乎无法识别某些字形(在我的例子中是减号和乘号)。真正奇怪的是,只有当这些特定字形出现在刻度标签中时才会发生错误。当我故意输入一些数学表达式时图题,效果不错
考虑以下示例和生成的图像:
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
})
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
如图所示,刻度标签中的乘法和一些减号已替换为其他字符。如果我使用 LaTeX(通过将 'text.usetex'
设置为 True
),一切都呈现得很好。为什么会发生这种情况,更重要的是,如何在不从 mathtext 更改为 LaTeX 的情况下修复它?
附加信息
这是 运行 示例代码时打印的警告:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '\times' [U+d7]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
请注意,指数中出现的减号已正确呈现。如果我省略 'mathtext.fontset': 'cm'
,这些也可能不会呈现,产生另一个类似的警告:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '-' [U+2212]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
此外,如果我在 rcParams
中包含 'axes.unicode_minus': False
(并保留 'mathtext.fontset': 'cm'
),所有减号都会正确呈现,但乘号仍然存在问题。
乘号错误在旧版本的 matplotlib 上似乎不是问题(我测试过 1.5.1、1.4.3 和 1.3.1)。但是,这些 matplotib 坚持只在 10⁻²、10⁻¹、1、10、10² 等处生成刻度标签,因此不需要乘号。
错误报告
这已作为 bug report 提交给 Matplotlib。
我发现 STIX 字体是现代计算机可接受的替代品。
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update(
{
'text.usetex': False,
'font.family': 'stixgeneral',
'mathtext.fontset': 'stix',
}
)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
这会在我的笔记本电脑上产生以下输出:
问题原因
我现在明白是怎么回事了。 yticklabels 的格式都类似于
r'$\mathdefault{6\times10^{-2}}$'
这对于缺少 \times10^{-2}
部分的主要刻度标签效果很好。我相信这对于次要刻度标签是失败的,因为 \times
在 \mathdefault{}
内部不起作用。如 here 所述,\mathdefault{}
用于生成与用于数学文本的字体相同的常规(非数学)文本,但限制是可用符号少得多。由于 \mathdefault{}
中的所有内容都是 数学,因此 \mathdefault{}
的使用完全多余,因此可以安全地删除它。这解决了问题。
解决方案
可以使用 matplotlib 的 tick formatters 解决这个问题。但是,我想保留默认的(次要)刻度标签位置和(预期的)格式,因此更简单的解决方案是简单地删除刻度标签的 \mathdefault
部分:
import warnings
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.mathtext import MathTextWarning
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
# Use ASCII minus
'axes.unicode_minus': False,
})
# Function implementing the fix
def fix(ax=None):
if ax is None:
ax = plt.gca()
fig = ax.get_figure()
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
# Remove '\mathdefault' from all minor tick labels
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_xminorticklabels()]
ax.set_xticklabels(labels, minor=True)
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_yminorticklabels()]
ax.set_yticklabels(labels, minor=True)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
fix()
plt.savefig('test.png')
编写此修复程序的棘手部分是您无法在绘制图形之前获取刻度标签。因此我们需要先调用fig.canvas.draw()
。这将引发我已抑制的警告。这也意味着您应该尽可能晚地调用 fix()
,以便所有轴都像最后一样绘制。最后(如问题中所述),'axes.unicode_minus'
已设置为 False
以解决减号的类似问题。
生成的图像:
敏锐的 LaTeX 眼睛可能会发现关于 xticklabels 中的缺点仍然有些不对劲。这与问题无关,但发生是因为 xticklabels 中的数字未包含在 $...$
.
中
matplotlib 3.1.0 更新
从 matplotlib 版本 3.1.0 开始,警告通过 logging
模块发出,而不是 warnings
。要消除警告,请替换
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
与
# Force the figure to be drawn
import logging
logger = logging.getLogger('matplotlib.mathtext')
original_level = logger.getEffectiveLevel()
logger.setLevel(logging.ERROR)
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
logger.setLevel(original_level)
现在忽略警告,无论它是通过 logging
还是 warnings
发出的。
为了掩盖警告,我在消息中使用了 filterwarnings()
。
import warnings
warnings.filterwarnings("ignore", message="Glyph 146 missing from current font.")
因此请将 "Glyph 146 missing from current font." 替换为您自己的错误消息。
我在 matplotlib 2.0.2 中使用默认 mathtext as opposed to the LaTeX 数学渲染引擎渲染数学时发现错误。数学文本似乎无法识别某些字形(在我的例子中是减号和乘号)。真正奇怪的是,只有当这些特定字形出现在刻度标签中时才会发生错误。当我故意输入一些数学表达式时图题,效果不错
考虑以下示例和生成的图像:
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
})
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
如图所示,刻度标签中的乘法和一些减号已替换为其他字符。如果我使用 LaTeX(通过将 'text.usetex'
设置为 True
),一切都呈现得很好。为什么会发生这种情况,更重要的是,如何在不从 mathtext 更改为 LaTeX 的情况下修复它?
附加信息
这是 运行 示例代码时打印的警告:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '\times' [U+d7]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
请注意,指数中出现的减号已正确呈现。如果我省略 'mathtext.fontset': 'cm'
,这些也可能不会呈现,产生另一个类似的警告:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '-' [U+2212]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
此外,如果我在 rcParams
中包含 'axes.unicode_minus': False
(并保留 'mathtext.fontset': 'cm'
),所有减号都会正确呈现,但乘号仍然存在问题。
乘号错误在旧版本的 matplotlib 上似乎不是问题(我测试过 1.5.1、1.4.3 和 1.3.1)。但是,这些 matplotib 坚持只在 10⁻²、10⁻¹、1、10、10² 等处生成刻度标签,因此不需要乘号。
错误报告
这已作为 bug report 提交给 Matplotlib。
我发现 STIX 字体是现代计算机可接受的替代品。
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update(
{
'text.usetex': False,
'font.family': 'stixgeneral',
'mathtext.fontset': 'stix',
}
)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
这会在我的笔记本电脑上产生以下输出:
问题原因
我现在明白是怎么回事了。 yticklabels 的格式都类似于
r'$\mathdefault{6\times10^{-2}}$'
这对于缺少 \times10^{-2}
部分的主要刻度标签效果很好。我相信这对于次要刻度标签是失败的,因为 \times
在 \mathdefault{}
内部不起作用。如 here 所述,\mathdefault{}
用于生成与用于数学文本的字体相同的常规(非数学)文本,但限制是可用符号少得多。由于 \mathdefault{}
中的所有内容都是 数学,因此 \mathdefault{}
的使用完全多余,因此可以安全地删除它。这解决了问题。
解决方案
可以使用 matplotlib 的 tick formatters 解决这个问题。但是,我想保留默认的(次要)刻度标签位置和(预期的)格式,因此更简单的解决方案是简单地删除刻度标签的 \mathdefault
部分:
import warnings
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.mathtext import MathTextWarning
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
# Use ASCII minus
'axes.unicode_minus': False,
})
# Function implementing the fix
def fix(ax=None):
if ax is None:
ax = plt.gca()
fig = ax.get_figure()
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
# Remove '\mathdefault' from all minor tick labels
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_xminorticklabels()]
ax.set_xticklabels(labels, minor=True)
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_yminorticklabels()]
ax.set_yticklabels(labels, minor=True)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
fix()
plt.savefig('test.png')
编写此修复程序的棘手部分是您无法在绘制图形之前获取刻度标签。因此我们需要先调用fig.canvas.draw()
。这将引发我已抑制的警告。这也意味着您应该尽可能晚地调用 fix()
,以便所有轴都像最后一样绘制。最后(如问题中所述),'axes.unicode_minus'
已设置为 False
以解决减号的类似问题。
生成的图像:
$...$
.
matplotlib 3.1.0 更新
从 matplotlib 版本 3.1.0 开始,警告通过 logging
模块发出,而不是 warnings
。要消除警告,请替换
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
与
# Force the figure to be drawn
import logging
logger = logging.getLogger('matplotlib.mathtext')
original_level = logger.getEffectiveLevel()
logger.setLevel(logging.ERROR)
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
logger.setLevel(original_level)
现在忽略警告,无论它是通过 logging
还是 warnings
发出的。
为了掩盖警告,我在消息中使用了 filterwarnings()
。
import warnings
warnings.filterwarnings("ignore", message="Glyph 146 missing from current font.")
因此请将 "Glyph 146 missing from current font." 替换为您自己的错误消息。