数据科学 IPython 笔记本 8.12 文本和注解

8.12 文本和注解

原文:Text and Annotation

译者:飞龙

协议:CC BY-NC-SA 4.0

本节是《Python 数据科学手册》(Python Data Science Handbook)的摘录。

创建良好的可视化涉及引导读者并使图形讲述故事。在某些情况下,可以以完全可视的方式讲述这个故事,而不需要添加文本,但在其他情况下,需要小的文本提示和标签。也许你将使用的最基本的注释类型是轴标签和标题,但选项超出了这个范围。让我们看看一些数据,以及我们如何可视化和注释它,来有助于传达有趣的信息。 我们首先设置笔记本来绘图并导入我们将使用的函数:

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
plt.style.use('seaborn-whitegrid')
import numpy as np
import pandas as pd

示例: 美国新生儿的假期效应

让我们回到之前处理的一些数据,在“示例:出生率数据”中,我们在日历年上生成了平均出生率的图表;如前所述,这些数据可以在 https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv 下载。

我们将使用我们在那里使用的相同清理过程开始,并绘制结果:

births = pd.read_csv('data/births.csv')

quartiles = np.percentile(births['births'], [25, 50, 75])
mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])
births = births.query('(births > @mu - 5 * @sig) & (births < @mu + 5 * @sig)')

births['day'] = births['day'].astype(int)

births.index = pd.to_datetime(10000 * births.year +
                              100 * births.month +
                              births.day, format='%Y%m%d')
births_by_date = births.pivot_table('births',
                                    [births.index.month, births.index.day])
births_by_date.index = [pd.datetime(2012, month, day)
                        for (month, day) in births_by_date.index]

fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax);
png

When we're communicating data like this, it is often useful to annotate certain features of the plot to draw the reader's attention.
This can be done manually with the plt.text/ax.text command, which will place text at a particular x/y value:

fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)

# 向绘图添加标签
style = dict(size=10, color='gray')

ax.text('2012-1-1', 3950, "New Year's Day", **style)
ax.text('2012-7-4', 4250, "Independence Day", ha='center', **style)
ax.text('2012-9-4', 4850, "Labor Day", ha='center', **style)
ax.text('2012-10-31', 4600, "Halloween", ha='right', **style)
ax.text('2012-11-25', 4450, "Thanksgiving", ha='center', **style)
ax.text('2012-12-25', 3850, "Christmas ", ha='right', **style)

# 标记轴域
ax.set(title='USA births by day of year (1969-1988)',
       ylabel='average daily births')

# 使用中心化的月标签将 x 轴格式化
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));
png

ax.text方法接受x位置,y位置,字符串,然后是可选关键字,指定文本的颜色,大小,样式,对齐方式和其他属性。在这里,我们使用ha='right'ha='center',其中ha是 horizonal alignment 的缩写。可用选项的更多信息,请参阅plt.text()mpl.text.Text()的文档字符串。

变换和文本位置

在前面的示例中,我们将文本注释锚定到数据位置。 有时最好将文本锚定到轴或图上的位置,与数据无关。在 Matplotlib 中,这是通过修改变换来完成的。

任何图形显示框架都需要一些在坐标系之间进行转换的方案。例如,(x, y) = (1, 1)处的数据点,需要以某种方式表示在图上的某个位置,而该位置又需要在屏幕上以像素表示。在数学上,这种坐标转换相对简单,Matplotlib 有一套完善的工具,它们在内部使用来执行(这些工具可以在matplotlib.transforms子模块中进行探索)。

普通用户很少需要关心这些变换的细节,但在考虑在图形上放置文本时,它是有用的知识。 在这种情况下,有三种预定义的转换可能很有用:

  • ax.transData:数据坐标相关的变换
  • ax.transAxes:轴域(以轴域维度为单位)相关的变换
  • fig.transFigure:图形(以图形维度为单位)相关的变换

这里让我们看一下,使用这些变换在不同位置绘制文本的示例:

fig, ax = plt.subplots(facecolor='lightgray')
ax.axis([0, 10, 0, 10])

# transform=ax.transData 是默认值,但是我们无论如何也要指定它
ax.text(1, 5, ". Data: (1, 5)", transform=ax.transData)
ax.text(0.5, 0.1, ". Axes: (0.5, 0.1)", transform=ax.transAxes)
ax.text(0.2, 0.2, ". Figure: (0.2, 0.2)", transform=fig.transFigure);
png

请注意,默认情况下,文本在指定坐标的上方和左侧对齐:这里,在每个字符串的开头的'.'将近似标记给定的坐标位置。

transData坐标给出了关联x轴和y轴标签的常用数据坐标。transAxes坐标给出了相对于轴域左下角(这里是白框)的位置,作为轴域大小的比例。transFigure坐标是相似的,但是指定相对于图左下角(这里是灰框)的位置,作为图形大小的比例。

