《利用Python進行數據分析·第2版》第9章 繪圖和可視化
來自專欄 Python程序員
作者:SeanCheney Python愛好者社區--專欄作者
來源:簡書
第1章 準備工作
第2章 Python語法基礎,IPython和Jupyter Notebooks 第3章 Python的數據結構、函數和文件 第4章 NumPy基礎:數組和矢量計算 第5章 pandas入門第6章 數據載入、存儲與文件格式
第7章 數據清洗和準備 第8章 數據規整:聚合、合併和重塑 第9章 繪圖和可視化 第10章 數據聚合與分組運算 第11章 時間序列 第12章 pandas高級應用 第13章 Python建模庫介紹第14章 數據分析案例附錄A NumPy高級應用附錄B 更多關於IPython的內容(完)
信息可視化(也叫繪圖)是數據分析中最重要的工作之一。它可能是探索過程的一部分,例如,幫助我們找出異常值、必要的數據轉換、得出有關模型的idea等。另外,做一個可交互的數據可視化也許是工作的最終目標。Python有許多庫進行靜態或動態的數據可視化,但我這裡重要關注於matplotlib(http://matplotlib.org/)和基於它的庫。
matplotlib是一個用於創建出版質量圖表的桌面繪圖包(主要是2D方面)。該項目是由John Hunter於2002年啟動的,其目的是為Python構建一個MATLAB式的繪圖介面。matplotlib和IPython社區進行合作,簡化了從IPython shell(包括現在的Jupyter notebook)進行互動式繪圖。matplotlib支持各種操作系統上許多不同的GUI後端,而且還能將圖片導出為各種常見的矢量(vector)和光柵(raster)圖:PDF、SVG、JPG、PNG、BMP、GIF等。除了幾張,本書中的大部分圖都是用它生成的。
隨著時間的發展,matplotlib衍生出了多個數據可視化的工具集,它們使用matplotlib作為底層。其中之一是seaborn(http://seaborn.pydata.org/),本章後面會學習它。
學習本章代碼案例的最簡單方法是在Jupyter notebook進行互動式繪圖。在Jupyter notebook中執行下面的語句:
%matplotlib notebook
9.1 matplotlib API入門
matplotlib的通常引入約定是:
In [11]: import matplotlib.pyplot as plt
在Jupyter中運行%matplotlib notebook(或在IPython中運行%matplotlib),就可以創建一個簡單的圖形。如果一切設置正確,會看到圖9-1:
In [12]: import numpy as npIn [13]: data = np.arange(10)In [14]: dataOut[14]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])In [15]: plt.plot(data)
圖9-1 簡單的線圖
雖然seaborn這樣的庫和pandas的內置繪圖函數能夠處理許多普通的繪圖任務,但如果需要自定義一些高級功能的話就必須學習matplotlib API。
筆記:雖然本書沒有詳細地討論matplotlib的各種功能,但足以將你引入門。matplotlib的示例庫和文檔是學習高級特性的最好資源。
Figure和Subplot
matplotlib的圖像都位於Figure對象中。你可以用plt.figure創建一個新的Figure:
In [16]: fig = plt.figure()
如果用的是IPython,這時會彈出一個空窗口,但在Jupyter中,必須再輸入更多命令才能看到。plt.figure有一些選項,特別是figsize,它用於確保當圖片保存到磁碟時具有一定的大小和縱橫比。
不能通過空Figure繪圖。必須用add_subplot創建一個或多個subplot才行:
In [17]: ax1 = fig.add_subplot(2, 2, 1)
這條代碼的意思是:圖像應該是2×2的(即最多4張圖),且當前選中的是4個subplot中的第一個(編號從1開始)。如果再把後面兩個subplot也創建出來,最終得到的圖像如圖9-2所示:
In [18]: ax2 = fig.add_subplot(2, 2, 2)In [19]: ax3 = fig.add_subplot(2, 2, 3)
圖9-2 帶有三個subplot的Figure
提示:使用Jupyter notebook有一點不同,即每個小窗重新執行後,圖形會被重置。因此,對於複雜的圖形,,你必須將所有的繪圖命令存在一個小窗里。
這裡,我們運行同一個小窗里的所有命令:
fig = plt.figure()ax1 = fig.add_subplot(2, 2, 1)ax2 = fig.add_subplot(2, 2, 2)ax3 = fig.add_subplot(2, 2, 3)
如果這時執行一條繪圖命令(如plt.plot([1.5, 3.5, -2, 1.6])),matplotlib就會在最後一個用過的subplot(如果沒有則創建一個)上進行繪製,隱藏創建figure和subplot的過程。因此,如果我們執行下列命令,你就會得到如圖9-3所示的結果:
In [20]: plt.plot(np.random.randn(50).cumsum(), k--)
圖9-3 繪製一次之後的圖像
"k--"是一個線型選項,用於告訴matplotlib繪製黑色虛線圖。上面那些由fig.add_subplot所返回的對象是AxesSubplot對象,直接調用它們的實例方法就可以在其它空著的格子裡面畫圖了,如圖9-4所示:
In [21]: _ = ax1.hist(np.random.randn(100), bins=20, color=k, alpha=0.3)In [22]: ax2.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))
圖9-4 繼續繪製兩次之後的圖像
你可以在matplotlib的文檔中找到各種圖表類型。
創建包含subplot網格的figure是一個非常常見的任務,matplotlib有一個更為方便的方法plt.subplots,它可以創建一個新的Figure,並返回一個含有已創建的subplot對象的NumPy數組:
In [24]: fig, axes = plt.subplots(2, 3)In [25]: axesOut[25]: array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fb626374048>, <matplotlib.axes._subplots.AxesSubplot object at 0x7fb62625db00>, <matplotlib.axes._subplots.AxesSubplot object at 0x7fb6262f6c88>], [<matplotlib.axes._subplots.AxesSubplot object at 0x7fb6261a36a0>, <matplotlib.axes._subplots.AxesSubplot object at 0x7fb626181860>, <matplotlib.axes._subplots.AxesSubplot object at 0x7fb6260fd4e0>]], dtype=object)
這是非常實用的,因為可以輕鬆地對axes數組進行索引,就好像是一個二維數組一樣,例如axes[0,1]。你還可以通過sharex和sharey指定subplot應該具有相同的X軸或Y軸。在比較相同範圍的數據時,這也是非常實用的,否則,matplotlib會自動縮放各圖表的界限。有關該方法的更多信息,請參見表9-1。
表9-1 pyplot.subplots的選項
調整subplot周圍的間距
默認情況下,matplotlib會在subplot外圍留下一定的邊距,並在subplot之間留下一定的間距。間距跟圖像的高度和寬度有關,因此,如果你調整了圖像大小(不管是編程還是手工),間距也會自動調整。利用Figure的subplots_adjust方法可以輕而易舉地修改間距,此外,它也是個頂級函數:
subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
wspace和hspace用於控制寬度和高度的百分比,可以用作subplot之間的間距。下面是一個簡單的例子,其中我將間距收縮到了0(如圖9-5所示):
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)for i in range(2): for j in range(2): axes[i, j].hist(np.random.randn(500), bins=50, color=k, alpha=0.5)plt.subplots_adjust(wspace=0, hspace=0)
圖9-5 各subplot之間沒有間距
不難看出,其中的軸標籤重疊了。matplotlib不會檢查標籤是否重疊,所以對於這種情況,你只能自己設定刻度位置和刻度標籤。後面幾節將會詳細介紹該內容。
顏色、標記和線型
matplotlib的plot函數接受一組X和Y坐標,還可以接受一個表示顏色和線型的字元串縮寫。例如,要根據x和y繪製綠色虛線,你可以執行如下代碼:
ax.plot(x, y, g--)
這種在一個字元串中指定顏色和線型的方式非常方便。在實際中,如果你是用代碼繪圖,你可能不想通過處理字元串來獲得想要的格式。通過下面這種更為明確的方式也能得到同樣的效果:
ax.plot(x, y, linestylex=--, color=g)
常用的顏色可以使用顏色縮寫,你也可以指定顏色碼(例如,#CECECE)。你可以通過查看plot的文檔字元串查看所有線型的合集(在IPython和Jupyter中使用plot?)。
線圖可以使用標記強調數據點。因為matplotlib可以創建連續線圖,在點之間進行插值,因此有時可能不太容易看出真實數據點的位置。標記也可以放到格式字元串中,但標記類型和線型必須放在顏色後面(見圖9-6):
In [30]: from numpy.random import randnIn [31]: plt.plot(randn(30).cumsum(), ko--)
圖9-6 帶有標記的線型圖示例
還可以將其寫成更為明確的形式:
plot(randn(30).cumsum(), color=k, linestylex=dashed, marker=o)
在線型圖中,非實際數據點默認是按線性方式插值的。可以通過drawstyle選項修改(見圖9-7):
In [33]: data = np.random.randn(30).cumsum()In [34]: plt.plot(data, k--, label=Default)Out[34]: [<matplotlib.lines.Line2D at 0x7fb624d86160>]In [35]: plt.plot(data, , drawstylex=steps-post, label=steps-post)Out[35]: [<matplotlib.lines.Line2D at 0x7fb624d869e8>]In [36]: plt.legend(loc=best)
圖9-7 不同drawstyle選項的線型圖
你可能注意到運行上面代碼時有輸出<matplotlib.lines.Line2D at ...>。matplotlib會返回引用了新添加的子組件的對象。大多數時候,你可以放心地忽略這些輸出。這裡,因為我們傳遞了label參數到plot,我們可以創建一個plot圖例,指明每條使用plt.legend的線。
筆記:你必須調用plt.legend(或使用ax.legend,如果引用了軸的話)來創建圖例,無論你繪圖時是否傳遞label標籤選項。
刻度、標籤和圖例
對於大多數的圖表裝飾項,其主要實現方式有二:使用過程型的pyplot介面(例如,matplotlib.pyplot)以及更為面向對象的原生matplotlib API。
pyplot介面的設計目的就是互動式使用,含有諸如xlim、xticks和xticklabels之類的方法。它們分別控制圖表的範圍、刻度位置、刻度標籤等。其使用方式有以下兩種:
- 調用時不帶參數,則返回當前的參數值(例如,plt.xlim()返回當前的X軸繪圖範圍)。
- 調用時帶參數,則設置參數值(例如,plt.xlim([0,10])會將X軸的範圍設置為0到10)。
所有這些方法都是對當前或最近創建的AxesSubplot起作用的。它們各自對應subplot對象上的兩個方法,以xlim為例,就是ax.get_xlim和ax.set_xlim。我更喜歡使用subplot的實例方法(因為我喜歡明確的事情,而且在處理多個subplot時這樣也更清楚一些)。當然你完全可以選擇自己覺得方便的那個。
設置標題、軸標籤、刻度以及刻度標籤
為了說明自定義軸,我將創建一個簡單的圖像並繪製一段隨機漫步(如圖9-8所示):
In [37]: fig = plt.figure()In [38]: ax = fig.add_subplot(1, 1, 1)In [39]: ax.plot(np.random.randn(1000).cumsum())
圖9-8 用於演示xticks的簡單線型圖(帶有標籤)
要改變x軸刻度,最簡單的辦法是使用set_xticks和set_xticklabels。前者告訴matplotlib要將刻度放在數據範圍中的哪些位置,默認情況下,這些位置也就是刻度標籤。但我們可以通過set_xticklabels將任何其他的值用作標籤:
In [40]: ticks = ax.set_xticks([0, 250, 500, 750, 1000])In [41]: labels = ax.set_xticklabels([one, two, three, four, five], ....: rotation=30, fontsize=small)
rotation選項設定x刻度標籤傾斜30度。最後,再用set_xlabel為X軸設置一個名稱,並用set_title設置一個標題(見圖9-9的結果):
In [42]: ax.set_title(My first matplotlib plot)Out[42]: <matplotlib.text.Text at 0x7fb624d055f8>In [43]: ax.set_xlabel(Stages)
圖9-9 用於演示xticks的簡單線型圖
Y軸的修改方式與此類似,只需將上述代碼中的x替換為y即可。軸的類有集合方法,可以批量設定繪圖選項。前面的例子,也可以寫為:
props = { title: My first matplotlib plot, xlabel: Stages}ax.set(**props)
添加圖例
圖例(legend)是另一種用於標識圖表元素的重要工具。添加圖例的方式有多種。最簡單的是在添加subplot的時候傳入label參數:
In [44]: from numpy.random import randnIn [45]: fig = plt.figure(); ax = fig.add_subplot(1, 1, 1)In [46]: ax.plot(randn(1000).cumsum(), k, label=one)Out[46]: [<matplotlib.lines.Line2D at 0x7fb624bdf860>]In [47]: ax.plot(randn(1000).cumsum(), k--, label=two)Out[47]: [<matplotlib.lines.Line2D at 0x7fb624be90f0>]In [48]: ax.plot(randn(1000).cumsum(), k., label=three)Out[48]: [<matplotlib.lines.Line2D at 0x7fb624be9160>]
在此之後,你可以調用ax.legend()或plt.legend()來自動創建圖例(結果見圖9-10):
In [49]: ax.legend(loc=best)
圖9-10 帶有三條線以及圖例的簡單線型圖
legend方法有幾個其它的loc位置參數選項。請查看文檔字元串(使用ax.legend?)。
loc告訴matplotlib要將圖例放在哪。如果你不是吹毛求疵的話,"best"是不錯的選擇,因為它會選擇最不礙事的位置。要從圖例中去除一個或多個元素,不傳入label或傳入label=nolegend即可。(中文第一版這裡把best錯寫成了beat)
註解以及在Subplot上繪圖
除標準的繪圖類型,你可能還希望繪製一些子集的註解,可能是文本、箭頭或其他圖形等。註解和文字可以通過text、arrow和annotate函數進行添加。text可以將文本繪製在圖表的指定坐標(x,y),還可以加上一些自定義格式:
ax.text(x, y, Hello world!, family=monospace, fontsize=10)
註解中可以既含有文本也含有箭頭。例如,我們根據最近的標準普爾500指數價格(來自Yahoo!Finance)繪製一張曲線圖,並標出2008年到2009年金融危機期間的一些重要日期。你可以在Jupyter notebook的一個小窗中試驗這段代碼(圖9-11是結果):
from datetime import datetimefig = plt.figure()ax = fig.add_subplot(1, 1, 1)data = pd.read_csv(examples/spx.csv, index_col=0, parse_dates=True)spx = data[SPX]spx.plot(ax=ax, stylex=)crisis_data = [ (datetime(2007, 10, 11), Peak of bull market), (datetime(2008, 3, 12), Bear Stearns Fails), (datetime(2008, 9, 15), Lehman Bankruptcy)]for date, label in crisis_data: ax.annotate(label, xy=(date, spx.asof(date) + 75), xytext=(date, spx.asof(date) + 225), arrowprops=dict(facecolor=black, headwidth_=4, width_=2, headlength=4), horizontalalignment=left, verticalalignment=top)# Zoom in on 2007-2010ax.set_xlim([1/1/2007, 1/1/2011])ax.set_ylim([600, 1800])ax.set_title(Important dates in the 2008-2009 financial crisis)
圖9-11 2008-2009年金融危機期間的重要日期
這張圖中有幾個重要的點要強調:ax.annotate方法可以在指定的x和y坐標軸繪製標籤。我們使用set_xlim和set_ylim人工設定起始和結束邊界,而不使用matplotlib的默認方法。最後,用ax.set_title添加圖標標題。
更多有關註解的示例,請訪問matplotlib的在線示例庫。
圖形的繪製要麻煩一些。matplotlib有一些表示常見圖形的對象。這些對象被稱為塊(patch)。其中有些(如Rectangle和Circle),可以在matplotlib.pyplot中找到,但完整集合位於matplotlib.patches。
要在圖表中添加一個圖形,你需要創建一個塊對象shp,然後通過ax.add_patch(shp)將其添加到subplot中(如圖9-12所示):
fig = plt.figure()ax = fig.add_subplot(1, 1, 1)rect = plt.Rectangle((0.2, 0.75), 0.4, 0.15, color=k, alpha=0.3)circ = plt.Circle((0.7, 0.2), 0.15, color=b, alpha=0.3)pgon = plt.Polygon([[0.15, 0.15], [0.35, 0.4], [0.2, 0.6]], color=g, alpha=0.5)ax.add_patch(rect)ax.add_patch(circ)ax.add_patch(pgon)
圖9-12 由三個塊圖形組成的圖
如果查看許多常見圖表對象的具體實現代碼,你就會發現它們其實就是由塊patch組裝而成的。
將圖表保存到文件
利用plt.savefig可以將當前圖表保存到文件。該方法相當於Figure對象的實例方法savefig。例如,要將圖表保存為SVG文件,你只需輸入:
plt.savefig(figpath.svg)
文件類型是通過文件擴展名推斷出來的。因此,如果你使用的是.pdf,就會得到一個PDF文件。我在發布圖片時最常用到兩個重要的選項是dpi(控制「每英寸點數」解析度)和bbox_inches(可以剪除當前圖表周圍的空白部分)。要得到一張帶有最小白邊且解析度為400DPI的PNG圖片,你可以:
plt.savefig(figpath.png, dpi=400, bbox_inches=tight)
savefig並非一定要寫入磁碟,也可以寫入任何文件型的對象,比如BytesIO:
from io import BytesIObuffer = BytesIO()plt.savefig(buffer)plot_data = buffer.getvalue()
表9-2列出了savefig的其它選項。
表9-2 Figure.savefig的選項
matplotlib配置
matplotlib自帶一些配色方案,以及為生成出版質量的圖片而設定的默認配置信息。幸運的是,幾乎所有默認行為都能通過一組全局參數進行自定義,它們可以管理圖像大小、subplot邊距、配色方案、字體大小、網格類型等。一種Python編程方式配置系統的方法是使用rc方法。例如,要將全局的圖像默認大小設置為10×10,你可以執行:
plt.rc(figure, figsize=(10, 10))
rc的第一個參數是希望自定義的對象,如figure、axes、xtick、ytick、grid、legend等。其後可以跟上一系列的關鍵字參數。一個簡單的辦法是將這些選項寫成一個字典:
font_options = {family : monospace, weight : bold, size : small}plt.rc(font, **font_options)
要了解全部的自定義選項,請查閱matplotlib的配置文件matplotlibrc(位於matplotlib/mpl-data目錄中)。如果對該文件進行了自定義,並將其放在你自己的.matplotlibrc目錄中,則每次使用matplotlib時就會載入該文件。
下一節,我們會看到,seaborn包有若干內置的繪圖主題或類型,它們使用了matplotlib的內部配置。
9.2 使用pandas和seaborn繪圖
matplotlib實際上是一種比較低級的工具。要繪製一張圖表,你組裝一些基本組件就行:數據展示(即圖表類型:線型圖、柱狀圖、盒形圖、散布圖、等值線圖等)、圖例、標題、刻度標籤以及其他註解型信息。
在pandas中,我們有多列數據,還有行和列標籤。pandas自身就有內置的方法,用於簡化從DataFrame和Series繪製圖形。另一個庫seaborn(https://seaborn.pydata.org/),由Michael Waskom創建的靜態圖形庫。Seaborn簡化了許多常見可視類型的創建。
提示:引入seaborn會修改matplotlib默認的顏色方案和繪圖類型,以提高可讀性和美觀度。即使你不使用seaborn API,你可能也會引入seaborn,作為提高美觀度和繪製常見matplotlib圖形的簡化方法。
線型圖
Series和DataFrame都有一個用於生成各類圖表的plot方法。默認情況下,它們所生成的是線型圖(如圖9-13所示):
In [60]: s = pd.Series(np.random.randn(10).cumsum(), index=np.arange(0, 100, 10))In [61]: s.plot()
圖9-13 簡單的Series圖表示例
該Series對象的索引會被傳給matplotlib,並用以繪製X軸。可以通過use_index=False禁用該功能。X軸的刻度和界限可以通過xticks和xlim選項進行調節,Y軸就用yticks和ylim。plot參數的完整列表請參見表9-3。我只會講解其中幾個,剩下的就留給讀者自己去研究了。
表9-3 Series.plot方法的參數
pandas的大部分繪圖方法都有一個可選的ax參數,它可以是一個matplotlib的subplot對象。這使你能夠在網格布局中更為靈活地處理subplot的位置。
DataFrame的plot方法會在一個subplot中為各列繪製一條線,並自動創建圖例(如圖9-14所示):
In [62]: df = pd.DataFrame(np.random.randn(10, 4).cumsum(0), ....: columns=[A, B, C, D], ....: index=np.arange(0, 100, 10))In [63]: df.plot()
圖9-14 簡單的DataFrame繪圖
plot屬性包含一批不同繪圖類型的方法。例如,df.plot()等價於df.plot.line()。後面會學習這些方法。
筆記:plot的其他關鍵字參數會被傳給相應的matplotlib繪圖函數,所以要更深入地自定義圖表,就必須學習更多有關matplotlib API的知識。
DataFrame還有一些用於對列進行靈活處理的選項,例如,是要將所有列都繪製到一個subplot中還是創建各自的subplot。詳細信息請參見表9-4。
表9-4 專用於DataFrame的plot參數
注意: 有關時間序列的繪圖,請見第11章。
柱狀圖
plot.bar()和plot.barh()分別繪製水平和垂直的柱狀圖。這時,Series和DataFrame的索引將會被用作X(bar)或Y(barh)刻度(如圖9-15所示):
In [64]: fig, axes = plt.subplots(2, 1)In [65]: data = pd.Series(np.random.rand(16), index=list(abcdefghijklmnop))In [66]: data.plot.bar(ax=axes[0], color=k, alpha=0.7)Out[66]: <matplotlib.axes._subplots.AxesSubplot at 0x7fb62493d470>In [67]: data.plot.barh(ax=axes[1], color=k, alpha=0.7)
圖9-15 水平和垂直的柱狀圖
color=k和alpha=0.7設定了圖形的顏色為黑色,並使用部分的填充透明度。對於DataFrame,柱狀圖會將每一行的值分為一組,並排顯示,如圖9-16所示:
In [69]: df = pd.DataFrame(np.random.rand(6, 4), ....: index=[one, two, three, four, five, six], ....: columns=pd.Index([A, B, C, D], name=Genus))In [70]: dfOut[70]: Genus A B C Done 0.370670 0.602792 0.229159 0.486744two 0.420082 0.571653 0.049024 0.880592three 0.814568 0.277160 0.880316 0.431326four 0.374020 0.899420 0.460304 0.100843five 0.433270 0.125107 0.494675 0.961825six 0.601648 0.478576 0.205690 0.560547In [71]: df.plot.bar()
圖9-16 DataFrame的柱狀圖
注意,DataFrame各列的名稱"Genus"被用作了圖例的標題。
設置stacked=True即可為DataFrame生成堆積柱狀圖,這樣每行的值就會被堆積在一起(如圖9-17所示):
In [73]: df.plot.barh(stacked=True, alpha=0.5)
圖9-17 DataFrame的堆積柱狀圖
筆記:柱狀圖有一個非常不錯的用法:利用value_counts圖形化顯示Series中各值的出現頻率,比如s.value_counts().plot.bar()。
再以本書前面用過的那個有關小費的數據集為例,假設我們想要做一張堆積柱狀圖以展示每天各種聚會規模的數據點的百分比。我用read_csv將數據載入進來,然後根據日期和聚會規模創建一張交叉表:
In [75]: tips = pd.read_csv(examples/tips.csv)In [76]: party_counts = pd.crosstab(tips[day], tips[size])In [77]: party_countsOut[77]: size 1 2 3 4 5 6day Fri 1 16 1 1 0 0Sat 2 53 18 13 1 0Sun 0 39 15 18 3 1Thur 1 48 4 5 1 3# Not many 1- and 6-person partiesIn [78]: party_counts = party_counts.loc[:, 2:5]
然後進行規格化,使得各行的和為1,並生成圖表(如圖9-18所示):
# Normalize to sum to 1In [79]: party_pcts = party_counts.div(party_counts.sum(1), axis=0)In [80]: party_pctsOut[80]: size 2 3 4 5day Fri 0.888889 0.055556 0.055556 0.000000Sat 0.623529 0.211765 0.152941 0.011765Sun 0.520000 0.200000 0.240000 0.040000Thur 0.827586 0.068966 0.086207 0.017241In [81]: party_pcts.plot.bar()
圖9-18 每天各種聚會規模的比例
於是,通過該數據集就可以看出,聚會規模在周末會變大。
對於在繪製一個圖形之前,需要進行合計的數據,使用seaborn可以減少工作量。用seaborn來看每天的小費比例(圖9-19是結果):
In [83]: import seaborn as snsIn [84]: tips[tip_pct] = tips[tip] / (tips[total_bill] - tips[tip])In [85]: tips.head()Out[85]: total_bill tip smoker day time size tip_pct0 16.99 1.01 No Sun Dinner 2 0.0632041 10.34 1.66 No Sun Dinner 3 0.1912442 21.01 3.50 No Sun Dinner 3 0.1998863 23.68 3.31 No Sun Dinner 2 0.1624944 24.59 3.61 No Sun Dinner 4 0.172069In [86]: sns.barplot(x=tip_pct, y=day, data=tips, orient=h)
圖9-19 小費的每日比例,帶有誤差條
seaborn的繪製函數使用data參數,它可能是pandas的DataFrame。其它的參數是關於列的名字。因為一天的每個值有多次觀察,柱狀圖的值是tip_pct的平均值。繪製在柱狀圖上的黑線代表95%置信區間(可以通過可選參數配置)。
seaborn.barplot有顏色選項,使我們能夠通過一個額外的值設置(見圖9-20):
In [88]: sns.barplot(x=tip_pct, y=day, hue=time, data=tips, orient=h)
圖9-20 根據天和時間的小費比例
注意,seaborn已經自動修改了圖形的美觀度:默認調色板,圖形背景和網格線的顏色。你可以用seaborn.set在不同的圖形外觀之間切換:
In [90]: sns.set(stylex="whitegrid")
直方圖和密度圖
直方圖(histogram)是一種可以對值頻率進行離散化顯示的柱狀圖。數據點被拆分到離散的、間隔均勻的面元中,繪製的是各面元中數據點的數量。再以前面那個小費數據為例,通過在Series使用plot.hist方法,我們可以生成一張「小費佔消費總額百分比」的直方圖(如圖9-21所示):
In [92]: tips[tip_pct].plot.hist(bins=50)
圖9-21 小費百分比的直方圖
與此相關的一種圖表類型是密度圖,它是通過計算「可能會產生觀測數據的連續概率分布的估計」而產生的。一般的過程是將該分布近似為一組核(即諸如正態分布之類的較為簡單的分布)。因此,密度圖也被稱作KDE(Kernel Density Estimate,核密度估計)圖。使用plot.kde和標準混合正態分布估計即可生成一張密度圖(見圖9-22):
In [94]: tips[tip_pct].plot.density()
圖9-22 小費百分比的密度圖
seaborn的distplot方法繪製直方圖和密度圖更加簡單,還可以同時畫出直方圖和連續密度估計圖。作為例子,考慮一個雙峰分布,由兩個不同的標準正態分布組成(見圖9-23):
In [96]: comp1 = np.random.normal(0, 1, size=200)In [97]: comp2 = np.random.normal(10, 2, size=200)In [98]: values = pd.Series(np.concatenate([comp1, comp2]))In [99]: sns.distplot(values, bins=100, color=k)
圖9-23 標準混合密度估計的標準直方圖
散布圖或點圖
點圖或散布圖是觀察兩個一維數據序列之間的關係的有效手段。在下面這個例子中,我載入了來自statsmodels項目的macrodata數據集,選擇了幾個變數,然後計算對數差:
In [100]: macro = pd.read_csv(examples/macrodata.csv)In [101]: data = macro[[cpi, m1, tbilrate, unemp]]In [102]: trans_data = np.log(data).diff().dropna()In [103]: trans_data[-5:]Out[103]: cpi m1 tbilrate unemp198 -0.007904 0.045361 -0.396881 0.105361199 -0.021979 0.066753 -2.277267 0.139762200 0.002340 0.010286 0.606136 0.160343201 0.008419 0.037461 -0.200671 0.127339202 0.008894 0.012202 -0.405465 0.042560
然後可以使用seaborn的regplot方法,它可以做一個散布圖,並加上一條線性回歸的線(見圖9-24):
In [105]: sns.regplot(m1, unemp, data=trans_data)Out[105]: <matplotlib.axes._subplots.AxesSubplot at 0x7fb613720be0>In [106]: plt.title(Changes in log %s versus log %s % (m1, unemp))
圖9-24 seaborn的回歸/散布圖
在探索式數據分析工作中,同時觀察一組變數的散布圖是很有意義的,這也被稱為散布圖矩陣(scatter plot matrix)。純手工創建這樣的圖表很費工夫,所以seaborn提供了一個便捷的pairplot函數,它支持在對角線上放置每個變數的直方圖或密度估計(見圖9-25):
In [107]: sns.pairplot(trans_data, diag_kind=kde, plot_kws={alpha: 0.2})
圖9-25 statsmodels macro data的散布圖矩陣
你可能注意到了plot_kws參數。它可以讓我們傳遞配置選項到非對角線元素上的圖形使用。對於更詳細的配置選項,可以查閱seaborn.pairplot文檔字元串。
分面網格(facet grid)和類型數據
要是數據集有額外的分組維度呢?有多個分類變數的數據可視化的一種方法是使用小面網格。seaborn有一個有用的內置函數factorplot,可以簡化製作多種分面圖(見圖9-26):
In [108]: sns.factorplot(x=day, y=tip_pct, hue=time, col=smoker, .....: kind=bar, data=tips[tips.tip_pct < 1])
圖9-26 按照天/時間/吸煙者的小費百分比
除了在分面中用不同的顏色按時間分組,我們還可以通過給每個時間值添加一行來擴展分面網格:
In [109]: sns.factorplot(x=day, y=tip_pct, row=time, .....: col=smoker, .....: kind=bar, data=tips[tips.tip_pct < 1])
圖9-27 按天的tip_pct,通過time/smoker分面
factorplot支持其它的繪圖類型,你可能會用到。例如,盒圖(它可以顯示中位數,四分位數,和異常值)就是一個有用的可視化類型(見圖9-28):
In [110]: sns.factorplot(x=tip_pct, y=day, kind=box, .....: data=tips[tips.tip_pct < 0.5])
圖9-28 按天的tip_pct的盒圖
使用更通用的seaborn.FacetGrid類,你可以創建自己的分面網格。請查閱seaborn的文檔(https://seaborn.pydata.org/)。
9.3 其它的Python可視化工具
與其它開源庫類似,Python創建圖形的方式非常多(根本羅列不完)。自從2010年,許多開發工作都集中在創建互動式圖形以便在Web上發布。利用工具如Boken(https://bokeh.pydata.org/en/latest/)和Plotly(https://github.com/plotly/plotly.py),現在可以創建動態交互圖形,用於網頁瀏覽器。
對於創建用於列印或網頁的靜態圖形,我建議默認使用matplotlib和附加的庫,比如pandas和seaborn。對於其它數據可視化要求,學習其它的可用工具可能是有用的。我鼓勵你探索繪圖的生態系統,因為它將持續發展。
9.4 總結
本章的目的是熟悉一些基本的數據可視化操作,使用pandas,matplotlib,和seaborn。如果視覺顯示數據分析的結果對你的工作很重要,我鼓勵你尋求更多的資源來了解更高效的數據可視化。這是一個活躍的研究領域,你可以通過在線和紙質的形式學習許多優秀的資源。
下一章,我們將重點放在pandas的數據聚合和分組操作上。
推薦閱讀:
※Matplotlib:入門二
※畫給設計師看的營銷大法
※01.向量是什麼
※Matplotlib畫三維曲線
※如何用Mapbox製作一張音樂地圖?這張地圖告訴你全世界都在聽什麼