【python】matplotlib画图的正确姿势---只需五步,就能画出让你心动的图形

1. 前言

做科研的小伙伴可能经常要与画图打交道,好马配好鞍,优秀的结果如果没有恰当的图形来展示,不得不说是一种遗憾。
而一个图好不好看,主要取决于以下几个方法:

  • 画图工具本身够不够优秀
  • 数据的分布是否有趣(如:高斯分布,幂律分布等)
  • 图形的选择是否恰当(如:曲线图,柱状图等)
  • 点线形状于配色方案(图形的灵魂,“红花”)
  • 图例坐标轴等模板的配置(“绿叶”)

matplotlib是python中最常用的绘图第三方库,基本可以实现绝大多数图形的绘制(很优秀),本文将分享一种高效的matplotlib绘图流程,让你轻轻松松五步作图。


2. 常规模式

import matplotlib.pyplot as plt  # 导入matplotlib

# 1.数据准备
x, y = [1, 2], [3, 4]

# 2.建立画板
plt.figure()

# 3.编写绘图逻辑
plt.plot(x, y, 'r-')

# 4.显示图形
plt.show()  # 

分析:

优点:代码量少,能快速作图(草图)
缺点:一图一例, 代码复用率很低

3. 细化绘图模式-----五步作图

import matplotlib.pyplot as plt  # 导入matplotlib

# 1.数据准备
x, y = [1, 2], [3, 4]

# 2.建立画板与画笔
fig, axes = plt.subplots()

# 3.使用画笔绘制关键图形
axes.plot(x,
		  y,
		  color=‘r',
		  linewidth=2,
		  linestyle='dashed')