现在请注意,如果我们更改轴限制,那么只有transData坐标会受到影响,而其他坐标则保持不变:

ax.set_xlim(0, 2)
ax.set_ylim(-6, 6)
fig
png

通过交互式更改轴限制可以更清楚地看到这种行为:如果你在笔记本中执行此代码,你可以通过将%matplotlib inline更改为%matplotlib notebook,并使用每个绘图的菜单与它互动来实现它。

箭头和标注

除了刻度线和文本,另一个有用的标注或标记是简单的箭头。

在 Matplotlib 中绘制箭头通常比砍价要困难得多。虽然plt.arrow()函数是可用的,我不建议使用它:它创建的箭头是 SVG 对象,它们会受到不同长宽比的影响,结果很少是用户所期望的。相反,我建议使用plt.annotate()函数。此函数可创建一些文本和箭头,并且箭头可以非常灵活地指定。

在这里,我们将使用annotate及其几个选项:

%matplotlib inline

fig, ax = plt.subplots()

x = np.linspace(0, 20, 1000)
ax.plot(x, np.cos(x))
ax.axis('equal')

ax.annotate('local maximum', xy=(6.28, 1), xytext=(10, 4),
            arrowprops=dict(facecolor='black', shrink=0.05))

ax.annotate('local minimum', xy=(5 * np.pi, -1), xytext=(2, -6),
            arrowprops=dict(arrowstyle="->",
                            connectionstyle="angle3,angleA=0,angleB=-90"));
png

箭头样式通过arrowprops字典控制,该字典有许多选项。这些选项在 Matplotlib 的在线文档中有相当详细的记录,因此,比起在此复述这些选项,快速展示一些选项可能更有用。让我们使用之前的出生率图表演示几种可用选项:

fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)

# 向绘图添加标签
ax.annotate("New Year's Day", xy=('2012-1-1', 4100),  xycoords='data',
            xytext=(50, -30), textcoords='offset points',
            arrowprops=dict(arrowstyle="->",
                            connectionstyle="arc3,rad=-0.2"))

ax.annotate("Independence Day", xy=('2012-7-4', 4250),  xycoords='data',
            bbox=dict(boxstyle="round", fc="none", ec="gray"),
            xytext=(10, -40), textcoords='offset points', ha='center',
            arrowprops=dict(arrowstyle="->"))

ax.annotate('Labor Day', xy=('2012-9-4', 4850), xycoords='data', ha='center',
            xytext=(0, -20), textcoords='offset points')
ax.annotate('', xy=('2012-9-1', 4850), xytext=('2012-9-7', 4850),
            xycoords='data', textcoords='data',
            arrowprops={'arrowstyle': '|-|,widthA=0.2,widthB=0.2', })

ax.annotate('Halloween', xy=('2012-10-31', 4600),  xycoords='data',
            xytext=(-80, -40), textcoords='offset points',
            arrowprops=dict(arrowstyle="fancy",
                            fc="0.6", ec="none",
                            connectionstyle="angle3,angleA=0,angleB=-90"))

ax.annotate('Thanksgiving', xy=('2012-11-25', 4500),  xycoords='data',
            xytext=(-120, -60), textcoords='offset points',
            bbox=dict(boxstyle="round4,pad=.5", fc="0.9"),
            arrowprops=dict(arrowstyle="->",
                            connectionstyle="angle,angleA=0,angleB=80,rad=20"))


ax.annotate('Christmas', xy=('2012-12-25', 3850),  xycoords='data',
             xytext=(-30, 0), textcoords='offset points',
             size=13, ha='right', va="center",
             bbox=dict(boxstyle="round", alpha=0.1),
             arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1));

# 标记轴域
ax.set(title='USA births by day of year (1969-1988)',
       ylabel='average daily births')

# 使用中心化的月标签将 x 轴格式化
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));

ax.set_ylim(3600, 5400);

[图片上传失败...(image-6e934c-1547868405690)]

你会注意到箭头和文本框的规格非常详细:这使你能够创建几乎任何箭头样式。不幸的是,这也意味着这些功能通常必须手动调整,这个过程在制作出版品质的图形时非常耗时!最后我要提醒你,前面的样式混合绝不是展示数据的最佳实践,而是作为一些可用选项的演示。

可用箭头和注释样式的更多讨论和示例,可以在 Matplotlib 库中找到,特别是标注的演示

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,783评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,396评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,834评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,036评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,035评论 5 362
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,242评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,727评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,376评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,415评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,463评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,140评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,734评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,809评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,028评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,521评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,119评论 2 341

推荐阅读更多精彩内容