使用amsmath和sfmath进行绘图标记时的LaTeX字体样式问题
我有一个关于在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”。
所以我想给你看看我现在的样子:
还有我想要的样子(关于x轴标签):
请注意,为了生成第二张图片,我把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 个回答
我原以为,所有没有放在 $ ... $
里的内容都会被当作普通文本来处理。这样的话,我们就可以单独定义一种文本字体和一种数学字体。然而,正如你提到的,刻度标签却受到了 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 看起来不错,这里有一张截图:
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)
我们可以通过你提到的第二种方法来实现你想要的效果:使用 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)