Matplotlib 中文用戶指南 3.5 藝術家教程

藝術家教程

原文:Artist tutorial

譯者:飛龍

協議:CC BY-NC-SA 4.0

matplotlib API 有三個層級。 matplotlib.backend_bases.FigureCanvas是繪制圖形的區域,matplotlib.backend_bases.Renderer是知道如何在ChartCanvas上繪制的對象,而matplotlib.artist.Artist是知道如何使用渲染器在畫布上畫圖的對象。 FigureCanvasRenderer處理與用戶界面工具包(如 wxPython)或 PostScript? 等繪圖語言交互的所有細節,Artist處理所有高級結構,如表示和布局圖形,文本和線條。用戶通常要花費95%的時間來處理藝術家。

有兩種類型的藝術家:基本類型和容器類型。基本類型表示我們想要繪制到畫布上的標準圖形對象:Line2DRectangleTextAxesImage等,容器是放置它們的位置(AxisAxesFigure)。標準用法是創建一個Figure實例,使用Figure創建一個或多個AxesSubplot實例,并使用Axes實例的輔助方法來創建基本類型。在下面的示例中,我們使用matplotlib.pyplot.figure()創建一個Figure實例,這是一個便捷的方法,用于實例化Figure實例并將它們與你的用戶界面或繪圖工具包FigureCanvas連接。正如我們將在下面討論的,這不是必須的 - 你可以直接使用 PostScript,PDF,Gtk+ 或 wxPython FigureCanvas實例,直接實例化你的圖形并連接它們 - 但是因為我們在這里關注藝術家 API,我們讓pyplot為我們處理一些細節:

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(2,1,1) # two rows, one column, first plot

Axes可能是 matplotlib API 中最重要的類,你將在大多數時間使用它。 這是因為Axes是大多數對象所進入的繪圖區域,Axes有許多特殊的輔助方法(plot()text()hist()imshow())來創建最常見的圖形基本類型 Line2DTextRectangleImage)。 這些輔助方法將獲取你的數據(例如 numpy 數組和字符串),并根據需要創建基本Artist實例(例如,Line2D),將它們添加到相關容器中,并在請求時繪制它們。 大多數人可能熟悉子圖,這只是Axes的一個特例,它存在于Subplot實例的列網格的固定行上。 如果要在任意位置創建Axes,只需使用add_axes()方法,該方法接受[left, bottom, width, height]值的列表,以 0~1 的圖形相對坐標為單位:

fig2 = plt.figure()
ax2 = fig2.add_axes([0.15, 0.1, 0.7, 0.3])

以我們的例子繼續:

import numpy as np
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*np.pi*t)
line, = ax.plot(t, s, color='blue', lw=2)

在這個例子中,ax是上面的fig.add_subplot調用創建的Axes實例(記住Subplot只是Axes的一個子類),當你調用ax.plot時,它創建一個Line2D實例并將其添加到Axes.lines列表中。 在下面的 ipython 交互式會話中,你可以看到Axes.lines列表的長度為 1,并且包含由line, = ax.plot...調用返回的相同線條:

In [101]: ax.lines[0]
Out[101]: <matplotlib.lines.Line2D instance at 0x19a95710>

In [102]: line
Out[102]: <matplotlib.lines.Line2D instance at 0x19a95710>

如果你對ax.plot進行連續調用(并且保持狀態為『on』,這是默認值),則將在列表中添加其他線條。 你可以稍后通過調用列表方法刪除線條;任何一個方法都可以:

del ax.lines[0]
ax.lines.remove(line)  # one or the other, not both!

軸域也擁有輔助方法,用于設置和裝飾 x 和 y 軸的刻度、刻度標簽和軸標簽:

xtext = ax.set_xlabel('my xdata') # returns a Text instance
ytext = ax.set_ylabel('my ydata')

當你調用ax.set_xlabel時,它將信息傳遞給XAxisText實例,每個Axes實例都包含XAxisYAxis,它們處理刻度、刻度標簽和軸標簽的布局和繪制。

嘗試創建下面的圖形:

自定義你的對象

