【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入口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章