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)
效果展示
總結:
五步法繪製簡單圖形流程:
- 準備數據
- 建立畫板與畫筆
- 繪製關鍵圖形
- 配置模板參數
- 圖形的保存於展示
具體使用過程:
- 在Preprocessing中的import_data中準備數據
- 在Postprocessing中編寫特殊的後續處理函數,若無,則跳過
- 在Axes中創建所需繪製的圖形邏輯,若存在,則可跳過
- 在PythonMatplotlib中創建畫圖主邏輯(如:simple_line),
- 調用Axes的對應畫圖函數
- 配置template
- 調用Postprocessing的後續處理函數,默認只寫保存和顯示