藝術家教程
譯者:飛龍
matplotlib API 有三個層級。 matplotlib.backend_bases.FigureCanvas
是繪制圖形的區域,matplotlib.backend_bases.Renderer
是知道如何在ChartCanvas
上繪制的對象,而matplotlib.artist.Artist
是知道如何使用渲染器在畫布上畫圖的對象。 FigureCanvas
和Renderer
處理與用戶界面工具包(如 wxPython)或 PostScript? 等繪圖語言交互的所有細節,Artist
處理所有高級結構,如表示和布局圖形,文本和線條。用戶通常要花費95%的時間來處理藝術家。
有兩種類型的藝術家:基本類型和容器類型。基本類型表示我們想要繪制到畫布上的標準圖形對象:Line2D
,Rectangle
,Text
,AxesImage
等,容器是放置它們的位置(Axis
,Axes
和Figure
)。標準用法是創建一個Figure
實例,使用Figure
創建一個或多個Axes
或Subplot
實例,并使用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()
)來創建最常見的圖形基本類型 Line2D
,Text
,Rectangle
,Image
)。 這些輔助方法將獲取你的數據(例如 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
時,它將信息傳遞給XAxis
的Text
實例,每個Axes
實例都包含XAxis
和YAxis
,它們處理刻度、刻度標簽和軸標簽的布局和繪制。
嘗試創建下面的圖形:
自定義你的對象
圖中的每個元素都由一個 matplotlib 藝術家表示,每個元素都有一個擴展屬性列表用于配置它的外觀。 圖形本身包含一個Rectangle
,正好是圖形的大小,你可以使用它來設置圖形的背景顏色和透明度。 同樣,每個Axes
邊框(在通常的 matplotlib 繪圖中是標準的白底黑邊)擁有一個Rectangle
實例,用于確定軸域的顏色,透明度和其他屬性,這些實例存儲為成員變量Figure.patch
和Axes.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 透明度) |
每個屬性都使用一個老式的setter
或getter
(是的,我們知道這會刺激 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
派生的類,例如Figure
和Rectangle
。 這里是上面提到的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.lines
或Axes.patches
列表,除非你確切知道你在做什么,因為Axes
需要在它創建和添加對象做一些事情。 它設置Artist
的figure
和axes
屬性,以及默認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 - 誤差條形圖 |
Line2D 和 Rectangle
|
ax.lines 和 ax.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
包含兩個重要的藝術家容器:XAxis
和YAxis
,它們處理刻度和標簽的繪制。 它們被存儲為實例變量xaxis
和yaxis
。 XAxis
和YAxis
容器將在下面詳細介紹,但請注意,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
還存儲在自動縮放,平移和縮放中使用的數據和視圖間隔,以及Locator
和Formatter
實例,它們控制刻度位置以及它們表示為字符串的方式。
每個Axis
對象都包含一個label
屬性(這是 pylab 在調用xlabel()
和ylabel()
時修改的東西)以及主和次刻度的列表。刻度是XTick
和YTick
實例,它包含渲染刻度和刻度標簽的實際線條和文本基本類型。因為刻度是按需動態創建的(例如,當平移和縮放時),你應該通過訪問器方法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
是我們從Figure
到Axes
再到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()