电子书地址:https://github.com/rougier/scientific-visualization-book
一本科学详解matplotlib的可视化书籍
目录
anatomy of figure
如上图所示,matplotlib图形由多个元素的层次结构组成,这些元素组合在一起形成实际图形,了解图形的不同元素很重要。
元素
Figure
figure最重要的元素是figure本身。它是在调用figure方法时创建的,我们已经看到您可以指定其大小,也可以指定背景色(facecolor)和标题(suptitle)。重要的是要知道,在保存figure时不会使用背景色,因为savefig函数还有一个facecolor参数(默认为白色),该参数将覆盖figure的背景色。如果不需要任何背景,可以在保存figure时指定transparent=True
。
Axes
Axes是第二个最重要的元素,对应数据实际的呈现区域。它也被称为subplot。每个图形可以有一到多个轴,每个轴通常由称为spines的四条边(左、上、右和下)包围。这些spines中的每一根都可以用major ticks和minor ticks(分别指向内部或外部)、tick labels和label来装饰。
Axis
这些装饰过的spines被称为轴(axis)。水平方向是X轴,垂直方向是Y轴。每一个都由spine、major ticks和minor ticks、主刻度标签和次刻度标签以及轴标签组成。
Spines
spines是连接轴刻度线并标注数据区域边界的线。它们可以放置在任意位置,可以是可见的,也可以是不可见的。
Artist
图形上的一切,包括图形、轴和轴对象,都是artist。包括文字对象、Line2D对象、集合对象、面片对象。渲染人物时,所有artist都被绘制到画布上。给定的artist只能在一个轴上。
渲染
作者对matplotlib的渲染也给出了详细的设置方式
import matplotlib
matplotlib.use("xxx")
维度和分辨率
fig = plt.figure(figsize=(6,6))
plt.savefig("output.png")
matplotlib的默认dpi为100
,该尺寸对应于6英寸乘以6英寸的分辨率。对于一篇 scientific article,出版商通常会要求figures dpi在300到600之间。为了让dpi设置变得正确,我们需要知道在文档中插入图形的物理尺寸是多少。
作者提供了一个更具体的例子,让我们考虑一下这本书的格式是A5(148×210毫米)。左右边距各为20毫米,图像通常使用全文宽度显示。这意味着图像的物理宽度正好为108毫米,或大约为4.25英寸。如果我们使用推荐的600 dpi,我们最终会得到2550 pixels的宽度,这可能超出屏幕分辨率,因此不太方便。相反,当我们在屏幕上显示图形时,我们可以使用默认的 matplotlib dpi(100),只有在保存图形时,我们才使用不同且更高的dpi:
def figure(dpi):
fig = plt.figure(figsize=(4.25,.2))
ax = plt.subplot(1,1,1)
text = "Text rendered at 10pt using %d dpi" % dpi
ax.text(0.5, 0.5, text, ha="center", va="center",
fontname="Source Serif Pro",
fontsize=10, fontweight="light")
plt.savefig("figure-dpi-%03d.png" % dpi, dpi=dpi)
figure(50), figure(100), figure(300), figure(600)
作者提供了许多有用的科研作图练习
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def curve():
n = np.random.randint(1,5)
centers = np.random.normal(0.0,1.0,n)
widths = np.random.uniform(5.0,50.0,n)
widths = 10*widths/widths.sum()
scales = np.random.uniform(0.1,1.0,n)
scales /= scales.sum()
X = np.zeros(500)
x = np.linspace(-3,3,len(X))
for center, width, scale in zip(centers, widths, scales):
X = X + scale*np.exp(- (x-center)*(x-center)*width)
return X
np.random.seed(123)
cmap = mpl.cm.get_cmap("Spectral")
fig = plt.figure(figsize=(8, 8))
ax = None
for n in range(3):
ax = plt.subplot(1, 3, n + 1, frameon=False, sharex=ax)
for i in range(50):
Y = curve()
X = np.linspace(-3, 3, len(Y))
ax.plot(X, 3 * Y + i, color="k", linewidth=0.75, zorder=100 - i)
color = cmap(i / 50)
ax.fill_between(X, 3 * Y + i, i, color=color, zorder=100 - i)
# Some random text on the right of the curve
v = np.random.uniform(0, 1)
if v < 0.4:
text = "*"
if v < 0.05:
text = "***"
elif v < 0.2:
text = "**"
ax.text(
3.0,
i,
text,
ha="right",
va="baseline",
size=8,
transform=ax.transData,
zorder=300,
)
ax.yaxis.set_tick_params(tick1On=False)
ax.set_xlim(-3, 3)
ax.set_ylim(-1, 53)
ax.axvline(0.0, ls="--", lw=0.75, color="black", zorder=250)
ax.text(
0.0,
1.0,
"Value %d" % (n + 1),
ha="left",
va="top",
weight="bold",
transform=ax.transAxes,
)
if n == 0:
ax.yaxis.set_tick_params(labelleft=True)
ax.set_yticks(np.arange(50))
ax.set_yticklabels(["Serie %d" % i for i in range(1, 51)])
for tick in ax.yaxis.get_major_ticks():
tick.label.set_fontsize(6)
tick.label.set_verticalalignment("bottom")
else:
ax.yaxis.set_tick_params(labelleft=False)
plt.tight_layout()
plt.savefig("./zorder-plots.png", dpi=600)
plt.savefig("./zorder-plots.pdf")
plt.show()
参考资料:
[1] Nicolas Rougier. Scientific Visualization: Python + Matplotlib. Nicolas P. Rougier. 2021, 978-2-
9579901-0-8. hal-03427242