【问题标题】:Matplotlib mathtext: Glyph errors in tick labelsMatplotlib mathtext:刻度标签中的字形错误
【发布时间】:2018-04-25 11:51:04
【问题描述】:

在 matplotlib 2.0.2 中使用默认的 mathtext 而不是 LaTeX 数学渲染引擎时,我观察到了错误。似乎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')

如图所示,刻度标签中的乘法和一些减号已被其他字符替换。如果我使用 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。

【问题讨论】:

  • 这似乎是cmr10 字体的问题。如果您改用'font.serif': 'Times New Roman',,它会显示得很好。如果您认为 cmr10 也应该可以工作,最好将其发布到 matplotlib issue tracker
  • @ImportanceOfBeingErnest 是的,更改为其他字体会有所帮助。但是,我非常想要 matplotlib 附带的类似“计算机现代”的字体。我无法找到保证所有 matplotlib 安装都可用的完整字体列表。
  • 我想如果字体在标题中正确呈现但在轴标签中不正确呈现,则可能被视为错误。所以我想说,在它所属的地方报告它仍然是最好的主意。

标签: python matplotlib unicode latex text-rendering


【解决方案1】:

我发现 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')

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

【讨论】:

  • 我确实通过 anaconda 3 复制了同样的内容
【解决方案2】:

问题原因

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

r'$\mathdefault{6\times10^{-2}}$'

这适用于主要刻度标签,其中\times10^{-2} 部分不存在。我相信这对于小刻度标签会失败,因为\times\mathdefault{} 内部不起作用。如here 所述,\mathdefault{} 用于生成具有与 mathtext 相同字体的常规(非数学)文本,其限制是可用的符号要少得多。由于\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 发出的警告都会忽略。

【讨论】:

    【解决方案3】:

    为了掩盖我在消息中使用filterwarnings() 的警告。

    import warnings

    warnings.filterwarnings("ignore", message="Glyph 146 missing from current font.")

    所以替换“当前字体中缺少 Glyph 146”。由您自己的错误消息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-19
      • 2013-07-23
      • 2014-03-22
      • 2019-07-10
      • 1970-01-01
      相关资源
      最近更新 更多