使用amsmath和sfmath进行绘图标记时的LaTeX字体样式问题

7 投票
3 回答
4571 浏览
提问于 2025-04-18 10:40

我有一个关于在Python中使用TeX的问题。

我启用了以下这些包:

import numpy
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rc('text', usetex = True)
matplotlib.rc('font', **{'family' : "sans-serif"})
params = {'text.latex.preamble' : [r'\usepackage{siunitx}', r'\usepackage{sfmath}']}
plt.rcParams.update(params)

你可以在我之前的一个问题中找到这个的原因。

不过,现在我也想使用amsmath包的字体。当我把它加到参数中时,它没有反应。我只需要amsmath来给图的x轴标记一个“a”。

所以我想给你看看我现在的样子:

enter image description here

还有我想要的样子(关于x轴标签):

enter image description here

请注意,为了生成第二张图片,我把sfmath换成了amsmath。这立刻搞乱了x轴和y轴的刻度。我不想要这种情况。

有没有可能只把一个字母或单词的字体样式改成amsmath的?这样我就可以在图的x轴标签上只使用那种字体样式。

另一种方法是把sfmath替换成amsmath,然后确保刻度看起来像第一张图片那样。

谢谢

顺便说一下,这些图是用以下方式创建的:

fig = plt.figure()

ax1 = fig.add_subplot(1, 1, 1)
ax1.set_xlabel(r"$a$", fontsize = 14)

plt.show()

3 个回答

0

我原以为,所有没有放在 $ ... $ 里的内容都会被当作普通文本来处理。这样的话,我们就可以单独定义一种文本字体和一种数学字体。然而,正如你提到的,刻度标签却受到了 LaTeX 数学字体的影响。这就有点奇怪了,因为 matplotlib 似乎把所有的刻度标签都放在了一个数学环境里。因此,根本的问题在于,刻度标签和坐标轴标签使用的是同一种字体。我们可以通过使用多种数学字母来解决这个问题,但这样的话,你就得到处使用 \mathrm{}

不过,我猜测你最初的问题是你不喜欢 Computer Modern 字体的刻度标签。所以我建议你把整体字体换成一种看起来不错的字体,这样刻度标签和坐标轴标签都能好看。我发现 Linux Libertine 字体就挺不错的。我对 matplotlib 是如何处理文本的不是特别确定,所以我建议同时定义文本和数学字体,并确保它们搭配得当。可以通过 newtx 这个 LaTeX 包来实现。使用 lualatex 时,前言部分可以保持得很简洁,这样就能达到我们想要的效果,而且我们使用的是一个现代的工具。根据 http://matplotlib.org/users/pgf.html,LuaLaTeX 是官方支持的,可以通过 PGF 后端生成 PDF 图形。所以,我建议使用以下代码:

import matplotlib as mpl
mpl.use('pgf')
import matplotlib.pyplot as plt

pgf_with_lualatex = {
    "text.usetex": True,
    "pgf.rcfonts": False,   # Do not set up fonts from rc parameters.
    "pgf.texsystem": "lualatex",
    "pgf.preamble": [
        r'\usepackage[libertine]{newtxmath}',
        r'\usepackage[no-math]{fontspec}',
        r'\usepackage{libertine}',
        ]
}
mpl.rcParams.update(pgf_with_lualatex)

fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_xlabel("$a b c$", fontsize = 14)
plt.savefig('figure.pdf')

我们通过将 rcfonts 设置为 False 来完全控制字体,并在 LaTeX 前言中设置文本和数学字体。为了让 newtxmath 设置数学字体,需要给 fontspec 包加上 no-math 选项。

生成的 PDF 看起来不错,这里有一张截图:

enter image description here

1

sfmath这个包有一个选项叫mathitOrig,它可以保持原来的斜体数学字体(在LaTeX中,大部分数学公式都是用斜体的)。如果你使用这个选项,再加上$\mathit{a}$,你得到的效果就接近你想要的了,虽然不是完全一样,因为这个数学字体和amsmath的字体还是有点区别。

import numpy
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rc('text', usetex = True)
matplotlib.rc('font', **{'family' : "sans-serif"})
params = {'text.latex.preamble' : [r'\usepackage{siunitx}', r'\usepackage[mathitOrig]{sfmath}']}
plt.rcParams.update(params)

fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_xlabel(r"$\mathit{a}$", fontsize = 14)

With matitOrig

8

我们可以通过你提到的第二种方法来实现你想要的效果:使用 amsmath,然后把刻度标签的字体换成不同的。这种方法有点像小技巧,虽然不是这里其他答案的常规做法,但它最接近你想要的效果。

诀窍在于使用 amsmath,让 Matplotlib 按照正常方式渲染所有内容,然后在最后,把刻度标签重置为原来的无衬线字体。我不确定这是否完全绕过了 usetex,但如果是的话,你就会得到一个奇怪的组合:TeX 渲染的标签和 Matplotlib 渲染的标签混在一起。看起来效果很不错!

关键的代码是

fontProperties = {'family':'sans-serif', 'weight': 'normal', 'size': 12}
ax1.set_xticklabels(ax1.get_xticks(), fontProperties)
ax1.set_yticklabels(ax1.get_yticks(), fontProperties)

把这个添加到你脚本的末尾,并且加上 amsmath,就会得到以下效果:

import numpy
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rc('text', usetex = True)
matplotlib.rc('font', **{'family' : "sans-serif"})
params = {'text.latex.preamble' : [r'\usepackage{siunitx}', r'\usepackage{amsmath}']}
plt.rcParams.update(params)

fig = plt.figure(figsize=(8,6))
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_xlabel(r"$a$", fontsize = 14)

fontProperties = {'family':'sans-serif', 'weight': 'normal', 'size': 12}
ax1.set_xticklabels(ax1.get_xticks(), fontProperties)
ax1.set_yticklabels(ax1.get_yticks(), fontProperties)

图像

撰写回答