当我第一次尝试学术发表时,我得到的审稿意见之一是使用>300DPI (Dot Per Inch)清晰度的图表,以确保打印后的图标足够清晰。因此之后在代码中保存图片时,我都将参数设置为dpi=300。但今天当我调试matplotlib的图时,面对大小以英尺为单位的figsize,我很疑惑究竟应该怎么设置。
我的任务是将10个subfigure水平排列到一个Figure中,如果我是用默认的figsize(6.4x4.8inches,一个不怎么长的长方形),每个subfigure都变成了非常窄的一竖条,因此我需要调整figsize从而保证每个subfigure都有足够的水平空间。
Figure的figsize参数的默认值是[6.4, 4.8] ,单位是英尺(inch)。如果我需要将图片的比例变得更宽,例如接近10:1,我应该将6.4变成6.4*10,还是将4.8变成4.8/10?
我搜索到的第一个结论是,DPI,即打印时每英尺的点数,只有在确定打印尺寸的情况下才有意义。对于数字图片来说,确定他们清晰度的唯一属性是像素点(pixel)的数目。一张分辨率为3840 × 2160的图片在我们的电脑屏幕上总是清晰的。当打印他们时,打印的大小决定了打印后的清晰度,如果300dpi是清晰打印的经验法则,那我们可以确保它打印到12.8x7.2英寸(32.512x18.288cm,约A4大小)的纸上后还是清晰的。因此确保打印清晰度的第一步时知道打印的目标大小。
假设我们的图表将要打印在期刊中,我们可以得知图片打印后的大致大小,以Elsevier给出的建议为例:
| TARGET SIZE | Image width | Image width | Pixels at 300 dpi | Pixels at 500 dpi | Pixels at 1000 dpi |
|---|---|---|---|---|---|
| Minimal size | 30 mm | 85 pt | 354 | 591 | 1181 |
| Single column | 90 mm | 255 pt | 1063 | 1772 | 3543 |
| 1.5 column | 140 mm | 397 pt | 1654 | 2756 | 5512 |
| Double column (full width) | 190 mm | 539 pt | 2244 | 3740 | 7480 |

保险起见我们可以假设打印宽度是20cm,那我们10:1大小的图片的打印尺寸可以设定为20x2cm,也就是大约7.87x0.787inches,让我们试一试 figsize=(7.87,0.787), dpi=300:

所有东西都糊在了一起。。。
看起来subfigure中画出来的元素都太大了,无论是线条、文字、坐标还是标记等等。这是因为这些元素的单位是Point(1/72inches),和figsize一样也是打印尺寸。这些默认值对于6.4x4.8inches的版面正合适,但当我们将每个subfigure分到的版面缩小到0.787x0.787inches,这些元素就显得相对太大了。我们可以改变rcParams里面对应的参数,将他们变小:
params = {
'axes.linewidth': 0.5, # 0.8
'axes.labelsize': 5, # medium
'axes.titlesize': 4, # large
'font.size': 5, # 10.0
'lines.linewidth': 0.5, # 1.5
'lines.markersize': 2, # 6.0
'lines.markeredgewidth': 0.5, # 1.0
'boxplot.boxprops.linewidth': 0.5, # 1.0
'boxplot.capprops.linewidth': 0.5, # 1.0
'boxplot.flierprops.markeredgewidth': 0.5, # 1.0
'boxplot.meanprops.linewidth': 0.5, # 1.0
'boxplot.medianprops.linewidth': 0.5, # 1.0
'boxplot.whiskerprops.linewidth': 0.5, # 1.0
'xtick.major.width': 0.5, # 0.8
'ytick.major.width': 0.5, # 0.8
}
plt.rcParams.update(params)
fig, ax = plt.subplots(1, 10, figsize=(7.87,0.787), dpi=300)
这时的图片合理了许多:

但设置这么多参数实在太不优雅了,况且他们本身的默认值本身就是相互协调的,例如全部使用默认值时候的效果:

为了保持这种协调,我们可以保持每个subfigure使用默认的版面尺寸(6.4x4.8inches),但由于我没找单独设置figure对象中axes对象大小的参数,我只好把figure对象的figsize参数设置为(6.4*10)x4.8inches(162.56x12.192cm):

每个subfigure中绘制的元素像上图一样协调,但他们看起来实在太小了!他们得打印到有65寸电视那么大的纸上才看的顺眼。想让绘图元素相对与版面再大一点,那么把figsize再缩小一点,比如缩小1/4到(6.4*10*0.25)x(4.8*0.25)inches就看起来相当不错了:
最后一个小问题,我们这张16x1.2inches的图片按照300dpi保存会得到一张4800x360pixel的图片,宽度4800个像素超出了保证期刊清晰打印需要的2244个像素两倍。
Pixels = Resolution (DPI) × Print size (in inches)
这是因为我们figure对象的实际版面尺寸比目标打印尺寸多了约一倍,也就是说我们的figure将要以更小的尺寸打印,那我们我们只需要让dpi = 300 * print_size / figsize,就能有足够的像素使得打印的时候满足300dpi的清晰度。因此在这个例子里,Figure对象的dpi只需要设置为300 * 7.87 / 16 = 147.5625。
def my_subplots(nrow, ncol, dpi=300, scale=1, print_width=19/2.54):
"""Subplots with sufficient DPI
Args:
nrow: number of rows of subfigures
ncol: number of column of subfigures
dpi: target print DPI
scale: compact factor in range (0, 1], smaller means plot elements are more compact
print_width: target print witdth in inches, default is Elsevier double column width
Returns:
figure: matplotlib Figure object
ax: list of matplotlib Axes objects
"""
default_width, default_height = (6.4, 4.8) # inches
fig_width = default_width * ncol * scale
fig_height = default_height * nrow * scale
my_dpi = dpi * print_width / fig_width
try:
figure, ax = plt.subplots(nrow, ncol, figsize=(fig_width, fig_height), dpi=my_dpi, layout="constrained")
return (figure, ax)
except NameError:
print("===ERROR===(my_subplot): import matplotlib first by: import matplotlib.pyplot as plt")
return (None, None)
fig, ax = my_subplots(1, 10, scale=0.25) # output image has exact 2244 pixels in width
© pvvq 2024. 以上通过 知识共享署名-相同方式共享 4.0 国际许可协议许可。 邮件评论