圖中的每個元素都由一個 matplotlib 藝術家表示,每個元素都有一個擴展屬性列表用于配置它的外觀。 圖形本身包含一個Rectangle,正好是圖形的大小,你可以使用它來設置圖形的背景顏色和透明度。 同樣,每個Axes邊框(在通常的 matplotlib 繪圖中是標準的白底黑邊)擁有一個Rectangle實例,用于確定軸域的顏色,透明度和其他屬性,這些實例存儲為成員變量Figure.patchAxes.patch(『Patch』是一個繼承自 MATLAB 的名稱,它是圖形上的一個顏色的 2D『補丁』,例如矩形,圓和多邊形)。每個 matplotlib 藝術家都有以下屬性。

屬性 描述
alpha 透明度 - 0 ~ 1 的標量
animated 用于幫助動畫繪制的布爾值
axes 藝術家所在的軸域,可能為空
clip_box 用于剪切藝術家的邊框
clip_on 剪切是否開啟
clip_path 藝術家被剪切的路徑
contains 一個拾取函數,用于判斷藝術家是否位于拾取點
figure 藝術家所在的圖形實例,可能為空
label 文本標簽(用于自動標記)
picker 控制對象拾取的 Python 對象
transform 變換
visible 布爾值,表示藝術家是否應該繪制
zorder 確定繪制順序的數值
rasterized 布爾值,是否將向量轉換為光柵圖形(出于壓縮或 eps 透明度)

每個屬性都使用一個老式的settergetter(是的,我們知道這會刺激 Python 愛好者,我們計劃支持通過屬性或 traits 直接訪問,但它還沒有完成)。 例如,要將當前alpha值變為一半:

a = o.get_alpha()
o.set_alpha(0.5*a)

如果你打算可以一次性設置一些屬性,你也可以以關鍵字參數使用set方法,例如:

o.set(alpha=0.5, zorder=2)

如果你在 Python 交互式 Shell 中工作,檢查Artist屬性的一種方便的方法是使用matplotlib.artist.getp()函數(在 pylab 中只需要getp()),它列出了屬性及其值。 這適用于從Artist派生的類,例如FigureRectangle。 這里是上面提到的Figure的矩形屬性:

In [149]: matplotlib.artist.getp(fig.patch)
    alpha = 1.0
    animated = False
    antialiased or aa = True
    axes = None
    clip_box = None
    clip_on = False
    clip_path = None
    contains = None
    edgecolor or ec = w
    facecolor or fc = 0.75
    figure = Figure(8.125x6.125)
    fill = 1
    hatch = None
    height = 1
    label =
    linewidth or lw = 1.0
    picker = None
    transform = <Affine object at 0x134cca84>
    verts = ((0, 0), (0, 1), (1, 1), (1, 0))
    visible = True
    width = 1
    window_extent = <Bbox object at 0x134acbcc>
    x = 0
    y = 0
    zorder = 1

所有類的文檔字符串也包含Artist屬性,因此你可以查閱交互式『幫助』或 Artist模塊,來獲取給定對象的屬性列表。

對象容器

現在我們知道如何檢查和設置我們想要配置的給定對象的屬性,現在我們需要如何獲取該對象。 前面提到了兩種對象:基本類型和容器類型。 基本類型通常是你想要配置的東西(Text實例的字體,Line2D的寬度),雖然容器也有一些屬性 - 例如 Axes是一個容器藝術家,包含你的繪圖中的許多基本類型,但它也有屬性,比如xscale來控制xaxis是『線性』還是『對數』。 在本節中,我們將回顧各種容器對象存儲你想要訪問的藝術家的位置。

圖形容器

頂層容器藝術家是matplotlib.figure.Figure,它包含圖形中的所有內容。 圖形的背景是一個Rectangle,存儲在Figure.patch中。 當你向圖形中添加子圖(add_subplot())和軸域(add_axes())時,這些會附加到Figure.axes。 它們也由創建它們的方法返回:

In [156]: fig = plt.figure()

In [157]: ax1 = fig.add_subplot(211)

In [158]: ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3])

