python有許多可視化工具,本書主要講解matplotlib。matplotlib是用於創建出版質量圖表的桌面繪圖包(主要是2D方面)。matplotlib的目的是為了構建一個MATLAB式的繪圖接口。本書中的大部分圖都是用它生成的。除了圖形界面顯示,還可以把圖片保存為pdf、svg、jpg、png、gif等形式。
1、matplotlib API入門
Ipython可以用close()關閉界面。
Figure和Subplot
matplotlib的圖像都位於Figure對象中。用plt.figure創建一個新的Figure。
import numpy as np import pandas as pd import matplotlib.pyplot as plt ''' #plt.plot(np.arange(10)) fig = plt.figure() #plt.show() #figsize 有一些重要的選項,特別是figsize,規定的是圖片保存到磁盤時具有一定大小的縱橫比。 #plt.gcf()即可得到當前Figure的引用 ax1 = fig.add_subplot(2,2,1) ax2 = fig.add_subplot(2,2,2) ax3 = fig.add_subplot(2,2,3) plt.plot(np.random.randn(50).cumsum(),'k--') #fig.add_subplot 返回的對象是AxesSubplot對象,下面調用就可以了 _ = ax1.hist(np.random.randn(100),bins = 20,color = 'k',alpha = 0.3) ax2.scatter(np.arange(30),np.arange(30) + 3 * np.random.randn(30)) plt.show() ''' #由於Figure 和 subplot是一件非常常見的任務,於是出現了更為方便的方法(plt.subplots ),它可以創建一個新的Figure, #並返回一個含有已創建的subplot對象的Numpy數組 fig,axes = plt.subplots(2,3) #print fig print axes[0][0] #axes[0][0].hist(np.random.randn(100),bins = 20,color = 'k',alpha = 0.3) plt.show() #這是非常實用的,因為可以輕松地對axes數組進行索引,就好像一個是一個二維數組一樣,例如 #axes[0,1].還可以通過sharex和sharey指定subplot具有相同的x軸和y軸。在比較相同范圍的數據時,這是 #非常實用的,否則matplotlib會自動縮放各圖表的界限。
看一下subplots的作用:
pyplot.subplots的選項還有:
上面的**fig_k可以有很多的參數,文檔中有更多的內容。
調整subplot周圍的間距
默認情況下,matplotlib會在subplot外圍留下一定的邊距,並在subplot之間留下一定的間距。間距和圖像的高度和寬度有關,會自動調整。利用Figure的subplots——adjust方法可以修改間距,因此,它是一個頂級函數。
import numpy as np import pandas as pd import matplotlib.pyplot as plt subplots_adjust(left = None,bottom = None,right = None,top = None,wspace = None,hspace = None) #wspace和space用於控制寬度和高度的百分比,可以用做subplot之間的間距,下面是個例子: ''' fig,ax = plt.subplots(2,2,sharex = True,sharey = True) for i in range(2): for j in range(2): ax[i,j].hist(np.random.randn(500),bins = 50,color = 'k',alpha = 0.5) plt.subplots_adjust(wspace = 0.5,hspace = 0.5) plt.show() #matplotlib不會檢查標簽的重疊(確實是這樣)。
# -*- encoding: UTF-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt fig,ax = plt.subplots(2,2) #cecece是白色…… ax[0,0].plot(np.arange(10),linestyle = '--',color = '#CECECE') #線上面還可以添加一些標記(marker),以強調實際的數據點。由於matplotlib創建的是連續的線形圖,因此有時可能不太容易看到真實點的位置,標記可以放到格式字符串中,但是標記類型和線性必須在顏色的后面 ax[0,1].plot(np.random.randn(30).cumsum(),'ko--') ax[1,0].plot(np.random.randn(30).cumsum(),color = 'k',linestyle = '--',marker = 'o') #在線型圖中,非實際數據點默認是按照線性插值的,可以通過drawstyle選項修改這一點。 data = np.random.randn(30).cumsum() ax[1,1].plot(data,'ko--') ax[1,1].plot(data,'k--',drawstyle = 'steps-post') plt.show()
注意上面的drawstyle選項可以規定點與點之間的連接方式,或者說是插值方式,結果為:
設置標題、軸標簽、刻度以及刻度標簽
# -*- encoding: UTF-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt import numpy.random as npr fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.plot(npr.randn(1000).cumsum()) #要想修改x的刻度,最簡單的方法就是使用set_xticks和set_xticklabels.前者告訴matplotlib將 #刻度放在數據范圍中的哪些位置,默認情況下,這些位置就是刻度標簽。但是可以使用set_xticklabels將#任何其他的值用作標簽 ticks = ax.set_xticks([0,250,500,700,900,1000]) #下面的totation是規定旋轉角度 labels = ax.set_xticklabels(['a','b','c','d','e','f'],rotation = 30,fontsize = 'small') #可以為x軸設置名稱 ax.set_xlabel('Stages') plt.show()
圖例
# -*- encoding: UTF-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt import numpy.random as npr from datetime import datetime #添加圖例 fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.plot(npr.randn(1000).cumsum(),'k',label = 'one') ax.plot(npr.randn(1000).cumsum(),'k--',label = 'two') ax.plot(npr.randn(1000).cumsum(),'k.',label = 'three') ax.legend(loc = 'best') plt.show()
注解與繪圖
# -*- encoding: UTF-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt import numpy.random as npr from datetime import datetime fig = plt.figure() ax = fig.add_subplot(1,1,1) data = pd.read_csv('E:\\spx.csv',index_col = 0,parse_dates = True) spx = data['SPX'] spx.plot(ax = ax,style = 'k-') 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) + 50), xytext = (date,spx.asof(date) + 200), arrowprops = dict(facecolor = 'black'), horizontalalignment = 'left',verticalalignment = 'top') ax.set_xlim(['1/1/2007','1/1/2011']) ax.set_ylim([600,1800]) ax.set_title('Important dates in 2008-2009 finacial crisis') plt.show() #更多關於注解的示例,請看文檔 #圖形的繪制要麻煩些,有一些常見的圖形的對象,這些對象成為塊(patch) #如Rectangle 和 Circle,完整的塊位於matplotlib.patches #要繪制圖形,需要創建一個塊對象shp,然后通過ax.add_patch(shp)將其添加到subplot中 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) plt.show()
將圖表保存到文件
# -*- encoding: UTF-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt import numpy.random as npr from datetime import datetime from io import StringIO #將圖標保存到文件 #savefig函數可以保存圖形文件,不同的擴展名保存為不同的格式 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) #注意下面的dpi(每英寸點數)和bbox_inches(可以剪除當前圖標周圍的空白部分)(確實有效) #plt.savefig('pic.jpg',dpi = 100,bbox_inches = 'tight') #不一定save到文件中,也可以寫入任何文件型對象,比如StringIO: buffer = StringIO() plt.savefig(buffer) plot_data = buffer.getvalue() #這對Web上提供動態生成的圖片是很實用的 #plt.show()
savefig的一些選項:
matplotlib配置
matplotlib的一些屬性是可以設置的,比如圖像大小、subplot邊距、配色方案、字體大小、網格類型等。有兩種方式進行操作。第一種是Python變成方式,即利用rc方法。比如:
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)
matplotlibrc是配置文件,定義好以后每次加載就會用設置的參數。
2、pandas中的繪圖函數
matplotlib是一種比較低級的工具,需要將各種組件組合好:數據展示(線型圖、柱狀圖等)、圖例、標題、刻度標簽以及注解。這是因為制作一張圖表一般需要用到多個對象。在pandas中,會省事不少。pandas能夠利用DataFrame的對象特點創建標准圖表的高級繪圖方法。作者說pandas在線文檔時最好的學習工具,書上的代碼可能過時了。
線型圖
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame s = Series(np.random.randn(10).cumsum(),index = np.arange(0,100,10)) #該Series對象的索引會被傳給matplotlib,並繪制X軸。 #可以用use_index = False 禁用該功能 s.plot(use_index = False) #X軸的刻度和界限可以通過xticks和xlim選項進行調節,Y軸通過xticks和ylim調節 plt.show() #pandas的大部分方法都有一個可選的ax參數,可以是一個subplot對象。這可以 #使在網格中更為靈活地處理subplot的位置。 #DataFrame的plot方法會在一個subplot中為各列繪制線型圖,並自動添加圖例 df = DataFrame(np.random.randn(10,4).cumsum(0), columns = ['A','B','C','D'], index = np.arange(0,100,10)) df.plot() plt.show()
下面把參數貼一下:
DataFrame還有一些對列進行處理的參數:
自下面開始就有一些專門的圖形,繪制的時候可以與R語言進行對比:http://www.cnblogs.com/batteryhp/p/4733474.html。
柱狀圖
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame #生成的線形圖中代碼加上kind = ‘bar’(垂直柱圖) 或者 (水平)kind = ‘barh’(水平柱圖) #Series和DataFrame的索引被用作X(bar)或者Y(barh)的刻度 fig,axes = plt.subplots(2,1) data = Series(np.random.randn(16),index = list('abcdefghijklmnop')) data.plot(kind = 'barh',ax = axes[0],color = 'k',alpha = 0.7) data.plot(kind = 'bar',ax = axes[1],color = 'k',alpha = 0.7) #DataFrame會按照行對數據進行分組 df = DataFrame(np.random.randn(6,4),index = ['one','two','three','four','five','six'], columns = pd.Index(['A','B','C','D'],name = 'Genus')) #注意這里的name會被用作圖例的標題,因為,這本來就是列的名字 print df df.plot(kind = 'bar') plt.show() #這里的stacked是標明畫累計柱圖 df.plot(kind = 'bar',stacked = True,alpha = 0.5) plt.show() #Series的value_counts可以用來顯示Series中各值的頻數(實驗證明) s = Series([1,2,2,3,4,4,4,5,5,5]) s.value_counts().plot(kind = 'bar') plt.show()
下面看一個例子:
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame #下面是一個例子:做一張堆積柱狀圖來顯示每天各種聚會規模的數據點百分比 tips = pd.read_csv('E:\\tips.csv') party_counts = pd.crosstab(tips.day,tips.size) print party_counts party_counts = party_counts.ix[:,2:5] #然后進行歸一化是各行和為1 party_pcts = party_counts.div(party_counts.sum(1).astype(float),axis = 0) print party_pcts party_pcts.plot(kind = 'bar',stacked = True) plt.show()
直方圖和密度圖
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame #繪制小費百分比直方圖 tips = pd.read_csv('E:\\tips.csv') tips['tip_pct'] = tips['tip'] / tips['total_bill'] #bins規定一共分多少個組 tips['tip_pct'].hist(bins = 50) plt.show() #與此相關的是密度圖:他是通過計算“可能會產生觀測數據的連續概率分布的估計” #而產生的。一般的過程將該分布金思維一組核(諸如正態之類的較為簡單的分布)。 #此時的密度圖稱為KDE圖。kind = ‘kde’即可。 tips['tip_pct'].plot(kind = 'kde') plt.show() #顯然,直方圖和密度圖經常會在一起出現 comp1 = np.random.normal(0,1,size = 200) comp2 = np.random.normal(10,2,size = 200) values = Series(np.concatenate([comp1,comp2])) print values values.hist(bins = 100,alpha = 0.3,color = 'k',normed = True) values.plot(kind = 'kde',style = 'k--') plt.show()
散布圖
散布圖(scantter plot)是觀察兩個一維數據序列之間的關系的有效手段。matplotlib中的scantter方法是繪制散布圖的主要方法。
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame #下面加載macrodata中的數據集,選擇其中幾列並計算對數差 macro = pd.read_csv('E:\\macrodata.csv') data = macro[['cpi','m1','tbilrate','unemp']] #這里的diff函數是用來計算相鄰兩數只差,對每一列,后一個數減前一個數 trans_data = np.log(data).diff().dropna() #print np.log(data).head() #print np.log(data).diff().head() print trans_data.head() plt.scatter(trans_data['m1'],trans_data['unemp']) plt.title('Changes in log %s vs. log %s'%('m1','unemp')) plt.show() #畫散布圖矩陣式很有意義的pandas提供了scantter_matrix函數來創建散步矩陣 #關於 diagonal 參數,是為了不讓對角線上的圖形(自己和自己的散布圖)顯示為一條直線而設置的關於這種數據的某些圖形顯示 #比如 diagonal = 'kde'就是畫密度圖且核為kde,若diagonal='hist',則為直方圖 pd.scatter_matrix(trans_data,diagonal = 'kde',color = 'k',alpha = 0.3) pd.scatter_matrix(trans_data,diagonal = 'hist',color = 'k',alpha = 0.3) plt.show()
繪制地圖:圖形化顯示海地地震危機數據
這是一個例子。
#-*- encoding:utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import Series,DataFrame from mpl_toolkits.basemap import Basemap #下面的例子應該是比較綜合的 data = pd.read_csv('E:\\Haiti.csv') #print data #下面處理一下數據,下面的為日期,緯度、經度 #print data[['INCIDENT DATE','LATITUDE','LONGITUDE']][:10] #print data['CATEGORY'][:6] #這些代表消息的類型 #數據中很有可能有異常值、缺失值,下面看一下 #print data.describe() #清除錯誤信息並移除缺失分類信息是“一件簡單的事情” data = data[(data.LATITUDE > 18) & (data.LATITUDE < 20) & (data.LONGITUDE > -75) & (data.LONGITUDE < -70) & data.CATEGORY.notnull()] #我們想根據分類對數據做一些分析或者圖形化工作,但是各個分類字段中可能含有多個分類。此外,各個分類信息 #不僅有一個編碼,還有一個英語(法語)名稱。因此需要對數據進行規整化處理。下面編寫兩(三)個 #函數,一個用於獲取所有分類的列表,一個用於將各個分類信息拆分為編碼和英語明名稱 #sptrip 是刪除空白字符,'\n'等;注意作者這種隱式循環寫法 def to_cat_list(catstr): stripped = (x.strip() for x in catstr.split(',')) return [x for x in stripped if x] def get_all_categoties(cat_series): cat_sets = (set(to_cat_list(x)) for x in cat_series) return sorted(set.union(*cat_sets)) def get_english(cat): code,names = cat.split('.') if '|' in names: names = names.split('|')[1] return code,names.strip() #下面進行一下ceshi #print get_english('2.Urgences logistiques | Vital Lines') #接下來做了一個將編碼跟名稱映射起來的字典,這是因為我們等會要用編碼進行分析。 #下面將所有組合弄出來 all_cats = get_all_categoties(data.CATEGORY) #print data.CATEGORY[:10] #print all_cats #生成器表達式 #生成字典 english_mapping = dict(get_english(x) for x in all_cats) #print english_mapping['2a'] #print english_mapping['6c'] #根據分類選取記錄的方式有很多,其中之一就是添加指標(或者啞變量)列,每個分類一列。 #為此,首先抽取出唯一的分類編碼,並構造一個全零DataFrame(列為分類編碼,索引跟data的索引一樣) def get_code(seq): return [x.split('.')[0] for x in seq if x] #下面是將所有的key取出來 all_codes = get_code(all_cats) #print all_codes code_index = pd.Index(np.unique(all_codes)) #print code_index dummy_frame = DataFrame(np.zeros((len(data),len(code_index))),index = data.index,columns = code_index) #print len(data) #print dummy_frame.ix[:,:6] #下面將各行中適當的項設置為1,然后再與data進行連接: for row,cat in zip(data.index,data.CATEGORY): codes = get_code(to_cat_list(cat)) dummy_frame.ix[row,codes] = 1 #添加前綴,並且合並一下 data = data.join(dummy_frame.add_prefix('category_')) #print data #接下來開始畫圖吧,我們希望把數據繪制在海地的地圖上。basemap數據集是matplotloib的一個插件 #使得能夠用Python在地圖上繪制2D數據。basemap提供了許多不同的地球投影以及一種將地球上的經緯度 #坐標投影轉換為二維matplotlib圖的方式。 #“經過一遍又一遍的嘗試”,作者編寫了下面的函數,繪制出一張簡單的黑白地圖。 def basic_haiti_map(ax = None,lllat = 17.25,urlat = 20.25,lllon = -75,urlon = -71): #創建極球面投影的Basemap實例。 m = Basemap(ax = ax,projection = 'stere', lon_0 = (urlon + lllon) / 2, lat_0 = (urlat + lllat) / 2, llcrnrlat = lllat,urcrnrlat = urlat, llcrnrlon = lllon,urcrnrlon = urlon, resolution = 'f' )
由於window下安裝geos不成功,這部分等ubuntu裝好了再接着寫。
4、Python圖形化工具生態系統
介紹幾個其他的繪圖工具。
Chaco
特點:靜態圖 + 交互圖形,非常適合用復雜的圖形化方法表示數據的內部關系。對交互支持的好的多,交互式GUI是個不錯選擇。
mayavi
這是一個基於開源C++圖形庫VTK的3D圖形工具包。可以集成到Ipython交互使用。
其他庫
其他庫或者應用還有:PyQwt、Veusz、gnuplotpy、biggles等,大部庫都在向基於Web的技術發展,並逐漸遠離桌面圖形技術。
圖形化工具的未來
基於Web技術(如Javascript)的圖形化是必然的發展趨勢,現在已經有不少了,higncharts等。