这里介绍一下我常用的论文图表绘制的样式,能够极大地减轻后期手动调整图表样式,比如调整线宽,子图字号,间距,颜色等等内容,从而提高工作效率。

在绘制论文图表的时候,通常要对图表的样式进行精心调整。这里要考虑到的几个情况是:一般杂志要求字号是 6-8 pt,然后对于 Figure 一般是要求宽度在 160 mm 以内(如果是单column最好在80 mm以内),还有就是一张 Figure 通常有好多子图。而matplotlib或者seaborn默认的样式往往是用于单张 Figure 而不是子图,所以实际绘图的样式需要基于以上情况进行调整。

虽然在前面的python教程中,我总是推荐处理完数据之后,可以把用于作图的数据保存为 csv 或者 excel 这类电子表格文件,方便使用其它大家熟练的作图软件进行绘制(比如 originLab,GraphPad 之类的)。但事实上 python 对数据进行可视化也是很方便的,只是如果要满足科技论文发表的需求,调整美学样式上就不是特别方便了,因为这个是依赖人主观感受需要不断调整代码中作图参数的过程。

不过当个人的作图风格在不断实践中趋于稳定的时候,这时就可以调整出来一套固定的公用参数。这个时候基于代码进行作图,就不仅仅是因为数据量太大不得已而为之,还因为它在固定参数之后,可以无限复用。

直角坐标轴设置

%matplotlib inline
%config InlineBackend.figure_format='svg'
import matplotlib.pyplot as plt

inch = 2.54  # 1 inch = 2.54 cm
# 按厘米作为单位控制图表大小可以使用如下命令
# plt.figure(figsize=(3/inch,2/inch))

# 控制直角坐标系
plt.rcParams.update({
    'font.family': 'Arial',
    'font.size': 6,                  # 字体大小
    'figure.dpi': 300,                # 图表分辨率
    'axes.linewidth': 0.5,            # 坐标轴粗细
    'axes.edgecolor': 'black',        # 坐标轴颜色
    'axes.labelsize': 6,             # 轴标签字体大小
    'axes.labelweight': 'regular',       # 轴标签粗体
    'axes.labelpad': 1,
    'xtick.major.width': 0.5,         # x轴主刻度线粗细
    'ytick.major.width': 0.5,         # y轴主刻度线粗细
    'xtick.major.size': 2,            # x轴主刻度线长短
    'ytick.major.size': 2,            # y轴主刻度线长短
    'xtick.major.pad': 1,           # x轴主刻度线与标签间距
    'ytick.major.pad': 1,           # y轴主刻度线与标签间距
})

注意这一套样式参数,仅适合于一张大 Figure 中子图表的绘制。

图表后期加工

下图是一个简单的直方分布图,它想表达的是大于一定阈值的数据分布占比高达 93.9 %。实际作图时,先使用python进行作图,控制bin的数量,但颜色就先默认灰色,然后导出为 svg 格式的矢量图,图中带颜色的文字和虚线都是后期在矢量绘图软件(如Ai)中添加的,然后bar的颜色也是后期修改的(因为数量不多,Ai上的「直接选择工具」也很容易选中这些bar)。


需要注意的是,在Ai中修改图表的美学样式是一个非常依赖手工操作的事情,所以只适合少量的需要修改和额外标注等情况。如果是复杂图表,这个时候再使用Ai来改变,不仅几乎很难办到,还存在极高的篡改数据的风险,比如heatmap的颜色色系,就很可能将数据和颜色之间的线性映射关系变为非线性的。

自定义colormap

在绘制比较复杂的图表的时候,最好是使用 colormap 来对数值进行线性映射。这里我通常喜欢配合主色调选定一种颜色,然后通过渐变饱和度来生成自定义线性颜色映射表:

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from skimage import color

def gen_linear_colors(rgb:list, mode='sat', num=10):
    '''使用snipaste复制选中的颜色的rgb数值,生成该单色系的线性颜色序列,
    转换到 hsv颜色空间,亮度自动调满,然后变化饱和度;或者饱和度调满,变化亮度。'''
    cc = np.array(rgb)
    hue, sat, val = color.rgb2hsv(cc)
    box = []
    if mode=='sat':
        val = 1
        for i in range(num):
            sat = (i+1)/num
            c2 = color.hsv2rgb(np.array([hue, sat, val]))
            box.append(c2)
    elif mode=='val':
        sat = 1
        for i in range(num):
            val = (i+1)/num
            c2 = color.hsv2rgb(np.array([hue, sat, val]))
            box.append(c2)
    return box

cc = np.array([212, 20, 90])   # 指定一种颜色
color_list = gen_linear_colors(cc, num=30)
cmap = ListedColormap(color_list)

另一种生成自定义colormap的方法:

from matplotlib.colors import LinearSegmentedColormap

cdict = {
    'red': (
        (0.0,  0.0, 0.0),
        (0.1,  0.0, 0.0),
        (1.0,  1.0, 1.0),
    ),
    'green': (
        (0.0,  0.0, 0.0),
        (0.4,  0.0, 0.0),
        (1.0,  1.0, 1.0),
    ),
    'blue': (
        (0.0,  0.0, 0.0),
        (0.64,  0.0, 0.0),
        (1.0,  1.0, 1.0),
    )
}

cmap_name = 'AFM'
afm_cmap = LinearSegmentedColormap(cmap_name, cdict)

注意这里传入的字典里,RGB不同通道分别使用一个table来表示,其中每一列从上到下得是从0到1递增的。其实可以把这一列想象为一个滑杆(PPT调整渐变色的那个东西),中间那个数值就是滑块,类似的也可以增加多行。

最后修改:2025 年 05 月 13 日
请大力赞赏以支持本站持续运行!