创建一个优秀的可视化图表的关键在于引导读者,让他们能理解图表所讲述的故事。在一些情况下,这个故事可以通过纯图像的方式表达,不需要额外添加文字,但是在另外一些情况中,图表需要文字的提示和标签才能将故事讲好。也许标注最基本的类型就是图表的标签和标题,但是其中的选项参数却有很多。让我们在本节中使用一些数据来创建可视化图表并标注这些图表来表达这些有趣的信息。首先还是需要将要用到的模块和包导入Pycharm:
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(r'D:pythonGithub学习材料Python数据科学手册datairths.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);
当我们绘制了这样的图表来表达数据时,如果我们能对一些图表的特性作出标注来吸引读者的注意力通常是非常有帮助的。这可以通过调用plt.text
或ax.text
函数来实现,它们可以在某个特定的 x,y 轴位置输出一段文字:
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) # 设置标题和y轴标签 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.text
方法接收 x 位置、y 位置、一个字符串和额外可选的关键字参数可以用来设置颜色、大小、样式、对齐等文本格式。上面我们使用了ha='right'
和ha='center'
,这里的ha
是*hirizonal alignment(水平对齐)*的缩写。要查阅更多的可用参数,请查看plt.text()
和mpl.text.Text()
的文档字符串内容。
转换和文本位置
在刚才的例子中,我们将文字标注根据数据位置进行了定位。有些时候我们需要将文字标注独立于数据位置而根据图表位置进行定位。Matplotlib 通过转换完成这项工作。
任何的图形显示框架都需要在坐标系统之间进行转换的机制。例如,一个数据点位于 被转换为图表中的某个位置,进而转换为屏幕上显示的像素。这样的坐标转换在数学上都相对来说比较直接,,而且 Matplotlib 提供了一系列的工具实现了转换(这些工具可以在matplotlib.transforms
模块中找到)。
一般来说,用户很少需要关注这些转换的细节,但是当考虑将文本在图表上展示时,这些知识却比较有用。在这种情况中,下面三种定义好的转换是比较有用的:
ax.transData
:与数据坐标相关的转换ax.tranAxes
:与 Axes 尺寸相关的转换(单位是 axes 的宽和高)ax.tranFigure
:与 figure 尺寸相关的转换(单位是 figure 的宽和高)
下面我们来看看使用这些转换将文字写在图表中不同位置的例子:
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);
注意默认情况下,文字是在指定坐标位置靠左对齐的:这里每个字符串开始的"."的位置就是每种转换的坐标位置。
transData
坐标给定的是通常使用的 x 和 y 轴坐标位置。transAxes
坐标给定的是从 axes 左下角开始算起(白色区域)的坐标位置,使用的是宽度和长度的占比。transFigure
坐标类似,给定的是从 figure 左下角开始算起(灰色区域)的坐标位置,使用的也是宽度和长度的占比。
因此如果我们改变了轴的最大长度,只有transData
坐标会收到影响,其他两个还是保持在相同位置:
ax.set_xlim(0, 2) ax.set_ylim(-6, 6) fig
这个变化可以通过动态改变轴的最大长度看的更加清楚:如果你在 notebook 执行这段代码,你可以将%matplotlib inline
改为%matplotlib notebook
,然后使用图表的菜单来交互式的改变图表。
箭头和标注
除了刻度标签和文字标签,另一种常用的标注是箭头。
在 Matplotlib 中绘制箭头通常比你想象的难得多。虽然有plt.arrow()
函数,作者不建议使用它:这个函数绘制的箭头是一个 SVG 对象,因此在图表使用不同的比例的情况会产生问题,结果通常不能让用户满意。因此,作者建议使用plt.annotate()
函数。这个函数会绘制一些文字以及一个箭头,并且箭头可以非常灵活的进行配置。
下面我们提供一些参数来使用annotate
函数:
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"));
箭头的样式是使用箭头属性
字典值进行控制的,里面有很多可用的参数。这些参数在 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') # 设置月份坐标居中显示 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);
上图中箭头和文字框都非常详尽了:可以看出你几乎可以使用plt.annotate
创建任何你想要的箭头样式。不幸的是,这意味着这种特性都需要手工进行调整,因此如果需要获得印刷质量的图像,这将是一个非常耗费时间的工作。最后,必须指出,上述这种多种样式混合的方式来展现数据肯定不是最佳实践,这里只是为了尽可能多的介绍可用的参数。
更多关于 Matplotlib 的箭头和标注样式的讨论和例子可以访问 Matplotlib gallery。