# 標注
> 原文:[Annotation](http://matplotlib.org/users/annotations.html)
> 譯者:[飛龍](https://github.com/)
> 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
## 基本標注
使用`text()`會將文本放置在軸域的任意位置。 文本的一個常見用例是標注繪圖的某些特征,而`annotate()`方法提供輔助函數,使標注變得容易。 在標注中,有兩個要考慮的點:由參數`xy`表示的標注位置和`xytext`的文本位置。 這兩個參數都是`(x, y)`元組。
```py
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = ax.plot(t, s, lw=2)
ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),
)
ax.set_ylim(-2,2)
plt.show()
```
[源代碼](http://matplotlib.org/mpl_examples/pyplots/annotation_basic.py)

在該示例中,`xy`(箭頭尖端)和`xytext`位置(文本位置)都以數據坐標為單位。 有多種可以選擇的其他坐標系 - 你可以使用`xycoords`和`textcoords`以及下列字符串之一(默認為`data`)指定`xy`和`xytext`的坐標系。
| 參數 | 坐標系 |
| `'figure points'` | 距離圖形左下角的點數量 |
| `'figure pixels'` | 距離圖形左下角的像素數量 |
| `'figure fraction'` | 0,0 是圖形左下角,1,1 是右上角 |
| `'axes points'` | 距離軸域左下角的點數量 |
| `'axes pixels'` | 距離軸域左下角的像素數量 |
| `'axes fraction'` | 0,0 是軸域左下角,1,1 是右上角 |
| `'data'` | 使用軸域數據坐標系 |
例如將文本以軸域小數坐標系來放置,我們可以:
```py
ax.annotate('local max', xy=(3, 1), xycoords='data',
xytext=(0.8, 0.95), textcoords='axes fraction',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='right', verticalalignment='top',
)
```
對于物理坐標系(點或像素),原點是圖形或軸的左下角。
或者,你可以通過在可選關鍵字參數`arrowprops`中提供箭頭屬性字典來繪制從文本到注釋點的箭頭。
| `arrowprops`鍵 | 描述 |
| --- | --- |
| `width` | 箭頭寬度,以點為單位 |
| `frac` | 箭頭頭部所占據的比例 |
| `headwidth` | 箭頭的底部的寬度,以點為單位 |
| `shrink` | 移動提示,并使其離注釋點和文本一些距離 |
| `**kwargs` | `matplotlib.patches.Polygon`的任何鍵,例如`facecolor` |
在下面的示例中,`xy`點是原始坐標(`xycoords`默認為`'data'`)。 對于極坐標軸,它在`(theta, radius)`空間中。 此示例中的文本放置在圖形小數坐標系中。 `matplotlib.text.Text`關鍵字`args`,例如`horizontalalignment`,`verticalalignment`和`fontsize`,從`annotate`傳給`Text`實例。
[源代碼](http://matplotlib.org/mpl_examples/pyplots/annotation_polar.py)

注釋(包括花式箭頭)的所有高上大的內容的更多信息,請參閱[高級標注](http://matplotlib.org/users/annotations.html#plotting-guide-annotation)和[`pylab_examples`示例代碼:`annotation_demo.py`](http://matplotlib.org/examples/pylab_examples/annotation_demo.html#pylab-examples-annotation-demo)。
不要繼續,除非你已經閱讀了[基本標注](http://matplotlib.org/users/annotations.html#annotations-tutorial),[`text()`](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.text)和[`annotate()`](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.annotate)。
## 高級標注
### 使用框和文本來標注
讓我們以一個簡單的例子來開始。
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_text_arrow.py)

在`pyplot`模塊(或`Axes`類的`text`方法)中的`text()`函數接受`bbox`關鍵字參數,并且在提供時,在文本周圍繪制一個框。
與文本相關聯的補丁對象可以通過以下方式訪問:
```py
bb = t.get_bbox_patch()
```
返回值是`FancyBboxPatch`的一個實例,并且補丁屬性(如`facecolor`,`edgewidth`等)可以像平常一樣訪問和修改。 為了更改框的形狀,請使用`set_boxstyle`方法。
```py
bb.set_boxstyle("rarrow", pad=0.6)
```
該參數是框樣式的名稱與其作為關鍵字參數的屬性。 目前,實現了以下框樣式。
| 類 | 名稱 | 屬性 |
| --- | --- |
| Circle | `circle` | `pad=0.3` |
| DArrow | `darrow` | `pad=0.3` |
| LArrow | `larrow` | `pad=0.3` |
| RArrow | `rarrow` | `pad=0.3` |
| Round | `round` | `pad=0.3,rounding_size=None` |
| Round4 | `round4` | `pad=0.3,rounding_size=None` |
| Roundtooth | `roundtooth` | `pad=0.3,tooth_size=None` |
| Sawtooth | `sawtooth` | `pad=0.3,tooth_size=None` |
| Square | `square` | `pad=0.3` |
[源代碼](http://matplotlib.org/mpl_examples/pylab_examples/fancybox_demo2.py)

注意,屬性參數可以在樣式名稱中用逗號分隔(在初始化文本實例時,此形式可以用作`bbox`參數的`boxstyle`的值)。
```py
bb.set_boxstyle("rarrow,pad=0.6")
```
### 使用箭頭來標注
`pyplot`模塊(或`Axes`類的`annotate`方法)中的`annotate()`函數用于繪制連接圖上兩點的箭頭。
```py
ax.annotate("Annotation",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='offset points',
)
```
這會使用`textcoords`中提供的,`xytext`處的文本標注提供坐標(`xycoords`)中的`xy`處的點。 通常,數據坐標中規定了標注點,偏移點中規定了標注文本。 請參閱`annotate()`了解可用的坐標系。
連接兩個點(`xy`和`xytext`)的箭頭可以通過指定`arrowprops`參數可選地繪制。 為了僅繪制箭頭,請使用空字符串作為第一個參數。
```py
ax.annotate("",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3"),
)
```
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple01.py)

箭頭的繪制需要幾個步驟。
+ 創建兩個點之間的連接路徑。 這由`connectionstyle`鍵值控制。
+ 如果提供了補丁對象(`patchA`和`patchB`),則會剪切路徑以避開該補丁。
+ 路徑進一步由提供的像素總量來縮小(`shirnkA`&`shrinkB`)
+ 路徑轉換為箭頭補丁,由`arrowstyle`鍵值控制。
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_explain.py)

兩個點之間的連接路徑的創建由`connectionstyle `鍵控制,并且可用以下樣式。
| 名稱 | 屬性 |
| --- | --- |
| angle | `angleA=90,angleB=0,rad=0.0` |
| angle3 | `angleA=90,angleB=0` |
| arc | `angleA=0,angleB=0,armA=None,armB=None,rad=0.0` |
| arc3 | `rad=0.0` |
| bar | `armA=0.0,armB=0.0,fraction=0.3,angle=None` |
注意,`angle3`和`arc3`中的`3`意味著所得到的路徑是二次樣條段(三個控制點)。 如下面將討論的,當連接路徑是二次樣條時,可以使用一些箭頭樣式選項。
每個連接樣式的行為在下面的示例中(有限地)演示。 (警告:條形樣式的行為當前未定義好,將來可能會更改)。
[源代碼](http://matplotlib.org/users/plotting/examples/connectionstyle_demo.py)

然后根據給定的箭頭樣式將連接路徑(在剪切和收縮之后)變換為箭頭補丁。
| 名稱 | 屬性 |
| --- | --- |
| - | `None` |
| -> | `head_length=0.4,head_width=0.2` |
| -[ | `widthB=1.0,lengthB=0.2,angleB=None` |
| |-| | `widthA=1.0,widthB=1.0` |
| -|> | `head_length=0.4,head_width=0.2` |
| <- | `head_length=0.4,head_width=0.2` |
| <-> | `head_length=0.4,head_width=0.2` |
| <|- | `head_length=0.4,head_width=0.2` |
| <|-|> | `head_length=0.4,head_width=0.2` |
| fancy | `head_length=0.4,head_width=0.4,tail_width=0.4` |
| simple | `head_length=0.5,head_width=0.5,tail_width=0.2` |
| wedge | `tail_width=0.3,shrink_factor=0.5` |
[源代碼](http://matplotlib.org/mpl_examples/pylab_examples/fancyarrow_demo.py)

一些箭頭僅適用于生成二次樣條線段的連接樣式。 他們是`fancy`,`simple`,`wedge`。 對于這些箭頭樣式,必須使用`angle3`或`arc3`連接樣式。
如果提供了標注字符串,則`patchA`默認設置為文本的`bbox`補丁。
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple02.py)

與`text`命令一樣,可以使用`bbox`參數來繪制文本周圍的框。
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple03.py)

默認情況下,起點設置為文本范圍的中心。 可以使用`relpos`鍵值進行調整。 這些值根據文本的范圍進行歸一化。 例如,`(0,0)`表示左下角,`(1,1)`表示右上角。
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple04.py)

### 將藝術家放置在軸域的錨定位置
有一類藝術家可以放置在軸域的錨定位置。 一個常見的例子是圖例。 這種類型的藝術家可以使用`OffsetBox`類創建。 `mpl_toolkits.axes_grid.anchored_artists`中有幾個預定義類。
```py
from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
at = AnchoredText("Figure 1a",
prop=dict(size=8), frameon=True,
loc=2,
)
at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(at)
```
[源代碼](http://matplotlib.org/users/plotting/examples/anchored_box01.py)

`loc`關鍵字與`legend`命令中含義相同。
一個簡單的應用是當藝術家(或藝術家的集合)的像素大小在創建時已知。 例如,如果要繪制一個固定大小為 20 像素 ×20 像素(半徑為 10 像素)的圓,則可以使用`AnchoredDrawingArea`。 實例使用繪圖區域的大小創建(以像素為單位)。 用戶可以在繪圖區任意添加藝術家。 注意,添加到繪圖區域的藝術家的范圍與繪制區域本身的位置無關,只和初始大小有關。
```py
from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea
ada = AnchoredDrawingArea(20, 20, 0, 0,
loc=1, pad=0., frameon=False)
p1 = Circle((10, 10), 10)
ada.drawing_area.add_artist(p1)
p2 = Circle((30, 10), 5, fc="r")
ada.drawing_area.add_artist(p2)
```
添加到繪圖區域的藝術家不應該具有變換集(它們將被重寫),并且那些藝術家的尺寸被解釋為像素坐標,即,上述示例中的圓的半徑分別是 10 像素和 5 像素。
[源代碼](http://matplotlib.org/users/plotting/examples/anchored_box02.py)

有時,你想讓你的藝術家按數據坐標(或其他坐標,而不是畫布像素)縮放。 你可以使用`AnchoredAuxTransformBox`類。 這類似于`AnchoredDrawingArea`,除了藝術家的范圍在繪制時由指定的變換確定。
```py
from mpl_toolkits.axes_grid.anchored_artists import AnchoredAuxTransformBox
box = AnchoredAuxTransformBox(ax.transData, loc=2)
el = Ellipse((0,0), width=0.1, height=0.4, angle=30) # in data coordinates!
box.drawing_area.add_artist(el)
```
上述示例中的橢圓具有在數據坐標中對應于 0.1 和 0.4 的寬度和高度,并且當軸域的視圖限制改變時將自動縮放。
[源代碼](http://matplotlib.org/users/plotting/examples/anchored_box03.py)

如圖例所示,可以設置`bbox_to_anchor`參數。 使用`HPacker`和`VPacker`,你可以像圖例中一樣排列藝術家(事實上,這是圖例的創建方式)。
[源代碼](http://matplotlib.org/users/plotting/examples/anchored_box04.py)

請注意,與圖例不同,默認情況下,`bbox_transform`設置為`IdentityTransform`。
### 使用復雜坐標來標注
matplotlib 中的標注支持[標注文本](http://matplotlib.org/users/annotations_intro.html#annotations-tutorial)中描述的幾種類型的坐標。 對于想要更多控制的高級用戶,它支持幾個其他選項。
1. `Transform`實例,例如:
```py
ax.annotate("Test", xy=(0.5, 0.5), xycoords=ax.transAxes)
```
相當于:
```py
ax.annotate("Test", xy=(0.5, 0.5), xycoords="axes fraction")
```
使用它,你可以在其他軸域內標注一個點:
```py
ax1, ax2 = subplot(121), subplot(122)
ax2.annotate("Test", xy=(0.5, 0.5), xycoords=ax1.transData,
xytext=(0.5, 0.5), textcoords=ax2.transData,
arrowprops=dict(arrowstyle="->"))
```
2. `Artist`實例。`xy`值(或`xytext`)被解釋為藝術家的`bbox`(`get_window_extent`的返回值)的小數坐標。
```py
an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
va="center", ha="center",
bbox=dict(boxstyle="round", fc="w"))
an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1, # (1,0.5) of the an1's bbox
xytext=(30,0), textcoords="offset points",
va="center", ha="left",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
```
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple_coord01.py)

請注意,你的責任是在繪制`an2`之前確定坐標藝術家(上例中的`an1`)的范圍。 在大多數情況下,這意味著`an2`需要晚于`an1`。
3. 一個返回`BboxBase`或`Transform`的實例的可調用對象。 如果返回一個變換,它與 1 相同,如果返回`bbox`,它與 2 相同。可調用對象應該接受`renderer`實例的單個參數。 例如,以下兩個命令產生相同的結果:
```py
an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1,
xytext=(30,0), textcoords="offset points")
an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1.get_window_extent,
xytext=(30,0), textcoords="offset points")
```
4. 指定二元坐標的元組。 第一項用于`x`坐標,第二項用于`y`坐標。 例如,
```py
annotate("Test", xy=(0.5, 1), xycoords=("data", "axes fraction"))
```
0.5 的單位是數據坐標,1 的單位是歸一化軸域坐標。 你可以像使用元組一樣使用藝術家或變換。 例如,
```py
import matplotlib.pyplot as plt
plt.figure(figsize=(3,2))
ax=plt.axes([0.1, 0.1, 0.8, 0.7])
an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
va="center", ha="center",
bbox=dict(boxstyle="round", fc="w"))
an2 = ax.annotate("Test 2", xy=(0.5, 1.), xycoords=an1,
xytext=(0.5,1.1), textcoords=(an1, "axes fraction"),
va="bottom", ha="center",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
plt.show()
```
[源代碼](http://matplotlib.org/users/plotting/examples/annotate_simple_coord02.py)

5. 有時,您希望您的注釋帶有一些“偏移點”,不是距離注釋點,而是距離某些其他點。 `OffsetFrom`是這種情況下的輔助類。
```py
import matplotlib.pyplot as plt
plt.figure(figsize=(3,2))
ax=plt.axes([0.1, 0.1, 0.8, 0.7])
an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
va="center", ha="center",
bbox=dict(boxstyle="round", fc="w"))
from matplotlib.text import OffsetFrom
offset_from = OffsetFrom(an1, (0.5, 0))
an2 = ax.annotate("Test 2", xy=(0.1, 0.1), xycoords="data",
xytext=(0, -10), textcoords=offset_from,
# xytext is offset points from "xy=(0.5, 0), xycoords=an1"
va="top", ha="center",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
plt.show()
```

你可以參考這個鏈接:[`pylab_examples example code: annotation_demo3.py.`](http://matplotlib.org/examples/pylab_examples/annotation_demo3.html#pylab-examples-annotation-demo3)。
### 使用`ConnectorPatch`
`ConnectorPatch`類似于沒有文本的標注。 雖然在大多數情況下建議使用標注函數,但是當您想在不同的軸上連接點時,`ConnectorPatch`很有用。
```py
from matplotlib.patches import ConnectionPatch
xy = (0.2, 0.2)
con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data",
axesA=ax1, axesB=ax2)
ax2.add_artist(con)
```
上述代碼連接了`ax1`中數據坐標的`xy`點,與`ax2`中數據坐標的`xy`點。這是個簡單的例子。
[源代碼](http://matplotlib.org/users/plotting/examples/connect_simple01.py)

雖然`ConnectorPatch`實例可以添加到任何軸,但您可能需要將其添加到繪圖順序中最新的軸,以防止與其他軸重疊。
## 高級話題
### 軸域之間的縮放效果
`mpl_toolkits.axes_grid.inset_locator`定義了一些補丁類,用于互連兩個軸域。 理解代碼需要一些 mpl 轉換如何工作的知識。 但是,利用它的方式很直接。
[源代碼](http://matplotlib.org/mpl_examples/pylab_examples/axes_zoom_effect.py)

### 定義自定義盒樣式
你可以使用自定義盒樣式,`boxstyle`的值可以為如下形式的可調用對象:
```py
def __call__(self, x0, y0, width, height, mutation_size,
aspect_ratio=1.):
"""
Given the location and size of the box, return the path of
the box around it.
- *x0*, *y0*, *width*, *height* : location and size of the box
- *mutation_size* : a reference scale for the mutation.
- *aspect_ratio* : aspect-ratio for the mutation.
"""
path = ...
return path
```
這里是個復雜的例子:
[源代碼](http://matplotlib.org/users/plotting/examples/custom_boxstyle01.py)

但是,推薦你從`matplotlib.patches.BoxStyle._Base`派生,像這樣:
```py
from matplotlib.path import Path
from matplotlib.patches import BoxStyle
import matplotlib.pyplot as plt
# we may derive from matplotlib.patches.BoxStyle._Base class.
# You need to override transmute method in this case.
class MyStyle(BoxStyle._Base):
"""
A simple box.
"""
def __init__(self, pad=0.3):
"""
The arguments need to be floating numbers and need to have
default values.
*pad*
amount of padding
"""
self.pad = pad
super(MyStyle, self).__init__()
def transmute(self, x0, y0, width, height, mutation_size):
"""
Given the location and size of the box, return the path of
the box around it.
- *x0*, *y0*, *width*, *height* : location and size of the box
- *mutation_size* : a reference scale for the mutation.
Often, the *mutation_size* is the font size of the text.
You don't need to worry about the rotation as it is
automatically taken care of.
"""
# padding
pad = mutation_size * self.pad
# width and height with padding added.
width, height = width + 2.*pad, \
height + 2.*pad,
# boundary of the padded box
x0, y0 = x0-pad, y0-pad,
x1, y1 = x0+width, y0 + height
cp = [(x0, y0),
(x1, y0), (x1, y1), (x0, y1),
(x0-pad, (y0+y1)/2.), (x0, y0),
(x0, y0)]
com = [Path.MOVETO,
Path.LINETO, Path.LINETO, Path.LINETO,
Path.LINETO, Path.LINETO,
Path.CLOSEPOLY]
path = Path(cp, com)
return path
# register the custom style
BoxStyle._style_list["angled"] = MyStyle
plt.figure(1, figsize=(3,3))
ax = plt.subplot(111)
ax.text(0.5, 0.5, "Test", size=30, va="center", ha="center", rotation=30,
bbox=dict(boxstyle="angled,pad=0.5", alpha=0.2))
del BoxStyle._style_list["angled"]
plt.show()
```
[源代碼](http://matplotlib.org/users/plotting/examples/custom_boxstyle02.py)

與之類似,您可以定義一個自定義的`ConnectionStyle`和一個自定義的`ArrowStyle`。 請參閱`lib/matplotlib/patches.py`的源代碼,并查看每個樣式類是如何定義的。