In [159]: ax1
Out[159]: <matplotlib.axes.Subplot instance at 0xd54b26c>

In [160]: print fig.axes
[<matplotlib.axes.Subplot instance at 0xd54b26c>, <matplotlib.axes.Axes instance at 0xd3f0b2c>]

因為圖形維護了『當前軸域』(見figure.gca和圖figure.sca)的概念以支持 pylab/pyplot 狀態機,所以不應直接從軸域列表中插入或刪除軸域,而應使用add_subplot()add_axes()方法進行插入,并使用delaxes()方法進行刪除。 然而,你可以自由地遍歷軸域列表或索引,來訪問要自定義的Axes實例。 下面是一個打開所有軸域網格的示例:

for ax in fig.axes:
    ax.grid(True)

圖形還擁有自己的文本,線條,補丁和圖像,你可以使用它們直接添加基本類型。 圖形的默認坐標系統簡單地以像素(這通常不是你想要的)為單位,但你可以通過設置你添加到圖中的藝術家的transform屬性來控制它。

更有用的是『圖形坐標系』,其中(0,0)是圖的左下角,(1,1)是圖的右上角,你可以通過將Artist的變換設置為fig.transFigure來獲得:

In [191]: fig = plt.figure()

In [192]: l1 = matplotlib.lines.Line2D([0, 1], [0, 1],
           transform=fig.transFigure, figure=fig)

In [193]: l2 = matplotlib.lines.Line2D([0, 1], [1, 0],
           transform=fig.transFigure, figure=fig)

In [194]: fig.lines.extend([l1, l2])

In [195]: fig.canvas.draw()

這里是圖形可以包含的藝術家總結:

