標籤:

利用Python進行數據分析——繪圖和可視化(八)(1)

Python有許多可視化工具,但是我主要講解matplotlib(http://matplotlib.sourceforge.net)。此外,還可以利用諸如d3.js(d3js.org/)之類的工具為Web應用構建互動式圖像。

matplotlib是一個用於創建出版質量圖表的桌面繪圖包(主要是2D方面)。該項目是由John Hunter於2002年啟動的,其目的是為Python構建一個MATLAB式的繪圖介面。如果結合使用一種GUI工具包(如IPython),matplotlib還具有諸如縮放和平移等交互功能。它不僅支持各種操作系統上許多不同的GUI後端,而且還能將圖片導出為各種常見的矢量(vector)和光柵(raster)圖:PDF、SVG、JPG、PNG、BMP、GIF等。

matplotlib還有許多插件工具集,如用於3D圖形的mplot3d以及用於地圖和投影的basemap。要使用本章中的代碼示例,請確保你的IPython是以Pylab模式啟動的(ipython --pylab),或通過%gui魔術命令打開了GUI事件循環集成。

1、matplotlib API入門

使用matplotlib的辦法有很多種,最常用的方式是Python模式的IPython(ipython -pylab)。這樣會將IPython配置為使用你所指定的matplotlib GUI後端(Tk、wxPython、PyQt、Mac OS X native、GTK)。對大部分用戶而言,默認的後端就已經夠用了。Pylab模式還會向IPython引入一大堆模塊和函數以提供一種更接近於MATLAB的界面。繪製一張簡單的圖表即可測試是否一切準備就緒:

如果一切都沒有問題,就會彈出一個新窗口,其中繪製的是一條直線。你可以用滑鼠或輸入close()來關閉它。matplotlib API函數(如plot和close)都位於matplotlib.pyllot模塊中,其通常的引入約定是:

[python] view plaincopy

  1. In [2]: import matplotlib.pyplot as plt

雖然pandas的繪圖函數能夠處理許多普通的繪圖任務,但如果需要自定義一些高級功能的話就必須學習matplotlib API。matplotlib的示例庫和文檔是成為繪圖高手的最佳學習資源。

2、Figure和Subplot

matplotlib的圖像都位於Figure對象中。你可以用plt.figure創建一個新的Figure:

[python] view plaincopy

  1. In [3]: fig = plt.figure()

這時會彈出一個空窗口。plt.figure有一些選項,特別是figsize,它用於確保當圖片保存到磁碟時具有一定的大小和縱橫比。matplotlib中的Figure還支持一種MATLAB式的編號架構(如plt.figure(2))。通過plt.gcf()即可得到當前Figure的引用。

不能通過空Figure繪圖。必須用add_subplot創建一個或多個subplot才行:

[python] view plaincopy

  1. In [4]: ax1 = fig.add_subplot(2, 2, 1)

這條代碼的意思是:圖像應該是2

2的,且當前選中的是4個subplot中的第一個(編號從1開始)。如果再把後面兩個subplot也創建出來,最終得到的圖像如下所示:

如果這時發出一條繪圖命令哪個(如plt.plot([1.5, 3.5, -2, 1.6])),matplotlib就會在最後一個用過的subplot(如果沒有則創建一個)上進行繪製。因此,如果我們執行下列命令,你就會得到如下所示的效果:

「k--」是一個線型選項,用於告訴matplotlib繪製黑色虛線圖。上面那些由fig.add_subplot所返回的對象是AxesSubplot對象,直接調用它們的實例方法就可以在其他空著的格子裡面畫圖了,如下所示:

[python] view plaincopy

  1. In [9]: _ = ax1.hist(randn(100), bins=20, color=k, alpha=0.3)
  2. In [10]: ax2.scatter(np.arange(30), np.arange(30) + 3 * randn(30))
  3. Out[10]: <matplotlib.collections.PathCollection at 0xa8201cc>

你可以在matplotlib的文檔中找到各種圖標類型。由於根據特定布局創建Figure和subplot是一件非常常見的任務,於是便出現了一個更為方便的方法(plt.subplots),它可以創建一個新的Figure,並返回一個含有已創建的subplot對象的NumPy數組:

[python] view plaincopy

  1. In [13]: fig, axes = plt.subplots(2, 3)
  2. In [14]: axes
  3. Out[14]:
  4. array([[<matplotlib.axes.AxesSubplot object at 0xa76c7ec>,
  5. <matplotlib.axes.AxesSubplot object at 0xae8f1ec>,
  6. <matplotlib.axes.AxesSubplot object at 0xb40bc8c>],
  7. [<matplotlib.axes.AxesSubplot object at 0xb5b7dac>,
  8. <matplotlib.axes.AxesSubplot object at 0xadf680c>,
  9. <matplotlib.axes.AxesSubplot object at 0xad6222c>]], dtype=object)

這是非常實用的,因為可以輕鬆地對axes數組進行索引,就好像是一個二維數組一樣,例如,axes[0, 1]。你還可以通過sharex和sharey指定subplot應該具有相同的X軸或Y軸。在比較相同範圍的數據時,這也是非常實用的,否則,matplotlib會自動縮放各圖表的界限。

3、調整subplot周圍的間距

默認情況下,matplotlib會在subplot外圍留下一定的邊距,並在subplot之間留下一定的間距。間距跟圖像的高度和寬度有關,因此,如果你調整了圖像的大小(不管是編程還是手工),間距也會自動調整。利用Figure的subplots_adjust方法可以輕而易舉地修改間距,此外,它也是個頂級函數:

[python] view plaincopy

  1. In [15]: subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)

