Python Matplotlib mathtext:记号标签中的图示符错误

Python Matplotlib mathtext:记号标签中的图示符错误,python,matplotlib,unicode,latex,text-rendering,Python,Matplotlib,Unicode,Latex,Text Rendering,我在matplotlib 2.0.2中观察到在使用默认值而不是数学渲染引擎时渲染数学时出现错误。mathtext似乎无法识别某些字形(在我的例子中是减号和乘法符号)。真正奇怪的是,只有当这些特定的标志符号出现在记号标签中时,才会发生错误。当我故意在图形标题中键入一些数学表达式时,效果很好 考虑以下示例和生成的图像: import matplotlib import matplotlib.pyplot as plt # Customize matplotlib matplotlib.rcPara

我在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')

如图所示,记号标签中的乘法和减号已被其他字符替换。如果我使用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、1、10、10²等,因此不需要乘法符号

缺陷报告
这是作为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{}
中不起作用。如上所述,
\mathdefault{}
用于生成与mathtext相同字体的常规(非数学)文本,但其限制是可用的符号要少得多。由于
\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')
编写此修复程序的棘手部分是,在绘制图形之前,您无法获取刻度标签。因此,我们需要首先调用
fig.canvas.draw()
。这将引起警告,我已经抑制了它。这也意味着您应该尽可能晚地调用
fix()
,以便最终绘制所有轴。最后(如问题中所述),
'axes.unicode_-minus'
已设置为
False
,以解决带有减号的类似问题

生成的图像: 敏锐的乳胶眼可能会发现,关于Xticklabel的缺点,有些东西仍然有点不对劲。这与问题无关,但发生这种情况是因为XTICKLabel中的数字没有包含在
$…$

matplotlib 3.1.0的更新 从matplotlib 3.1.0版开始,警告通过
日志记录
模块发出,而不是
警告
。要使警告静音,请更换

    # Force the figure to be drawn
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', category=MathTextWarning)
        fig.canvas.draw()


它现在忽略警告,不管它是通过
日志记录发出的
还是通过
警告发出的
,以屏蔽我在消息中使用的警告

导入警告

警告。过滤器警告(“忽略”,message=“当前字体缺少Glyph 146”)


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

这似乎是
cmr10
font的问题。如果您使用
'font.serif':“Times New Roman',
,它会显示得很好。如果您认为cmr10也可以使用,那么最好将其发布在@ImportanceOfBeingErnest上。是的,更改为其他字体会有所帮助。不过,我非常喜欢matplotlib附带的“计算机现代”字体。我还没有找到保证所有matplotlib安装都可用的字体的完整列表。我想如果字体在标题中正确呈现,但在轴标签中不正确呈现,可能会被认为是一个错误。因此,在它所属的地方报告它仍然是最好的主意,我会说。我确实通过《蟒蛇3》复制了同样的想法
    # 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)