8.12 文本和注解
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
本節(jié)是《Python 數(shù)據(jù)科學(xué)手冊》(Python Data Science Handbook)的摘錄。
創(chuàng)建良好的可視化涉及引導(dǎo)讀者并使圖形講述故事。在某些情況下人柿,可以以完全可視的方式講述這個故事柴墩,而不需要添加文本,但在其他情況下凫岖,需要小的文本提示和標(biāo)簽江咳。也許你將使用的最基本的注釋類型是軸標(biāo)簽和標(biāo)題,但選項超出了這個范圍哥放。讓我們看看一些數(shù)據(jù)歼指,以及我們?nèi)绾慰梢暬妥⑨屗瑏碛兄趥鬟_(dá)有趣的信息甥雕。 我們首先設(shè)置筆記本來繪圖并導(dǎo)入我們將使用的函數(shù):
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
plt.style.use('seaborn-whitegrid')
import numpy as np
import pandas as pd
示例: 美國新生兒的假期效應(yīng)
讓我們回到之前處理的一些數(shù)據(jù)踩身,在“示例:出生率數(shù)據(jù)”中,我們在日歷年上生成了平均出生率的圖表社露;如前所述挟阻,這些數(shù)據(jù)可以在 https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv 下載。
我們將使用我們在那里使用的相同清理過程開始峭弟,并繪制結(jié)果:
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);
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)
# 向繪圖添加標(biāo)簽
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)
# 標(biāo)記軸域
ax.set(title='USA births by day of year (1969-1988)',
ylabel='average daily births')
# 使用中心化的月標(biāo)簽將 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
位置,字符串瞒瘸,然后是可選關(guān)鍵字坷备,指定文本的顏色,大小情臭,樣式省撑,對齊方式和其他屬性。在這里俯在,我們使用ha='right'
和ha='center'
竟秫,其中ha
是 horizonal alignment 的縮寫□卫郑可用選項的更多信息肥败,請參閱plt.text()
和mpl.text.Text()
的文檔字符串。
變換和文本位置
在前面的示例中劈猿,我們將文本注釋錨定到數(shù)據(jù)位置。 有時最好將文本錨定到軸或圖上的位置潮孽,與數(shù)據(jù)無關(guān)揪荣。在 Matplotlib 中,這是通過修改變換來完成的往史。
任何圖形顯示框架都需要一些在坐標(biāo)系之間進(jìn)行轉(zhuǎn)換的方案仗颈。例如,(x, y) = (1, 1)
處的數(shù)據(jù)點,需要以某種方式表示在圖上的某個位置挨决,而該位置又需要在屏幕上以像素表示请祖。在數(shù)學(xué)上,這種坐標(biāo)轉(zhuǎn)換相對簡單脖祈,Matplotlib 有一套完善的工具肆捕,它們在內(nèi)部使用來執(zhí)行(這些工具可以在matplotlib.transforms
子模塊中進(jìn)行探索)。
普通用戶很少需要關(guān)心這些變換的細(xì)節(jié)盖高,但在考慮在圖形上放置文本時慎陵,它是有用的知識。 在這種情況下喻奥,有三種預(yù)定義的轉(zhuǎn)換可能很有用:
-
ax.transData
:數(shù)據(jù)坐標(biāo)相關(guān)的變換 -
ax.transAxes
:軸域(以軸域維度為單位)相關(guān)的變換 -
fig.transFigure
:圖形(以圖形維度為單位)相關(guān)的變換
這里讓我們看一下席纽,使用這些變換在不同位置繪制文本的示例:
fig, ax = plt.subplots(facecolor='lightgray')
ax.axis([0, 10, 0, 10])
# transform=ax.transData 是默認(rèn)值,但是我們無論如何也要指定它
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);
請注意撞蚕,默認(rèn)情況下润梯,文本在指定坐標(biāo)的上方和左側(cè)對齊:這里,在每個字符串的開頭的'.'
將近似標(biāo)記給定的坐標(biāo)位置甥厦。
transData
坐標(biāo)給出了關(guān)聯(lián)x
軸和y
軸標(biāo)簽的常用數(shù)據(jù)坐標(biāo)纺铭。transAxes
坐標(biāo)給出了相對于軸域左下角(這里是白框)的位置,作為軸域大小的比例矫渔。transFigure
坐標(biāo)是相似的彤蔽,但是指定相對于圖左下角(這里是灰框)的位置,作為圖形大小的比例庙洼。
現(xiàn)在請注意顿痪,如果我們更改軸限制,那么只有transData
坐標(biāo)會受到影響油够,而其他坐標(biāo)則保持不變:
ax.set_xlim(0, 2)
ax.set_ylim(-6, 6)
fig
通過交互式更改軸限制可以更清楚地看到這種行為:如果你在筆記本中執(zhí)行此代碼蚁袭,你可以通過將%matplotlib inline
更改為%matplotlib notebook
,并使用每個繪圖的菜單與它互動來實現(xiàn)它石咬。
箭頭和標(biāo)注
除了刻度線和文本揩悄,另一個有用的標(biāo)注或標(biāo)記是簡單的箭頭。
在 Matplotlib 中繪制箭頭通常比砍價要困難得多鬼悠。雖然plt.arrow()
函數(shù)是可用的删性,我不建議使用它:它創(chuàng)建的箭頭是 SVG 對象,它們會受到不同長寬比的影響焕窝,結(jié)果很少是用戶所期望的蹬挺。相反,我建議使用plt.annotate()
函數(shù)它掂。此函數(shù)可創(chuàng)建一些文本和箭頭呆瞻,并且箭頭可以非常靈活地指定。
在這里秀鞭,我們將使用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"));
箭頭樣式通過arrowprops
字典控制,該字典有許多選項垃沦。這些選項在 Matplotlib 的在線文檔中有相當(dāng)詳細(xì)的記錄,因此用押,比起在此復(fù)述這些選項肢簿,快速展示一些選項可能更有用。讓我們使用之前的出生率圖表演示幾種可用選項:
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)
# 向繪圖添加標(biāo)簽
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));
# 標(biāo)記軸域
ax.set(title='USA births by day of year (1969-1988)',
ylabel='average daily births')
# 使用中心化的月標(biāo)簽將 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)]
你會注意到箭頭和文本框的規(guī)格非常詳細(xì):這使你能夠創(chuàng)建幾乎任何箭頭樣式只恨。不幸的是译仗,這也意味著這些功能通常必須手動調(diào)整,這個過程在制作出版品質(zhì)的圖形時非常耗時官觅!最后我要提醒你纵菌,前面的樣式混合絕不是展示數(shù)據(jù)的最佳實踐,而是作為一些可用選項的演示休涤。
可用箭頭和注釋樣式的更多討論和示例咱圆,可以在 Matplotlib 庫中找到,特別是標(biāo)注的演示功氨。