wspace和hspace用於控制寬度和高度的百分比,可以用作subplot之間的間距。下面是一個簡單的例子,我們將間距收縮到了0:

不難看出,其中的軸標籤重疊了。matplotlib不會檢查標籤是否重疊,所以對於這種情況,你只能自己設定刻度位置和刻度標籤。

4、顏色、標記和線型

matplotlib的plot函數接受一組X和Y坐標,還可以接受一個表示顏色和線型的字元串縮寫。例如,要根據x和y繪製綠色虛線,你可以執行如下代碼:

[python] view plaincopy

  1. In [18]: ax.plot(x, y, g--)

這種在一個字元串中指定顏色和線型的方式非常方便。通過下面這種更為明確的方式也能得到同樣的效果:

[python] view plaincopy

  1. In [19]: ax.plot(x, y, linestylex=--, color=g)

常用的顏色都有一個縮寫詞,要使用其他任意顏色則可以通過指定其RGB值的形式使用(例如,#CECECE)。完整的linestyle列表請參見plot的文檔。

線型圖還可以加上一些標記(marker),以強調實際的數據點。由於matplotlib創建的是連續的線型圖(點與點之間插值),因此有時可能不太容易看出真實數據點的位置。標記也可以放到格式字元串中,但標記類型和線型必須放在顏色後面。

[python] view plaincopy

  1. In [20]: plt.plot(randn(30).cumsum(), ko--)
  2. Out[20]: [<matplotlib.lines.Line2D at 0xb86924c>]

還可以將其寫成更為明確的形式:

[python] view plaincopy

  1. In [19]: plot(randn(30).cumsum(), color=k, linestylex=dashed, marker=o)

在線型圖中,非實際數據點默認是按線性方式插值的。可以通過drawstyle選項修改:

[python] view plaincopy

  1. In [18]: plt.plot(randn(30).cumsum(), ko--)
  2. Out[18]: [<matplotlib.lines.Line2D at 0xb86924c>]
  3. In [19]: data = randn(30).cumsum()
  4. In [20]: plt.plot(data, k--, label=Default)
  5. Out[20]: [<matplotlib.lines.Line2D at 0xba62c8c>]
  6. In [21]: plt.plot(data, k--, drawstylex=steps-post, label=steps-post)
  7. Out[21]: [<matplotlib.lines.Line2D at 0xba758ac>]
  8. In [22]: plt.legend(loc=best)
  9. Out[22]: <matplotlib.legend.Legend at 0xba75bcc>

5、刻度、標籤和圖例

對於大多數的圖表裝飾項,其主要實現方式有二:使用過程型的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的實例方法,當然你完全可以選擇自己覺得方便的那個。

(1)設置標題、軸標籤、刻度以及刻度標籤

為了說明軸的自定義,我將創建一個簡單的圖像並繪製一段隨機漫步:

[python] view plaincopy

  1. In [23]: fig = plt.figure();
  2. In [24]: ax = fig.add_subplot(1, 1, 1)
  3. In [25]: ax.plot(randn(1000).cumsum())
  4. Out[25]: [<matplotlib.lines.Line2D at 0xbc4da6c>]

要修改X軸的刻度,最簡單的辦法是使用set_xticks和set_xticklabels。前者告訴matplotlib要將刻度放在數據範圍中的哪些位置,默認情況下,這些位置也就是刻度標籤。但我們可以通過set_xticklabels將任何其他的值用作標籤:

[python] view plaincopy

  1. In [28]: fig = plt.figure();
  2. In [29]: ax = fig.add_subplot(1, 1, 1)
  3. In [30]: ax.plot(randn(1000).cumsum())
  4. Out[30]: [<matplotlib.lines.Line2D at 0xbd4684c>]
  5. In [31]: ticks = ax.set_xticks([0, 250, 500, 750, 1000])
  6. In [32]: labels = ax.set_xticklabels([one, two, three, four, five], rotation=30, fontsize=small)
  7. In [33]: ax.set_title(My first matplotlib plot)
  8. Out[33]: <matplotlib.text.Text at 0xbd1ed0c>
  9. In [34]: ax.set_xlabel(Stages)
  10. Out[34]: <matplotlib.text.Text at 0xba911cc>

說明:

Y軸的修改方式與此類似,只需將上述代碼中的x替換為y即可。

(2)添加圖例

圖例(legend)是另一種用於標識圖表元素的重要工具。添加圖例的方式有二。最簡單的是在添加subplot的時候傳入label參數:

[python] view plaincopy

  1. In [35]: fig = plt.figure(); ax = fig.add_subplot(1, 1, 1)
  2. In [36]: ax.plot(randn(1000).cumsum(), k, label=one)
  3. Out[36]: [<matplotlib.lines.Line2D at 0xc0e49cc>]
  4. In [37]: ax.plot(randn(1000).cumsum(), k--, label=two)
  5. Out[37]: [<matplotlib.lines.Line2D at 0xc0e7e2c>]
  6. In [38]: ax.plot(randn(1000).cumsum(), k., label=three)
  7. Out[38]: [<matplotlib.lines.Line2D at 0xc0f238c>]

在此之後,你可以調用ax.legend()或plt.legend()來自動創建圖例:

[python] view plaincopy

  1. In [40]: ax.legend(loc=best)
  2. Out[40]: <matplotlib.legend.Legend at 0xc0e7dcc>

loc告訴matplotlib要將圖例放在哪。如果你不是吹毛求疵的話,「best」是不錯的選擇,因為它會選擇最不礙事的位置。要從圖例中去除一個或多個元素,不傳入label或傳入label=_nolegend_即可。

推薦閱讀:

Powerful Pattern Matching in Flowpython
用python多進程,fork()之後創建了新進程,原來上下文裡面的局部變數也會再創建值完全一樣的么?
如何優雅而高效地使用Matplotlib實現數據可視化
30 秒找到藏在家裡的手機
初學python--認識裝飾器

TAG:Python |