圖形屬性 描述
axes Axes實例的列表(包括Subplot
patch Rectangle背景
images FigureImages補丁的列表 - 用于原始像素顯示
legends 圖形Legend實例的列表(不同于Axes.legends
lines 圖形Line2D實例的列表(很少使用,見Axes.lines
patches 圖形補丁列表(很少使用,見Axes.patches
texts 圖形Text實例的列表

軸域容器

matplotlib.axes.Axes是 matplotlib 宇宙的中心 - 它包含絕大多數在一個圖形中使用的藝術家,并帶有許多輔助方法來創建和添加這些藝術家本身,以及訪問和自定義所包含的藝術家的輔助方法。 就像Figure那樣,它包含一個Patch patch,它是一個用于笛卡爾坐標的Rectangle和一個用于極坐標的Cirecle; 這個補丁決定了繪圖區域的形狀,背景和邊框:

ax = fig.add_subplot(111)
rect = ax.patch  # a Rectangle instance
rect.set_facecolor('green')

當調用繪圖方法(例如通常是plot())并傳遞數組或值列表時,該方法將創建一個matplotlib.lines.Line2D()實例,將所有Line2D屬性作為關鍵字參數傳遞, 將該線條添加到Axes.lines容器,并將其返回給你:

In [213]: x, y = np.random.rand(2, 100)

In [214]: line, = ax.plot(x, y, '-', color='blue', linewidth=2)

plot返回一個線條列表,因為你可以傳入多個x,y偶對來繪制,我們將長度為一的列表的第一個元素解構到line變量中。 該線條已添加到Axes.lines列表中:

In [229]: print ax.lines
[<matplotlib.lines.Line2D instance at 0xd378b0c>]

與之類似,創建補丁的方法(如bar())會創建一個矩形列表,將補丁添加到Axes.patches列表中:

In [233]: n, bins, rectangles = ax.hist(np.random.randn(1000), 50, facecolor='yellow')

In [234]: rectangles
Out[234]: <a list of 50 Patch objects>

In [235]: print len(ax.patches)

你不應該直接將對象添加到Axes.linesAxes.patches列表,除非你確切知道你在做什么,因為Axes需要在它創建和添加對象做一些事情。 它設置Artistfigureaxes屬性,以及默認Axes變換(除非設置了變換)。 它還檢查Artist中包含的數據,來更新控制自動縮放的數據結構,以便可以調整視圖限制來包含繪制的數據。 但是,你可以自己創建對象,并使用輔助方法(如add_line()add_patch())將它們直接添加到Axes。 這里是一個注釋的交互式會話,說明正在發生什么:

In [261]: fig = plt.figure()

In [262]: ax = fig.add_subplot(111)

# create a rectangle instance
In [263]: rect = matplotlib.patches.Rectangle( (1,1), width=5, height=12)

# by default the axes instance is None
In [264]: print rect.get_axes()
None

# and the transformation instance is set to the "identity transform"
In [265]: print rect.get_transform()
<Affine object at 0x13695544>

# now we add the Rectangle to the Axes
In [266]: ax.add_patch(rect)

# and notice that the ax.add_patch method has set the axes
# instance
In [267]: print rect.get_axes()
Axes(0.125,0.1;0.775x0.8)

# and the transformation has been set too
In [268]: print rect.get_transform()
<Affine object at 0x15009ca4>

# the default axes transformation is ax.transData
In [269]: print ax.transData
<Affine object at 0x15009ca4>

# notice that the xlimits of the Axes have not been changed
In [270]: print ax.get_xlim()
(0.0, 1.0)

# but the data limits have been updated to encompass the rectangle
In [271]: print ax.dataLim.bounds
(1.0, 1.0, 5.0, 12.0)

# we can manually invoke the auto-scaling machinery
In [272]: ax.autoscale_view()

# and now the xlim are updated to encompass the rectangle
In [273]: print ax.get_xlim()
(1.0, 6.0)

# we have to manually force a figure draw
In [274]: ax.figure.canvas.draw()

有非常多的Axes輔助方法用于創建基本藝術家并將它們添加到他們各自的容器中。 下表總結了他們的一部分,他們創造的Artist的種類,以及他們在哪里存儲它們。

輔助方法 藝術家 容器
ax.annotate - 文本標注 Annotate ax.texts
ax.bar - 條形圖 Rectangle ax.patches
ax.errorbar - 誤差條形圖 Line2DRectangle ax.linesax.patches
ax.fill - 共享區域 Polygon ax.patches
ax.hist - 直方圖 Rectangle ax.patches
ax.imshow - 圖像數據 AxesImage ax.images
ax.legend - 軸域圖例 Legend ax.legends
ax.plot - xy 繪圖 Line2D ax.lines
ax.scatter - 散點圖 PolygonCollection ax.collections
ax.text - 文本 Text ax.texts

除了所有這些藝術家,Axes包含兩個重要的藝術家容器:XAxisYAxis,它們處理刻度和標簽的繪制。 它們被存儲為實例變量xaxisyaxisXAxisYAxis容器將在下面詳細介紹,但請注意,Axes包含許多輔助方法,它們會將調用轉發給Axis實例,因此你通常不需要直接使用它們,除非你愿意。 例如,你可以使用Axes輔助程序方法設置XAxis刻度標簽的字體大小:

for label in ax.get_xticklabels():
    label.set_color('orange')

下面是軸域所包含的藝術家的總結

軸域屬性 描述
artists Artist實例的列表
patch 用于軸域背景的Rectangle實例
collections Collection實例的列表
images AxesImage的列表
legends Legend實例的列表
lines Line2D實例的列表
patches Patch實例的列表
texts Text實例的列表
xaxis matplotlib.axis.XAxis實例
yaxis matplotlib.axis.YAxis實例

軸容器

matplotlib.axis.Axis實例處理刻度線,網格線,刻度標簽和軸標簽的繪制。你可以分別為y軸配置左和右刻度,為x軸分別配置上和下刻度。 Axis還存儲在自動縮放,平移和縮放中使用的數據和視圖間隔,以及LocatorFormatter實例,它們控制刻度位置以及它們表示為字符串的方式。

每個Axis對象都包含一個label屬性(這是 pylab 在調用xlabel()ylabel()時修改的東西)以及主和次刻度的列表。刻度是XTickYTick實例,它包含渲染刻度和刻度標簽的實際線條和文本基本類型。因為刻度是按需動態創建的(例如,當平移和縮放時),你應該通過訪問器方法get_major_ticks()get_minor_ticks()訪問主和次刻度的列表。雖然刻度包含所有下面要提及的基本類型,Axis方法包含訪問器方法來返回刻度線,刻度標簽,刻度位置等:

In [285]: axis = ax.xaxis

In [286]: axis.get_ticklocs()
Out[286]: array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

In [287]: axis.get_ticklabels()
Out[287]: <a list of 10 Text major ticklabel objects>

# note there are twice as many ticklines as labels because by
#  default there are tick lines at the top and bottom but only tick
#  labels below the xaxis; this can be customized
In [288]: axis.get_ticklines()
Out[288]: <a list of 20 Line2D ticklines objects>

# by default you get the major ticks back
In [291]: axis.get_ticklines()
Out[291]: <a list of 20 Line2D ticklines objects>

# but you can also ask for the minor ticks
In [292]: axis.get_ticklines(minor=True)
Out[292]: <a list of 0 Line2D ticklines objects>

下面是Axis的一些有用的訪問器方法的總結(它們擁有相應的setter,如set_major_formatter)。

訪問器方法 描述
get_scale 軸的比例,例如'log''linear'
get_view_interval 軸視圖范圍的內部實例
get_data_interval 軸數據范圍的內部實例
get_gridlines 軸的網格線列表
get_label 軸標簽 - Text實例
get_ticklabels Text實例的列表 - 關鍵字`minor=True False`
get_ticklines Line2D實例的列表 - 關鍵字`minor=True False`
get_ticklocs Tick位置的列表 - 關鍵字`minor=True False`
get_major_locator 用于主刻度的matplotlib.ticker.Locator實例
get_major_formatter 用于主刻度的matplotlib.ticker.Formatter實例
get_minor_locator 用于次刻度的matplotlib.ticker.Locator實例
get_minor_formatter 用于次刻度的matplotlib.ticker.Formatter實例
get_major_ticks 用于主刻度的Tick實例列表
get_minor_ticks 用于次刻度的Tick實例列表
grid 為主或次刻度打開或關閉網格

這里是個例子,出于美觀不太推薦,它自定義了軸域和刻度屬性。

import numpy as np
import matplotlib.pyplot as plt

# plt.figure creates a matplotlib.figure.Figure instance
fig = plt.figure()
rect = fig.patch # a rectangle instance
rect.set_facecolor('lightgoldenrodyellow')

ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4])
rect = ax1.patch
rect.set_facecolor('lightslategray')


for label in ax1.xaxis.get_ticklabels():
    # label is a Text instance
    label.set_color('red')
    label.set_rotation(45)
    label.set_fontsize(16)

for line in ax1.yaxis.get_ticklines():
    # line is a Line2D instance
    line.set_color('green')
    line.set_markersize(25)
    line.set_markeredgewidth(3)

plt.show()

刻度容器

matplotlib.axis.Tick是我們從FigureAxes再到Axis再到Tick的最終的容器對象。Tick包含刻度和網格線的實例,以及上側和下側刻度的標簽實例。 每個都可以直接作為Tick的屬性訪問。此外,也有用于確定上標簽和刻度是否對應x軸,以及右標簽和刻度是否對應y軸的布爾變量。

刻度屬性 描述
tick1line Line2D實例
tick2line Line2D實例
gridline Line2D實例
label1 Text實例
label2 Text實例
gridOn 確定是否繪制刻度線的布爾值
tick1On 確定是否繪制主刻度線的布爾值
tick2On 確定是否繪制次刻度線的布爾值
label1On 確定是否繪制主刻度標簽的布爾值
label2On 確定是否繪制次刻度標簽的布爾值

這里是個例子,使用美元符號設置右側刻度,并在y軸右側將它們設成綠色。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

# Fixing random state for reproducibility
np.random.seed(19680801)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(100*np.random.rand(20))

formatter = ticker.FormatStrFormatter('$%1.2f')
ax.yaxis.set_major_formatter(formatter)

for tick in ax.yaxis.get_major_ticks():
    tick.label1On = False
    tick.label2On = True
    tick.label2.set_color('green')

plt.show()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容