藝術家教程
原文: Artist tutorial 譯者: 飛龍 協定: CC BY-NC-SA 4.0
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()
這裡是圖形可以包含的藝術家總結:
圖形屬性 | |
---|---|
| 執行個體的清單(包括 ) |
| 背景 |
| 更新檔的清單 - 用于原始像素顯示 |
| 圖形 執行個體的清單(不同于 |
| 執行個體的清單(很少使用,見 |
| 圖形更新檔清單(很少使用,見 |
| 執行個體的清單 |
軸域容器
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
的種類,以及他們在哪裡存儲它們。
輔助方法 | 藝術家 | 容器 |
---|---|---|
- 文本标注 | | |
- 條形圖 | | |
- 誤差條形圖 | | |
- 共享區域 | | |
- 直方圖 | | |
- 圖像資料 | | |
- 軸域圖例 | | |
- xy 繪圖 | | |
- 散點圖 | | |
- 文本 | | |
除了所有這些藝術家,
Axes
包含兩個重要的藝術家容器:
XAxis
YAxis
,它們處理刻度和标簽的繪制。 它們被存儲為執行個體變量
xaxis
yaxis
。
XAxis
YAxis
容器将在下面詳細介紹,但請注意,
Axes
包含許多輔助方法,它們會将調用轉發給
Axis
執行個體,是以你通常不需要直接使用它們,除非你願意。 例如,你可以使用
Axes
輔助程式方法設定
XAxis
刻度标簽的字型大小:
for label in ax.get_xticklabels():
label.set_color('orange')
下面是軸域所包含的藝術家的總結
軸域屬性 | |
---|---|
| |
| 用于軸域背景的 執行個體 |
| |
| 的清單 |
| |
| |
| |
| |
| |
| |
軸容器
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 | 軸的比例,例如 |
get_view_interval | 軸視圖範圍的内部執行個體 |
get_data_interval | 軸資料範圍的内部執行個體 |
get_gridlines | 軸的網格線清單 |
get_label | 軸标簽 - |
get_ticklabels | 執行個體的清單 - 關鍵字`minor=True |
get_ticklines | |
get_ticklocs | 位置的清單 - 關鍵字`minor=True |
get_major_locator | 用于主刻度的 |
get_major_formatter | |
get_minor_locator | 用于次刻度的 |
get_minor_formatter | |
get_major_ticks | 執行個體清單 |
get_minor_ticks | |
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
軸的布爾變量。
刻度屬性 | |
---|---|
| |
| |
| |
| |
| |
| 确定是否繪制刻度線的布爾值 |
| 确定是否繪制主刻度線的布爾值 |
| 确定是否繪制次刻度線的布爾值 |
| 确定是否繪制主刻度标簽的布爾值 |
| 确定是否繪制次刻度标簽的布爾值 |
這裡是個例子,使用美元符号設定右側刻度,并在
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()