# 4.匹配模板
axes.set_xlim([1, 2])
axes.set_ylim([3, 4])
axes.set_xlabel(kwargs.get("xlabel")
axes.set_ylabel(kwargs.get("ylabel")

# 5.图形显示与保存
fig.show()  
fig.savefig('test.pdf',dpi=500,bbox_inches='tight')

为什么推荐使用"五步作图"?

1. 接近实际的画图模式

新的绘图模式,能更好的模拟我们的画图习惯,设想一下,我们平常是怎么画图的呢?

第一步:在脑子里构想好想要画的东西(数据准备)
第二步:准备好画板和画笔(建立画板与画笔)
第三步:用画笔在画板上画出主体部分,并给它上色什么的(使用画笔绘制关键图形)
第四步:签上自己的大名,创作日期之类的(匹配模板)
第五步:保存大作,并展示给别人看(图形显示与保存)

2. 对象化操作,提高代码复用率

伟人曾经说过,python一切皆对象,对象才是python标准的玩法。所以,我们有必要提炼一些对象出来。

Preprocessing类:数据的预处理(准备数据)
Axes类:画笔对象,用来绘制各类图形
Template类:模板对象,给你的大作配上帅气的logo
Postprocessing类:后续处理,如图形的显示与保存
PythonMatplotlib类:控制画图的主逻辑,完成上文提到的5步

3. 具有成长性

对象化之后,由于画笔与模板的分离,我们只需要不断地完善Axes类和Template类(在Axes添加新的图形,在Template中添加新的配置项),就能画出越来越好看地图形。


接下来,我们将利用具体地实例,展示"五步法"绘制简单图形的过程(实例参考自:Matplotlib Python 画图教程 (莫烦Python)

直接上代码

import matplotlib.pyplot as plt
import numpy as np


class Preprocessing:

    def import_data(self):
        """
        绘制简单曲线
        """
        x = np.linspace(-10, 10, 50)
        y = 2 * x + 1
        return x, y


class Postprocessing:

    def fig_show(self):
        plt.tight_layout()
        self.fig.show()
        plt.pause(3)
        plt.close()

    def fig_save(self, **kwargs):
        save_name = kwargs.get("save_name", "untitled")
        self.fig.savefig(save_name + '.pdf',
                         dpi=500,
                         bbox_inches='tight')


class Template:

    def common_template(self, **kwargs):
        # 配置坐标轴与原点位置(spines): 是否使用笛卡尔坐标系
        if kwargs.get("cartesian"):
            # gca = 'get current axis'
            ax = plt.gca()
            ax.spines['right'].set_color('none')
            ax.spines['top'].set_color('none')

            ax.xaxis.set_ticks_position('bottom')
            # ACCEPTS: [ 'top' | 'bottom' | 'both' | 'default' | 'none' ]

            ax.spines['bottom'].set_position(('data', 0))
            # the 1st is in 'outward' | 'axes' | 'data'
            # axes: percentage of y axis
            # data: depend on y data

            ax.yaxis.set_ticks_position('left')
            # ACCEPTS: [ 'left' | 'right' | 'both' | 'default' | 'none' ]

            ax.spines['left'].set_position(('data', 0))

        # 设置图像有效范围(lim)
        self.axes.set_xlim(kwargs.get("xlim"))
        self.axes.set_ylim(kwargs.get("ylim"))
        if kwargs.get("zlim"):
            self.axes.set_zlim(kwargs.get("zlim"))

        # 设置坐标轴名称(label)
        if kwargs.get("xlabel"):
            self.axes.set_xlabel(kwargs.get("xlabel"))
        if kwargs.get("ylabel"):
            self.axes.set_ylabel(kwargs.get("ylabel"))

        # 设置坐标轴刻度(ticks)和标签(tick_labels)
        if type(kwargs.get("xticks")) == np.ndarray or kwargs.get(
                "xticks") == [] or kwargs.get("xticks"):
            self.axes.set_xticks(kwargs.get("xticks"))
        if type(kwargs.get("yticks")) == np.ndarray or kwargs.get(
                "yticks") == [] or kwargs.get("yticks"):
            self.axes.set_yticks(kwargs.get("yticks"))
        if kwargs.get("xtick_labels"):
            self.axes.set_xticklabels(kwargs.get("xtick_labels"))
        if kwargs.get("ytick_labels"):
            self.axes.set_yticklabels(kwargs.get("ytick_labels"))

        # 设置图例(legend)
        if kwargs.get("show_legend"):
            plt.legend(loc=kwargs.get("loc"))
        elif kwargs.get("legend_labels"):
            plt.legend(handles=self.handles[0]
            if len(self.handles) == 1 else self.handles,
                       labels=kwargs.get("legend_labels"),
                       loc=kwargs.get("loc", "best"))

        # 设置标题
        if kwargs.get("title"):
            self.axes.set_title(kwargs.get("title"),
                                fontsize=12,
                                fontname="Times New Roman")

        # 对数坐标
        if kwargs.get("xlog"):
            self.axes.set_xscale('log')
        if kwargs.get("ylog"):
            self.axes.set_yscale('log')

        # # 设置坐标轴刻度的字体
        if kwargs.get("tick_font"):
            labels = self.axes.get_xticklabels() + self.axes.get_yticklabels()
            for label in labels:
                label.set_fontname('Times New Roman')
                label.set_fontsize(kwargs.get("tick_font"))
                label.set_bbox(
                    dict(facecolor=kwargs.get("facecolor", "white"),
                         edgecolor=kwargs.get("edgecolor", "none"),
                         alpha=kwargs.get("alpha", 0.8),
                         zorder=kwargs.get("zorder", 2)))

        # 设置色标(colorbar)
        if kwargs.get("colorbar"):
            plt.colorbar(shrink=kwargs.get("shrink", .92))
        return


class Axes:

    def draw_line(self, x, y, **kwargs):
        # the "," is very important in here l1, = plt... and l2, = plt... for this step
        l, = self.axes.plot(x,
                            y,
                            color=kwargs.get('color'),
                            marker=kwargs.get('marker'),
                            markersize=kwargs.get('markersize', 4),
                            markerfacecolor=kwargs.get('markerfacecolor'),
                            alpha=kwargs.get('alpha', 1),
                            linewidth=2,
                            linestyle='dashed')
        return l


class PythonMatplotlib(Preprocessing, Postprocessing, Template, Axes):

    def __init__(self, **kwargs):
        # 数据准备
        self.data = self.import_data()

        # 建立画板与画笔
        self.fig, self.axes = plt.subplots(num=kwargs.get("num"),
                                           figsize=kwargs.get("figsize"))
        self.handles = []

        # 常用配置
        self.ALPHA = [1, 1, 1, 1, 1, 1]
        self.COLOR = [plt.get_cmap('tab20c').colors[i] for i in [0, 4, 8, 12, 16, 18]]
        self.MARKER = ['^', 'o', 's', '*', '+', 'D']
        self.MARKER_COLOR = [plt.get_cmap('tab20c').colors[i] for i in [1, 5, 8, 12, 16, 18]]

    def simple_line(self, **kwargs):
        # 绘制图形
        self.draw_line(*self.data, color='red', linewidth=1.0, linestyle='--')

        # 使用模板
        self.common_template(xlim=(-1, 2),
                             ylim=(-2, 3),
                             xlabel="I am x",
                             ylabel="I am y",
                             xticks=np.linspace(-1, 2, 5),
                             yticks=[-2, -1.8, -1, 1.22, 3],
                             ytick_labels=[
                                 r'$really\ bad$', r'$bad$', r'$normal$',
                                 r'$good$', r'$really\ good$'
                             ])

        # 输出与保存(PDF)
        if kwargs.get("fig_show", True):
            self.fig_show()
        if kwargs.get("save_as_pdf"):
            self.fig_save(save_as_pdf=kwargs.get("save_as_pdf"),
                          save_name=kwargs.get("save_name", "simple_line"))


if __name__ == '__main__':
    client = PythonMatplotlib()
    client.simple_line(save_as_pdf=True)

效果展示

在这里插入图片描述

总结:

五步法绘制简单图形流程:

  1. 准备数据
  2. 建立画板与画笔
  3. 绘制关键图形
  4. 配置模板参数
  5. 图形的保存于展示

具体使用过程:

  • 在Preprocessing中的import_data中准备数据
  • 在Postprocessing中编写特殊的后续处理函数,若无,则跳过
  • 在Axes中创建所需绘制的图形逻辑,若存在,则可跳过
  • 在PythonMatplotlib中创建画图主逻辑(如:simple_line),
    • 调用Axes的对应画图函数
    • 配置template
    • 调用Postprocessing的后续处理函数,默认只写保存和显示

完整代码

GitHub入口

原创文章 36 获赞 32 访问量 2756

猜你喜欢

转载自blog.csdn.net/weixin_43868754/article/details/104956081