本節提要:圖例 Legend與colorbar
一、圖例Legend命令常用參數
作為成熟的科研圖表,圖例的重要性是不言而喻的。所謂一圖敵千言,在氣象科研領域,圖表是進行數據可視化的利器,而圖例是幫助閱讀者理解圖表信息的關鍵。繪圖庫matplotlib中專門闢出一個命令——Legend進行設置。下面首先介紹其常用關鍵字參數。locax2.legend((bar1,bar2,line1,line2),('降水','蒸發','墒情','氣溫'),frameon=True,framealpha=1,ncol=1,shadow=True,bbox_to_anchor=(0,0))可以看出,將絕對位置定為bbox_to_anchor=(0,0)後,圖例可以被放置在子圖外了。同樣的,可以放置到我們常見的圖例位置:
ax2.legend((bar1,bar2,line1,line2),('降水','蒸發','墒情','氣溫'),frameon=True,framealpha=1,ncol=1,shadow=True,bbox_to_anchor=(1.1,0.9))注意,兩個命令並不是衝突的,可以放在同一句中調節,不會報錯。
還可以進行如下操作,bbox_to_anchor=(x1,y1,x2,y2),給予圖例框的起始絕對位置和結束絕對位置:ax2.legend((bar1,bar2,line1,line2),('降水','蒸發','墒情','氣溫'),bbox_to_anchor=(0,-0.1,1.,-0.10),frameon=True,framealpha=1,ncol=2,shadow=True,mode="expand", borderaxespad=0.)在mode=『expand』命令下,指定了起始和結束位置後,圖例框將被拉伸到最大,我目前沒有用到,可能有讀者需要。在前面,我們將每個圖例分別注釋了標籤,在需要的時候,還可以進行分類操作。大多數時候,我們通過最簡便的方法建立一個實驗圖(直接在繪製時設置label=,legend會自動生成圖例):
line1=plt.plot(x,y1,lw=2,ls="-",color='cyan',label='line1')line2=plt.plot(x,y2,lw=2,ls='--',color='k',label='line2')line3=plt.plot(x,y3,lw=2,ls=':',color='lightgreen',label='line3')scatter1=plt.scatter(x2,y4,c='orange',s=15,marker='*',label='scatter1')scatter2=plt.scatter(x2,y5,c='darkgray',s=19,marker='1',label='scatter2')scatter3=plt.scatter(x2,y6,c='darkturquoise',s=19,marker='h',label='scatter3')plt.title('這是總標題')plt.legend(title='這是圖例標題',bbox_to_anchor=(1,0.9),frameon=False,framealpha=0.75)這種建立圖例的方法不能進行分類操作,所以通過在plt.legend(list1,list2)的方式建立圖例,一般來說list1代表繪製命令,list2裝載字符串作為名稱:
plt.legend([line1,line2,line3,scatter1,scatter2,scatter3],['line1','line2','line3','scatter1','scatter2','scatter3'])plt.legend([(line1,line2,line3),scatter1,scatter2,scatter3],['這是合併的線','scatter1','scatter2','scatter3']可以發現,雖然合併了,但是合併的圖例裡只有一根綠線了,這時需要引進新的模塊:
from matplotlib.legend_handler import HandlerLine2D, HandlerTupleplt.legend([(line1,line2,line3),scatter1,scatter2,scatter3],['這是合併的線','scatter1','scatter2','scatter3'], handler_map={tuple: HandlerTuple(ndivide=None)},numpoints=1, title='這是圖例標題',bbox_to_anchor=(1,0.9),frameon=False,framealpha=0.75)這之後,合併的圖例能正常顯示了。當然散點圖也能進行分類處理:
在matplotlib中,由於legend命令的特性,無論plt.legend還是ax.legend,都只能在圖表中添加一個圖例,一般來說以最後一個legend命令繪製,前面都會被覆蓋,比如:
三個命令,最終只能出現(***)這個圖例。但是科研圖表存在需要多個圖例的情況,如果確實需要繪製時,可以通過ax.add_artist()命令添加。仍然以上一小節的圖為例。首先,我們將所有line通過ax.legend()命令繪製出來:ax.legend([line1,line2,line3],['直線1','直線2','直線3'],numpoints=1,title='圖例一',bbox_to_anchor=(1,0.9),frameon=False,framealpha=0.75)然後,from matplotlib.legend import Legend模塊導入,將其他散點和直方在Legend命令下添加,Legend()內部關鍵字參數與ax.legend()的關鍵字參數一致,最後,以ax.add_artist()添加到子圖上:
from matplotlib.legend import Legendlegend2=Legend(ax,[scatter1,scatter2,scatter3,bar1],['散點1','散點2','散點3','直方1'],title='圖例二',frameon=False,bbox_to_anchor=(1,0.3))ax.add_artist(legend2)當然,你還可以增添三個乃至更多的圖例,讀者可自行推用。
在前面的推送中,介紹到散點圖的兩種使用方法:一種為以s為變量,固定顏色,通過散點直徑大小展示數據;一種是以顏色映射為變量,固定s,通過填色變化來展示數據。這兩種是最簡單的使用方式,進一步的,散點圖還可以將兩個都設置為變量,都展示數據的變化。filename=r'C:\Users\lenovo\Desktop\累年降水數據.xlsx'df=pd.read_excel(filename)lon=df['lon']#讀入站點經度lat=df['lat']#讀入站點緯度rain_days=df['rain_days']#讀入各站點某年累計降水日數rains=df['precipitation']#讀入各站點某年累計降水量rain_size=(rain_days-10)**2#對累計降水日數進行處理,使散點大小均勻然後繪製散點圖(添加地圖和站點名通過def create_map():繪製,這裡略去):sca=ax.scatter(lon,lat,s=rain_size,c=rains,cmap='GnBu',alpha=0.75,edgecolor='k',label='none')將處理過的累計降水日數傳入s,將累計降水量傳給color,設定cmap為'GnBu'。注意,最好能改變alpha小於1,因為散點存在互相重疊情況,不使散點透明,小散點可能被大散點完全覆蓋。edgecolor設為黑色在視覺上是最好的。當然,目前缺乏重要的輔助圖例,除了製圖員,沒人知道這幅圖表達了什麼,所以接下來,介紹兩種添加輔助閱讀工具手段。
A、通過添加圖例表示圓圈大小含義、通過添加colorbar表示填色的含義position=fig.add_axes([0.1,0.2,0.8,0.01])b=plt.colorbar(ax=sca,cax=position,extend='both',orientation='horizontal',shrink=0.3,label='累計降水量 $mm$',pad=0.1)這樣就能展示填色的意義了,閱讀者能明白填色代表累計降水量,然後,通過如下命令生成圖例:
marker1=ax.scatter([],[],s=rain_size.min(),c='k',alpha=0.3)marker2=ax.scatter([],[],s=rain_size.max()/2,c='k',alpha=0.3)marker3=ax.scatter([],[],s=rain_size.max(),c='k',alpha=0.3)legend_markers=[marker1,marker2,marker3]labels=['30日','60日','90日']ax.legend(title='累計降水日',handles=legend_markers, labels=labels,scatterpoints=1,frameon=False, labelspacing=0.39,handletextpad=3, bbox_to_anchor=(0.3,0.93))前面三句就是生成三個圓圈的命令,因為通過兩個空列表生成的,所以不會顯示在主圖上,只能通過句柄命令(handles)傳入ax.legend(),這時你可以理解為等同於(為了方便理解如此解釋):ax.legend([line1,line2,line3],['直線1','直線2','直線3'])前一部分為圖例,後一部分為標籤。
前面的程序與A中完全相同,在第四節中已經講了如何建立多個子圖,這裡馬上就上手使用了,這次不使用colorbar展示顏色變化,而使用帶顏色的散點:from matplotlib.lines import Line2Dcmap=plt.get_cmap('GnBu')levels=[40,60,80,100,120,140,160,180]ls = [Line2D(range(1), range(1), linewidth=0, color=cmap(v), marker='o', ms=(10)) for v in levels]ax.legend(ls,levels,frameon=False,bbox_to_anchor=(1.02,0.5),title='累計降水量($mm$)')第一步獲取cmap,這裡和主圖的cmap一致。第二步劃分降水level。第三步生成我們的彩色圓點,這些彩色圓點實際上是帶著marker的plot線條,但是我們將其linewidth設為了0,線條被截去了。第四步傳入legend命令。接著,使用前面提到的添加第二個圖例的方法,添加散點直徑圖例:
marker1=ax.scatter([],[],s=rain_size.min(),c='k',alpha=0.3)marker2=ax.scatter([],[],s=rain_size.max()/2,c='k',alpha=0.3)marker3=ax.scatter([],[],s=rain_size.max(),c='k',alpha=0.3)legend_markers=[marker1,marker2,marker3]labels=['30日','60日','90日']legend2=Legend(ax,title='累計降水日',handles=legend_markers, labels=labels,scatterpoints=1,frameon=False, labelspacing=0.39,handletextpad=3, bbox_to_anchor=(1.1,0.98))ax.add_artist(legend2)前面部分與方法A中的一致,但是我們已經在前面用ax.legend()命令繪製了一個圖例了,這時就只能用ax.add_artist(legend2)方法添加新的圖例。通過這幅圖能看出什麼呢?可以看出恩施州降水日數和降水量高值區都集中在利川市,而鶴峰的日數和降水量都偏少。再看宣恩縣和恩施市,宣恩的降水日更少,但是降水量比恩施市多。