數據科學 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 庫中找到,特別是標注的演示

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容