B.18 Python 基础绘图

Python 的 matplotlib 模块支持保存的图片格式有 eps, pdf, pgf, png, ps, raw, rgba, svg, svgz,不支持 cairo_pdf 绘图设备,所以这里使用 pdf 设备,但是这样会导致图形没有字体嵌入,从而不符合出版要求。一个解决办法是在后期嵌入字体,图形默认使用数学字体 STIX 和英文字体 DejaVu Sans,所以需要预先安装这些字体。

# CentOS 8
sudo dnf install -y dejavu-fonts-common dejavu-sans-fonts \
  dejavu-serif-fonts dejavu-sans-mono-fonts

借助 grDevices 包提供的 embedFonts() 函数,它支持 postscript 和 pdf 图形设备,嵌入字体借助了 Ghostscript 以及 PDF 阅读器 MuPDF

Windows 系统下需要手动指定 Ghostscript 安装路径,特别地,如果你想增加可选字体范围,需要指定相应字体搜索路径,而 Linux/MacOS 平台下不需要关心 Ghostscript 的安装路径问题,

Sys.setenv(R_GSCMD = "C:/Program Files/gs/gs9.26/bin/gswin64c.exe")
embedFonts(
  file = "cm.pdf", outfile = "cm-embed.pdf",
  fontpaths = system.file("fonts", package = "fontcm")
)
embedFonts(file = "cm.pdf", outfile = "cm-embed.pdf") 

另一个解决办法是使用 LaTeX 渲染图片中的文字,这就需要额外安装一些 LaTeX 宏包,此时默认执行渲染的 LaTeX 引擎是 PDFLaTeX。

tlmgr install type1cm cm-super dvipng psnfss ucs ncntrsbk helvetic

每年 4 月是 TeX Live 的升级月,升级指导见 https://www.tug.org/texlive/upgrade.html,升级之后,需要更新所有 LaTeX 宏包。

tlmgr update --self --all

如图 B.12 所示,我们采用第二个方法,它可以支持更好的数学公式显示,更多详情见 https://matplotlib.org/tutorials/text/mathtext.html

## [<matplotlib.lines.Line2D object at 0x7f3f48d57490>]
## Text(0.5, 0, 'Coord $x$')
## Text(0, 0.5, 'Coord $y$')
matplotlib 示例

图 B.12: matplotlib 示例

如果你的系统是 Windows/MacOS 可以添加 GPG 验证以增加安全性,最简单的方式就是:

tlmgr --repository http://www.preining.info/tlgpg/ install tlgpg

二维函数 \(f(x,y) = 20 + x^2 + y^2 - 10*\cos(2*\pi*x) - 10*\cos(2*\pi*y)\) 最小值 0 最大值 80

from math import cos, pi
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm

from matplotlib import rcParams
rcParams.update({'font.size': 18, 'text.usetex': True}) # 其它可配置选项见 rcParams.keys()
plt.switch_backend('agg')

xDomain = np.arange(-5.12, 5.12, .08)
yDomain = np.arange(-5.12, 5.12, .08)

X, Y = np.meshgrid(xDomain, yDomain)
z = [20 + x**2 + y**2 - (10*(cos(2*pi*x) + cos(2*pi*y))) for x in xDomain for y in yDomain]
Z = np.array(z).reshape(128,128)

fig = plt.figure(figsize = (12,10))
ax = fig.gca(projection='3d')
## <string>:1: MatplotlibDeprecationWarning: Calling gca() with keyword arguments was deprecated in Matplotlib 3.4. Starting two minor releases later, gca() will take no keyword arguments. The gca() function should only be used to get the current axes, or if no axes exist, create new axes with default keyword arguments. To create a new axes with non-default arguments, use plt.axes() or plt.subplot().
surf = ax.plot_surface(X, Y, Z, cmap=cm.RdYlGn, linewidth=1, antialiased=False)

ax.set_xlim(-5.12, 5.12)
## (-5.12, 5.12)
ax.set_ylim(-5.12, 5.12)
## (-5.12, 5.12)
ax.set_zlim(0, 80)
## (0.0, 80.0)
fig.colorbar(surf, aspect=30)
## <matplotlib.colorbar.Colorbar object at 0x7f3f48759490>
plt.title(r'Rastrigin Function in Two Dimensions')
## Text(0.5, 0.92, 'Rastrigin Function in Two Dimensions')
plt.show()