Matplotlib 2 mathtext:刻度标签中的字形错误

jmd*_*_dk 7 python unicode latex matplotlib text-rendering

在使用默认的mathtext而不是LaTeX数学渲染引擎时,我在matplotlib 2.0.2中渲染数学时发现了错误.似乎一些字形(在我的情况下是减号和乘号)不能被mathtext识别.真正奇怪的是,只有当这些特定字形出现在刻度标签中时才会出现错误.当我故意在图标题中输入一些蹩脚的表达时,它可以正常工作.

考虑以下示例和结果图像:

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')
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

请注意,指数中出现的减号会正确呈现.如果我遗漏'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)
Run Code Online (Sandbox Code Playgroud)

此外,如果我包含'axes.unicode_minus': FalsercParams(并保持'mathtext.fontset': 'cm')中,所有减号都会正确渲染,尽管问题仍然存在于乘法符号中.

在较旧版本的matplotlib上,乘法符号错误似乎不是问题(我已经测试过1.5.1,1.4.3和1.3.1).然而,这些matplotib坚持只生产10-2,1,10 -1,1,10,10 2等刻度标签,因此不需要乘法符号.

ngo*_*aum 6

我发现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')
Run Code Online (Sandbox Code Playgroud)

这会在我的笔记本电脑上产生以下输出:

在此输入图像描述


jmd*_*_dk 3

问题原因

我现在明白发生了什么事。yticklabels 的格式都类似于

r'$\mathdefault{6\times10^{-2}}$'
Run Code Online (Sandbox Code Playgroud)

这对于\times10^{-2}缺少该部分的主要刻度标签效果很好。我相信这对于小刻度标签会失败,因为\times\mathdefault{}. 如此处所述,\mathdefault{}用于生成常规(非数学)文本,其字体与数学文本使用的字体相同,但可用的符号要少得多。由于其中的所有内容\mathdefault{} 都是数学,因此使用\mathdefault{}是完全多余的,因此可以安全地将其删除。这解决了这个问题。

解决方案

人们可以使用 matplotlib 的刻度格式化程序来解决这个问题。然而,我想保留默认的(次要)刻度标签位置和(预期的)格式,因此更简单的解决方案就是撕掉\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')
Run Code Online (Sandbox Code Playgroud)

编写此修复程序的棘手部分是在绘制图形之前无法获取刻度标签。因此我们需要首先调用fig.canvas.draw(). 这将引发我已压制的警告。这也意味着您应该fix()尽可能晚地调用,以便所有轴都按照最终的样子绘制。最后(如问题中所述),'axes.unicode_minus'已设置False为修复带有减号的类似问题。

结果图像: 测试.png 敏锐的 LaTeX 眼睛可能会发现 xticklabel 中的缺点仍然略有偏差。这与问题无关,但发生是因为 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()
Run Code Online (Sandbox Code Playgroud)

    # 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)
Run Code Online (Sandbox Code Playgroud)

现在,无论警告是否通过logging或发出,都会忽略该警告warnings