• 标注
    • 基本标注
    • 高级标注
      • 使用框和文本来标注
      • 使用箭头来标注
      • 将艺术家放置在轴域的锚定位置
      • 使用复杂坐标来标注
      • 使用ConnectorPatch
    • 高级话题
      • 轴域之间的缩放效果
      • 定义自定义盒样式

    标注

    原文:Annotation

    译者:飞龙

    协议:CC BY-NC-SA 4.0

    基本标注

    使用text()会将文本放置在轴域的任意位置。 文本的一个常见用例是标注绘图的某些特征,而annotate()方法提供辅助函数,使标注变得容易。 在标注中,有两个要考虑的点:由参数xy表示的标注位置和xytext的文本位置。 这两个参数都是(x, y)元组。

    1. import numpy as np
    2. import matplotlib.pyplot as plt
    3. fig = plt.figure()
    4. ax = fig.add_subplot(111)
    5. t = np.arange(0.0, 5.0, 0.01)
    6. s = np.cos(2*np.pi*t)
    7. line, = ax.plot(t, s, lw=2)
    8. ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
    9. arrowprops=dict(facecolor='black', shrink=0.05),
    10. )
    11. ax.set_ylim(-2,2)
    12. plt.show()

    源代码

    标注 - 图1

    在该示例中,xy(箭头尖端)和xytext位置(文本位置)都以数据坐标为单位。 有多种可以选择的其他坐标系 - 你可以使用xycoordstextcoords以及下列字符串之一(默认为data)指定xyxytext的坐标系。

    | 参数 | 坐标系 |
    | 'figure points' | 距离图形左下角的点数量 |
    | 'figure pixels' | 距离图形左下角的像素数量 |
    | 'figure fraction' | 0,0 是图形左下角,1,1 是右上角 |
    | 'axes points' | 距离轴域左下角的点数量 |
    | 'axes pixels' | 距离轴域左下角的像素数量 |
    | 'axes fraction' | 0,0 是轴域左下角,1,1 是右上角 |
    | 'data' | 使用轴域数据坐标系 |

    例如将文本以轴域小数坐标系来放置,我们可以:

    1. ax.annotate('local max', xy=(3, 1), xycoords='data',
    2. xytext=(0.8, 0.95), textcoords='axes fraction',
    3. arrowprops=dict(facecolor='black', shrink=0.05),
    4. horizontalalignment='right', verticalalignment='top',
    5. )

    对于物理坐标系(点或像素),原点是图形或轴的左下角。

    或者,你可以通过在可选关键字参数arrowprops中提供箭头属性字典来绘制从文本到注释点的箭头。

    arrowprops 描述
    width 箭头宽度,以点为单位
    frac 箭头头部所占据的比例
    headwidth 箭头的底部的宽度,以点为单位
    shrink 移动提示,并使其离注释点和文本一些距离
    **kwargs matplotlib.patches.Polygon的任何键,例如facecolor

    在下面的示例中,xy点是原始坐标(xycoords默认为'data')。 对于极坐标轴,它在(theta, radius)空间中。 此示例中的文本放置在图形小数坐标系中。 matplotlib.text.Text关键字args,例如horizontalalignmentverticalalignmentfontsize,从annotate传给Text实例。

    源代码

    标注 - 图2

    注释(包括花式箭头)的所有高上大的内容的更多信息,请参阅高级标注和pylab_examples示例代码:annotation_demo.py

    不要继续,除非你已经阅读了基本标注,text()annotate()

    高级标注

    使用框和文本来标注

    让我们以一个简单的例子来开始。

    源代码

    标注 - 图3

    pyplot模块(或Axes类的text方法)中的text()函数接受bbox关键字参数,并且在提供时,在文本周围绘制一个框。

    与文本相关联的补丁对象可以通过以下方式访问:

    1. bb = t.get_bbox_patch()

    返回值是FancyBboxPatch的一个实例,并且补丁属性(如facecoloredgewidth等)可以像平常一样访问和修改。 为了更改框的形状,请使用set_boxstyle方法。

    1. 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

    源代码

    标注 - 图4

    注意,属性参数可以在样式名称中用逗号分隔(在初始化文本实例时,此形式可以用作bbox参数的boxstyle的值)。

    1. bb.set_boxstyle("rarrow,pad=0.6")

    使用箭头来标注

    pyplot模块(或Axes类的annotate方法)中的annotate()函数用于绘制连接图上两点的箭头。

    1. ax.annotate("Annotation",
    2. xy=(x1, y1), xycoords='data',
    3. xytext=(x2, y2), textcoords='offset points',
    4. )

    这会使用textcoords中提供的,xytext处的文本标注提供坐标(xycoords)中的xy处的点。 通常,数据坐标中规定了标注点,偏移点中规定了标注文本。 请参阅annotate()了解可用的坐标系。

    连接两个点(xyxytext)的箭头可以通过指定arrowprops参数可选地绘制。 为了仅绘制箭头,请使用空字符串作为第一个参数。

    1. ax.annotate("",
    2. xy=(0.2, 0.2), xycoords='data',
    3. xytext=(0.8, 0.8), textcoords='data',
    4. arrowprops=dict(arrowstyle="->",
    5. connectionstyle="arc3"),
    6. )

    源代码

    标注 - 图5

    箭头的绘制需要几个步骤。

    • 创建两个点之间的连接路径。 这由connectionstyle键值控制。
    • 如果提供了补丁对象(patchApatchB),则会剪切路径以避开该补丁。
    • 路径进一步由提供的像素总量来缩小(shirnkA&shrinkB
    • 路径转换为箭头补丁,由arrowstyle键值控制。

    源代码

    标注 - 图6

    两个点之间的连接路径的创建由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

    注意,angle3arc3中的3意味着所得到的路径是二次样条段(三个控制点)。 如下面将讨论的,当连接路径是二次样条时,可以使用一些箭头样式选项。

    每个连接样式的行为在下面的示例中(有限地)演示。 (警告:条形样式的行为当前未定义好,将来可能会更改)。

    源代码

    标注 - 图7

    然后根据给定的箭头样式将连接路径(在剪切和收缩之后)变换为箭头补丁。

    名称 属性
    - 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

    源代码

    标注 - 图8

    一些箭头仅适用于生成二次样条线段的连接样式。 他们是fancysimplewedge。 对于这些箭头样式,必须使用angle3arc3连接样式。

    如果提供了标注字符串,则patchA默认设置为文本的bbox补丁。

    源代码

    标注 - 图9

    text命令一样,可以使用bbox参数来绘制文本周围的框。

    源代码

    标注 - 图10

    默认情况下,起点设置为文本范围的中心。 可以使用relpos键值进行调整。 这些值根据文本的范围进行归一化。 例如,(0,0)表示左下角,(1,1)表示右上角。

    源代码

    标注 - 图11

    将艺术家放置在轴域的锚定位置

    有一类艺术家可以放置在轴域的锚定位置。 一个常见的例子是图例。 这种类型的艺术家可以使用OffsetBox类创建。 mpl_toolkits.axes_grid.anchored_artists中有几个预定义类。

    1. from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
    2. at = AnchoredText("Figure 1a",
    3. prop=dict(size=8), frameon=True,
    4. loc=2,
    5. )
    6. at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
    7. ax.add_artist(at)

    源代码

    标注 - 图12

    loc关键字与legend命令中含义相同。

    一个简单的应用是当艺术家(或艺术家的集合)的像素大小在创建时已知。 例如,如果要绘制一个固定大小为 20 像素 ×20 像素(半径为 10 像素)的圆,则可以使用AnchoredDrawingArea。 实例使用绘图区域的大小创建(以像素为单位)。 用户可以在绘图区任意添加艺术家。 注意,添加到绘图区域的艺术家的范围与绘制区域本身的位置无关,只和初始大小有关。

    1. from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea
    2. ada = AnchoredDrawingArea(20, 20, 0, 0,
    3. loc=1, pad=0., frameon=False)
    4. p1 = Circle((10, 10), 10)
    5. ada.drawing_area.add_artist(p1)
    6. p2 = Circle((30, 10), 5, fc="r")
    7. ada.drawing_area.add_artist(p2)

    添加到绘图区域的艺术家不应该具有变换集(它们将被重写),并且那些艺术家的尺寸被解释为像素坐标,即,上述示例中的圆的半径分别是 10 像素和 5 像素。

    源代码

    标注 - 图13

    有时,你想让你的艺术家按数据坐标(或其他坐标,而不是画布像素)缩放。 你可以使用AnchoredAuxTransformBox类。 这类似于AnchoredDrawingArea,除了艺术家的范围在绘制时由指定的变换确定。

    1. from mpl_toolkits.axes_grid.anchored_artists import AnchoredAuxTransformBox
    2. box = AnchoredAuxTransformBox(ax.transData, loc=2)
    3. el = Ellipse((0,0), width=0.1, height=0.4, angle=30) # in data coordinates!
    4. box.drawing_area.add_artist(el)

    上述示例中的椭圆具有在数据坐标中对应于 0.1 和 0.4 的宽度和高度,并且当轴域的视图限制改变时将自动缩放。

    源代码

    标注 - 图14

    如图例所示,可以设置bbox_to_anchor参数。 使用HPackerVPacker,你可以像图例中一样排列艺术家(事实上,这是图例的创建方式)。

    源代码

    标注 - 图15

    请注意,与图例不同,默认情况下,bbox_transform设置为IdentityTransform

    使用复杂坐标来标注

    matplotlib 中的标注支持标注文本中描述的几种类型的坐标。 对于想要更多控制的高级用户,它支持几个其他选项。

    1. Transform实例,例如:

      1. ax.annotate("Test", xy=(0.5, 0.5), xycoords=ax.transAxes)

      相当于:

      1. ax.annotate("Test", xy=(0.5, 0.5), xycoords="axes fraction")

      使用它,你可以在其他轴域内标注一个点:

      1. ax1, ax2 = subplot(121), subplot(122)
      2. ax2.annotate("Test", xy=(0.5, 0.5), xycoords=ax1.transData,
      3. xytext=(0.5, 0.5), textcoords=ax2.transData,
      4. arrowprops=dict(arrowstyle="->"))
    2. Artist实例。xy值(或xytext)被解释为艺术家的bboxget_window_extent的返回值)的小数坐标。

      1. an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
      2. va="center", ha="center",
      3. bbox=dict(boxstyle="round", fc="w"))
      4. an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1, # (1,0.5) of the an1's bbox
      5. xytext=(30,0), textcoords="offset points",
      6. va="center", ha="left",
      7. bbox=dict(boxstyle="round", fc="w"),
      8. arrowprops=dict(arrowstyle="->"))

      源代码

      标注 - 图16

      请注意,你的责任是在绘制an2之前确定坐标艺术家(上例中的an1)的范围。 在大多数情况下,这意味着an2需要晚于an1

    3. 一个返回BboxBaseTransform的实例的可调用对象。 如果返回一个变换,它与 1 相同,如果返回bbox,它与 2 相同。可调用对象应该接受renderer实例的单个参数。 例如,以下两个命令产生相同的结果:

      1. an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1,
      2. xytext=(30,0), textcoords="offset points")
      3. an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1.get_window_extent,
      4. xytext=(30,0), textcoords="offset points")
    4. 指定二元坐标的元组。 第一项用于x坐标,第二项用于y坐标。 例如,

      1. annotate("Test", xy=(0.5, 1), xycoords=("data", "axes fraction"))

      0.5 的单位是数据坐标,1 的单位是归一化轴域坐标。 你可以像使用元组一样使用艺术家或变换。 例如,

      1. import matplotlib.pyplot as plt
      2. plt.figure(figsize=(3,2))
      3. ax=plt.axes([0.1, 0.1, 0.8, 0.7])
      4. an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
      5. va="center", ha="center",
      6. bbox=dict(boxstyle="round", fc="w"))
      7. an2 = ax.annotate("Test 2", xy=(0.5, 1.), xycoords=an1,
      8. xytext=(0.5,1.1), textcoords=(an1, "axes fraction"),
      9. va="bottom", ha="center",
      10. bbox=dict(boxstyle="round", fc="w"),
      11. arrowprops=dict(arrowstyle="->"))
      12. plt.show()

      源代码

      标注 - 图17

    5. 有时,您希望您的注释带有一些“偏移点”,不是距离注释点,而是距离某些其他点。 OffsetFrom是这种情况下的辅助类。

      1. import matplotlib.pyplot as plt
      2. plt.figure(figsize=(3,2))
      3. ax=plt.axes([0.1, 0.1, 0.8, 0.7])
      4. an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
      5. va="center", ha="center",
      6. bbox=dict(boxstyle="round", fc="w"))
      7. from matplotlib.text import OffsetFrom
      8. offset_from = OffsetFrom(an1, (0.5, 0))
      9. an2 = ax.annotate("Test 2", xy=(0.1, 0.1), xycoords="data",
      10. xytext=(0, -10), textcoords=offset_from,
      11. # xytext is offset points from "xy=(0.5, 0), xycoords=an1"
      12. va="top", ha="center",
      13. bbox=dict(boxstyle="round", fc="w"),
      14. arrowprops=dict(arrowstyle="->"))
      15. plt.show()

      标注 - 图18

      你可以参考这个链接:pylab_examples example code: annotation_demo3.py.

    使用ConnectorPatch

    ConnectorPatch类似于没有文本的标注。 虽然在大多数情况下建议使用标注函数,但是当您想在不同的轴上连接点时,ConnectorPatch很有用。

    1. from matplotlib.patches import ConnectionPatch
    2. xy = (0.2, 0.2)
    3. con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data",
    4. axesA=ax1, axesB=ax2)
    5. ax2.add_artist(con)

    上述代码连接了ax1中数据坐标的xy点,与ax2中数据坐标的xy点。这是个简单的例子。

    源代码

    标注 - 图19

    虽然ConnectorPatch实例可以添加到任何轴,但您可能需要将其添加到绘图顺序中最新的轴,以防止与其他轴重叠。

    高级话题

    轴域之间的缩放效果

    mpl_toolkits.axes_grid.inset_locator定义了一些补丁类,用于互连两个轴域。 理解代码需要一些 mpl 转换如何工作的知识。 但是,利用它的方式很直接。

    源代码

    标注 - 图20

    定义自定义盒样式

    你可以使用自定义盒样式,boxstyle的值可以为如下形式的可调用对象:

    1. def __call__(self, x0, y0, width, height, mutation_size,
    2. aspect_ratio=1.):
    3. """
    4. Given the location and size of the box, return the path of
    5. the box around it.
    6. - *x0*, *y0*, *width*, *height* : location and size of the box
    7. - *mutation_size* : a reference scale for the mutation.
    8. - *aspect_ratio* : aspect-ratio for the mutation.
    9. """
    10. path = ...
    11. return path

    这里是个复杂的例子:

    源代码

    标注 - 图21

    但是,推荐你从matplotlib.patches.BoxStyle._Base派生,像这样:

    1. from matplotlib.path import Path
    2. from matplotlib.patches import BoxStyle
    3. import matplotlib.pyplot as plt
    4. # we may derive from matplotlib.patches.BoxStyle._Base class.
    5. # You need to override transmute method in this case.
    6. class MyStyle(BoxStyle._Base):
    7. """
    8. A simple box.
    9. """
    10. def __init__(self, pad=0.3):
    11. """
    12. The arguments need to be floating numbers and need to have
    13. default values.
    14. *pad*
    15. amount of padding
    16. """
    17. self.pad = pad
    18. super(MyStyle, self).__init__()
    19. def transmute(self, x0, y0, width, height, mutation_size):
    20. """
    21. Given the location and size of the box, return the path of
    22. the box around it.
    23. - *x0*, *y0*, *width*, *height* : location and size of the box
    24. - *mutation_size* : a reference scale for the mutation.
    25. Often, the *mutation_size* is the font size of the text.
    26. You don't need to worry about the rotation as it is
    27. automatically taken care of.
    28. """
    29. # padding
    30. pad = mutation_size * self.pad
    31. # width and height with padding added.
    32. width, height = width + 2.*pad, \
    33. height + 2.*pad,
    34. # boundary of the padded box
    35. x0, y0 = x0-pad, y0-pad,
    36. x1, y1 = x0+width, y0 + height
    37. cp = [(x0, y0),
    38. (x1, y0), (x1, y1), (x0, y1),
    39. (x0-pad, (y0+y1)/2.), (x0, y0),
    40. (x0, y0)]
    41. com = [Path.MOVETO,
    42. Path.LINETO, Path.LINETO, Path.LINETO,
    43. Path.LINETO, Path.LINETO,
    44. Path.CLOSEPOLY]
    45. path = Path(cp, com)
    46. return path
    47. # register the custom style
    48. BoxStyle._style_list["angled"] = MyStyle
    49. plt.figure(1, figsize=(3,3))
    50. ax = plt.subplot(111)
    51. ax.text(0.5, 0.5, "Test", size=30, va="center", ha="center", rotation=30,
    52. bbox=dict(boxstyle="angled,pad=0.5", alpha=0.2))
    53. del BoxStyle._style_list["angled"]
    54. plt.show()

    源代码

    标注 - 图22

    与之类似,您可以定义一个自定义的ConnectionStyle和一个自定义的ArrowStyle。 请参阅lib/matplotlib/patches.py的源代码,并查看每个样式类是如何定义的。