本節提要:關於colormap與colorbar的一站式教程。
章節引言:
在matplotlib和cartopy中,其常見的繪圖命令,若是帶有顏色映射的collection(s)類,則基本都可以引入cmap與colorbar功能來分析數據。cmap即是顏色映射表,colorbar即是顏色分析色條,前者只起到對繪圖對象上色的功能,後者實現色階與數值的對應。
常見的繪圖命令scatter、contour、contourf、pcolormesh等都可以引入cmap與colorbar,下面四幅圖分別使用了前述四種繪圖命令繪製,並更改了每一幅圖使用的顏色映射表:
cmap的引入
作為一個專門的數據可視化庫包,matplotlib專門開闢了一個cm功能來供繪圖者使用,如果需要使用一個顏色映射表,你可以使用get語句獲取該顏色映射表:
importmatplotlib as mpl colormap=mpl.cm.get_cmap(' Reds ' ) ax.contourf(cmap=colormap)你也可以在cm後直接加上顏色映射表名稱,而不是字符串,這兩種方法在此處是等效的:
當然,這種get命令的在此處顯得繁瑣了,更簡便的方法是徑直將代表該顏色映射表的字符串直接傳入繪圖命令中的cmap中:
ax.contourf(cmap='Reds ')使用顏色映射表時不必要記住全部的代表字符串,我們可以在使用的時候去官網查找後使用。
Matplotlib的默認cmap是viridis。
翻轉cmap的顏色順序
由於cmap的顏色映射表是有固定存儲順序的數組,所以我們可以在需要的時候翻轉cmap與數值的對應順序,翻轉命令為在顏色映射表的字符串最後加上』_r』。
ax1.contourf(X,Y,Z,cmap='Reds') ax2.contourf(X,Y,Z,cmap='Reds_r')cmap的分類
matplotlib專門提供了多樣的顏色映射表,他們在官網Tutorials下的Choosing Colormaps in Matplotlib說明書有詳細講解。
按照官網的說明,colormap簡要有以下六類:
1. 連續類(Sequential):色彩的亮度和飽和度遞增變化,用單一色調展示有序的信息。Matplotlib的默認cmap——『viridis』,即屬於這一類。這類視覺衝擊性較小,在梯度劃分較細時,連續臨近的色階在一定的時候可能出現混淆的情況。
2. 分色類(Diverging):不同顏色的亮度和飽和度逐漸變化。主要用於展示關於0對稱的數據。
3. 循環類(Cyclic):兩種不同顏色的亮度逐漸變化,在色彩映射的中間位置以及開始(或結束)端以非飽和色結束。一般應用於在端點處循環的值。
4. 定性類(Qualitative):將各個顏色無序拼接的顏色映射表。用於展示無序的信息。
5. 混雜類(Miscellaneous):多種對比明顯的各種顏色拼接在一起,一般用於展示信息中的細節變化。
以下為matplotlib官網提供的各種顏色映射表:
這裡推薦幾種比較實用的cmap:
GnBu、gray、cool、bwr、RdBu、tab20c
其中GnBu、RdBu、tab20c比較溫和,cool、bwr視覺衝擊性強。
引入外部cmap
由於matplotlib提供的顏色映射表是有限的,所以我們還需要藉助外部的庫包提供額外的顏色映射表。大氣科學與海洋科學常用的兩個外部顏色庫包為Palettable與cmaps,這兩個庫包都可以使用conda命令安裝。Palettable作為經典顏色庫包在很多地方都有使用;cmaps庫包是將NCL平臺的顏色移植到python平臺,嘉惠學林。
下面是cmaps一些顏色映射表舉例:
更多的顏色映射表名稱請至官網查詢。
在使用cmaps時,只需引入庫包,然後填寫顏色名,引入後可以視為正常的matplotlib內部colormap對待與實用:
import cmaps cmap=cmaps.MPL_RdYlGn ax.contourf(cmap=cmap)截取cmap
在日常使用時,一條colormap我們很可能只需要其中一部分,這時候就需要進行cmap的截取工作,前面已經提到了作為一連串的顏色列表合併的數組是cmap的本質,所以我們很容易想到通過切片的方式來截取cmap,通過切片,我們將jet_r顏色條中的暖色調全部截去不用。
import numpy asnp importmatplotlib as mpl importmatplotlib.pyplot as plt frommatplotlib.colors import ListedColormap cmap=mpl.cm.jet_r#獲取色條 newcolors=cmap(np.linspace(0,1,256))#分片操作 newcmap=ListedColormap(newcolors[125:])#切片取捨 fig=plt.figure(figsize=(1.5,0.3),dpi=500) ax1=fig.add_axes([0,0,1,0.45]) ax2=fig.add_axes([0,0.5,1,0.45]) norm =mpl.colors.Normalize(vmin=0, vmax=10) fc1=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap='jet_r'), cax=ax1, orientation='horizontal', extend='both') fc2=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=newcmap), cax=ax2, orientation='horizontal', extend='both') for i in[fc1,fc2]: i.ax.tick_params(labelsize=3,width=0.5,length=0.5) i.outline.set_linewidth(0.5)拼接cmap
既然能夠通過切片的方式截取cmap,那麼自然反向的我們可以通過組合的方式拼接cmap。
import numpy asnp importmatplotlib as mpl importmatplotlib.pyplot as plt frommatplotlib.colors import ListedColormap import cmaps plt.rcParams['font.sans-serif']=['FangSong'] cmap1=cmaps.spread_15lev_r cmap2=cmaps.sunshine_diff_12lev list_cmap1=cmap1(np.linspace(0,1,15)) list_cmap2=cmap2(np.linspace(0,1,12)) new_color_list=np.vstack((list_cmap1,list_cmap2)) new_cmap=ListedColormap(new_color_list,name='new_cmap ') fig=plt.figure(figsize=(1.5,0.5),dpi=500) ax1=fig.add_axes([0,0,1,0.33]) ax2=fig.add_axes([0,0.33,1,0.33]) ax3=fig.add_axes([0,0.66,1,0.33]) norm =mpl.colors.Normalize(vmin=0, vmax=10) fc1=fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap1),cax=ax1, orientation='horizontal',extend='both') fc2=fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap2),cax=ax2, orientation='horizontal',extend='both') fc3=fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=new_cmap),cax=ax3, orientation='horizontal',extend='both')colorbar基礎
colorbar是在有顏色映射的繪圖命令中使用的一種表徵顏色與數值的對應關係的特殊圖形。與Legend圖例命令不同,matplotlib允許使用者在不使用其他功能的情況下,無限次的添加colorbar。
colorbar的引入既可以是有源的,也可以是無源的。此處的有源無源,針對的是colorbar與子圖繪圖命令的關聯性。
colorbar的添加可以使用如下命令:
importmatplotlib.pyplot as plt fig=plt.figure() fig.colorbar()或
有源colorbar引入
有源colorbar指直接與當前要添加色條的子圖的繪圖命令相關聯的添加方式,具體來說,就是縮寫代稱該繪圖命令,然後作為映射源傳入生成colorbar命令中,如:
CS=ax1.contourf(X,Y,Z,cmap=cmaps.sunshine_diff_12lev) cb=fig.colorbar(CS,shrink=1,ax=ax1) CS2=ax2.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb2=fig.colorbar(CS2,shrink=1,ax=ax2)這裡的兩張圖形都是有源的colorbar,其中在子圖1中,我們稱CS、CS2為我們生成的colorbar的源頭。通過傳入CS、CS2代表的contourfcollections,我們比較簡便的生成了colorbar。
無源colorbar引入
無源colorbar主要是指不使用子圖中的繪圖命令的關聯性,由使用者通過定義norm、cmap等參數,生成一個與子圖沒有直接映射關係的colorbar,如:
CS=ax1.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb=fig.colorbar(CS,ax=ax1) ax2.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cmap=cmaps.MPL_RdYlGn bounds=np.arange(-2.0,2.5,0.5) norm =mpl.colors.BoundaryNorm(bounds,cmap.N) cb2=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), ax=ax2)通過使用者聲明的norm與cmap,我們獲得了一個與有源colorbar一模一樣的colorbar。這裡固然不如直接傳入有源的辦法簡捷,但是在後期某些高級定製時,是比較有用的。
Colorbar重要參數列舉
colorbar作為一個繪製圖形命令,自身必定攜帶多樣的修飾參數。下面簡答列舉各參數:
1. mappable(可映射源)
該參數即需要繪製的映射圖像源頭,一般默認當前子圖的繪圖命令,如上節提到的CS,或者mpl.cm.ScalarMappable自動生成的映射源。對於fig.colorbar命令來說,該參數是必要的,但是plt.colorbar命令在使用時如果沒有填寫顯性的源頭,程序會自動確認當前子圖可映射項。
2. ax(colorbar擺放的子圖位置)
該參數控制繪製的colorbar擺放在某個子圖旁邊,默認為當前子圖。可以傳入單獨的一個子圖,也可以傳入一個子圖的列表。
3. cax(colorbar擺放的子圖位置)
該參數設定後,擁有最高優先級,將覆蓋shrink 、ax、aspect等參數,colorbar將放置在指定位置,如:
cax=fig.add_axes([0.33,0,0.4,0.05]) CS=ax1.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb=fig.colorbar(CS,shrink=1,cax=cax,orientation='horizontal') ax2.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn)4. orientation(axes特性,colorbar擺放的橫豎位置)
該參數控制colorbar的橫豎位置。默認為豎向。其中豎向命令為vertical,橫向命令為horizontal,如3中圖片所示。
5. shrink(axes特性,colorbar的收縮比例)
該參數控制colorbar的收縮比例,在收縮時,colorbar長寬都會變化,如:
CS=ax1.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb=fig.colorbar(CS,shrink=0.5,ax=ax1) CS2=ax2.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb2=fig.colorbar(CS,shrink=1.25,ax=ax2)6. aspect(axes特性,colorbar的長寬比例)
該參數控制colorbar長寬的比例,如:
7. pad(axes特性,colorbar與子圖距離)
該參數控制colorbar與子圖的間距,如:
8. extend(colorbar特性,colorbar兩端擴充)
該參數可以控制colorbar兩端是否允許擴充,擴充後生成尖角,可選擇的變量有'neither','both', 'min', 'max'。需要注意,該參數不僅是生成尖角,填色亦會出現變化,如:
9. extendfrac(colorbar特性,colorbar兩端擴充的相對長度)
該參數控制衍生的小三角與colorbar主體的相對長度,如:
10. extendrect(colorbar特性,colorbar兩端擴充的相對形狀)
該參數控制兩端衍生的小三角的形狀,擁有True、False兩個可選變量,False時兩端為小三角,True時兩端為矩形,如:
11. spacing(colorbar特性,colorbar填色間隔)
該參數控制colorbar的填色間隔是否與數值間隔相關,擁有uniform, proportional兩個可選變量。其中uniform表示無論數值間隔是否相等,所有填色長度均勻相等,這是默認變量。proportional表示若數值間隔有大有小,則填色長度亦隨之變化,數值間隔越大,填色長度也越長,反之相反,如:
12. ticks(colorbar特性,colorbar刻度間隔)
該參數控制顏色條的刻度顯示。
13. format(colorbar特性,colorbar刻度單位制)
該參數控制colorbar顯示的刻度值的單位,如:
14. drawedges(colorbar特性,colorbar是否在邊界上劃線)
該參數控制是否在每個顏色交界處畫線,布爾值控制,如:
15. label(colorbar特性,colorbar的label)
該參數允許傳入一個字符串,將作為colorbar的標籤。
Colorbar的axes特性
在生成腳本中我們可知,colorbar是必然要生成一個axes來作為寄生軸的,所以colobar可以調用一些子圖的特性,調用方式如下:
CS=ax1.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb=fig.colorbar(CS,shrink=1,ax=ax1) cb.ax在調用了子圖屬性以後,我們可以使用子圖的屬性來進一步修飾colorbar,如:
def make_colorbar(cb): cb.ax.tick_params(labelsize=4,length=0.5,width=0.5) cb.outline.set_color('none') cb=fig.colorbar(CS,shrink=1,ax=ax1) cb2=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), ax=ax2) for cb in[cb,cb2]: make_colorbar(cb)我們還可以使用在前面提到的刻度生成器的方式修飾colorbar,如:
CS=ax1.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cb=fig.colorbar(CS,shrink=1,ax=ax1) cb.ax.yaxis.set_minor_locator(mticker.MultipleLocator(0.25)) ax2.contourf(X,Y,Z,cmap=cmaps.MPL_RdYlGn) cmap=cmaps.MPL_RdYlGn bounds=np.arange(-2.5,2.5,0.5) norm =mpl.colors.BoundaryNorm(bounds,cmap.N) cb2=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), ax=ax2)不同特色的colorbar繪製技巧
在某些時候,程序自動生成的colorbar在修飾後,仍然不能滿足我們的需求,這時,我們就會利用一些手段,製造各種不同特色的colorbar,下面我們介紹幾種常見異形colorbar的繪製方法。
1. 環狀colorbar
環狀colorbar表現在其色條不為直線型,在目前matplotlib的框架下,不能利用現有的colorbar參數修改為類似的形狀。這時,我們只能利用其它的方式仿製出colorbar
這裡我們提供兩種方式:第一,使用極坐標系下的柱形圖方法;第二,使用楔形形狀命令繪製圓環。
1.1極坐標系下的柱形圖繪製方法
由於極坐標系可以繪製柱狀圖,而柱狀圖又可以通過facecolor參數實現上色,所以可以用到環狀colorbar的繪製中來,如:
import numpy asnp import xarray asxr importmatplotlib.pyplot as plt importcartopy.crs as ccrs importcartopy.feature as cfeature importcartopy.io.shapereader as shpreader importmatplotlib.path as mpath importmatplotlib as mpl fromcartopy.util import add_cyclic_point from matplotlibimport cm,colors fromcartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER plt.rcParams['font.sans-serif']=['FangSong']plt.rcParams['axes.unicode_minus']=Falsefile=r'E:\aaaa\datanc\2019.6.23\fnl_20190620_00_00.grib2' data=xr.open_dataset(file,engine='cfgrib', backend_kwargs={'filter_by_keys':{'typeOfLevel': 'surface'}}) data['t']-273.15 data['latitude'] data['longitude'] cycle_t,cycle_lon = add_cyclic_point(data['t']-273.15, coord=data['longitude']) cycle_LON,cycle_LAT = np.meshgrid(cycle_lon,data['latitude']) fig=plt.figure(figsize=(2,2),dpi=700) ax1=fig.add_axes([0,0,1,1],polar=True) ax1.set_axis_off() ax2=fig.add_axes([0.127,0.123,0.75,0.75], projection=ccrs.SouthPolarStereo()) ax2.add_feature(cfeature.LAND) ax2.add_feature(cfeature.OCEAN) ax2.add_feature(cfeature.COASTLINE,lw=0.75) theta =np.linspace(0, 2*np.pi, 100) center, radius =[0.5, 0.5], 0.5 verts =np.vstack([np.sin(theta), np.cos(theta)]).T circle =mpath.Path(verts * radius + center) ax2.set_boundary(circle,transform=ax2.transAxes) cs=ax2.contourf(cycle_LON,cycle_LAT, cycle_t, levels=np.arange(60,60,10), cmap='RdBu_r',transform=ccrs.PlateCarree()) cs.levels cmap=cm.get_cmap('RdBu_r',len(cs.levels)-1) angle=np.arange(0,0.5*np.pi,0.5*np.pi/(len(cs.levels)-1)) radius=np.array([2,2,2,2,2,2,2,2,2,2,2]) cmaps=cmap(range(len(cs.levels)-1)) ax1.set_theta_offset(np.pi/2) ax1.bar(angle,radius,width=0.5*np.pi/11,color=cmaps,align='center') for i,x,y inzip(cs.levels,angle,radius): ax1.text(x-0.1,y+0.17,i,fontsize=3) ax1.text(0.9,2.6,'氣溫:℃',fontsize=4)1.2楔形圖形繪製環狀colorbar
利用matplotlib.patches中的楔形圖形命令Wedge,在循環迭代的方式下,添加一個環狀的colorbar。
import numpy asnp import xarray asxr importmatplotlib.pyplot as plt importcartopy.crs as ccrs importcartopy.feature as cfeature importcartopy.io.shapereader as shpreader importmatplotlib.path as mpath importmatplotlib as mpl fromcartopy.util import add_cyclic_point from matplotlibimport cm,colors fromcartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER frommatplotlib.patches import Wedge plt.rcParams['font.sans-serif']=['FangSong'] plt.rcParams['axes.unicode_minus']=False file=r'E:\aaaa\datanc\2019.6.23特大暴雨\fnl_20190620_00_00.grib2' data=xr.open_dataset(file,engine='cfgrib', backend_kwargs={'filter_by_keys':{'typeOfLevel': 'surface'}}) data['t']-273.15 data['latitude'] data['longitude'] cycle_t,cycle_lon = add_cyclic_point(data['t']-273.15, coord=data['longitude']) cycle_LON,cycle_LAT = np.meshgrid(cycle_lon,data['latitude']) fig=plt.figure(figsize=(2,2),dpi=700) ax=fig.add_axes([0,0,1,1],projection=ccrs.PlateCarree()) ax.add_feature(cfeature.LAND) ax.add_feature(cfeature.OCEAN) ax.add_feature(cfeature.COASTLINE,lw=0.3) ax.spines['geo'].set_linewidth(0.5) cs=ax.contourf(cycle_LON,cycle_LAT, cycle_t, levels=np.arange(-60,60,10),cmap='RdBu_r', transform=ccrs.PlateCarree(),extend='both') cmap=cm.get_cmap('RdBu_r',len(cs.levels)-1) cmaps=cmap(range(len(cs.levels)-1)) ax2=fig.add_axes([0.05,0.3,0.2,0.2]) delta=360/(len(cs.levels)-1) for c,p inzip(cmaps,range(len(cs.levels)-1)): wedge=Wedge((0.5,0.5),0.4,delta*p,delta*(p+1),facecolor=c, edgecolor='k',width=0.1,linewidth=0.1) ax2.add_patch(wedge) ax2.axis('off')2. 兩端三角形與色條主體分離的colorbar
import numpy asnp import xarray asxr importmatplotlib.pyplot as plt importmatplotlib.path as mpath importmatplotlib as mpl from matplotlibimport cm,colors from matplotlib.patchesimport Rectangle importcartopy.crs as ccrs fromcartopy.util import add_cyclic_point fromcartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER importcartopy.feature as cf importcartopy.io.shapereader as shpreader plt.rcParams['font.sans-serif']=['SimHei'] file=r'E:\aaaa\datanc\2019.6.23\fnl_20190620_00_00.grib2' data=xr.open_dataset(file,engine='cfgrib', backend_kwargs={'filter_by_keys':{'typeOfLevel': 'surface'}})t=data['t']-278.15 lat=data['latitude'] lon=data['longitude'] proj=ccrs.LambertConformal(central_longitude=105,central_latitude=35) reader= shpreader.Reader( r'F:\B\china.shp') provinces=cf.ShapelyFeature(reader.geometries(), crs=ccrs.PlateCarree(), edgecolor='k', facecolor='none', alpha=1,lw=0.5) extent=[80,130,10,60] fig=plt.figure(figsize=(1.5,1.5),dpi=800) ax=fig.add_axes([0,0.1,1,0.85],projection=proj) ax.add_feature(cf.COASTLINE.with_scale('50m'),lw=0.25) ax.add_feature(cf.OCEAN.with_scale('50m'),lw=0.5) ax.add_feature(cf.LAND.with_scale('50m'),lw=0.5,edgecolor='none') ax.add_feature(cf.RIVERS.with_scale('50m'),lw=0.25) ax.add_feature(provinces,linewidth=0.3) ax.add_geometries(shpreader.Reader(r'F:\B\nine.shp').geometries(), crs=ccrs.PlateCarree(),edgecolor='k', facecolor='k',alpha=1,lw=0.5) ax.spines['geo'].set_linewidth(0.25) ax.set_extent(extent) ax2=fig.add_axes([0.054,0,0.892,0.1]) ax2.tick_params(axis='both',which='major', direction='in',width=0.5, length=0.5,labelsize=3) for i in ['top','bottom','left','right']: ax2.spines[i].set_linewidth(0.5) ac=ax.contourf(lon,lat,t,cmap='Spectral_r', levels=np.arange(-20,40,2), transform=ccrs.PlateCarree()) ax.spines['geo'].set_linewidth(0.5) ax2.set(xlim=(-3,30),ylim=(0,1)) num=ac.levels colormap=cm.get_cmap('Spectral_r',len(num)-1) cmaps=colormap(range(len(num)-1)) camps=cmaps.tolist() for i,x,y,colorin zip(range(len(num)-2), range(len(num)-2), [0.25]*(len(num)-2), cmaps[1:-1]): rectangle=Rectangle([x,y],1,0.5, facecolor=color, alpha=1, edgecolor='k', linewidth=0.02) ax2.add_patch(rectangle) left=plt.Polygon(xy=[[-0.75,0.31],[-0.75, 0.69], [-2.5,0.5]], facecolor=cmaps[0],edgecolor='k',linewidth=0.02) right=plt.Polygon(xy=[[27.75,0.31],[27.75,0.69],[29.5,0.5]], facecolor=cmaps[-1],edgecolor='k',linewidth=0.02) ax2.add_patch(left) ax2.add_patch(right) for s in['top','bottom','left','right']: ax2.spines[s].set_linewidth(0.5) ax2.axis('off')3. 雙刻度列colorbar
使colorbar擁有兩條ticks,用於表現不一樣的量度。
import numpy asnp importmatplotlib as mpl importmatplotlib.pyplot as plt importmatplotlib.colors as mcolors plt.rcParams['font.sans-serif']=['SimHei'] fig=plt.figure(figsize=(1.5,0.2),dpi=500) ax=fig.add_axes([0,0,1,0.5]) colorlevel=[0.1,10.0,25.0,50.0,100.0,250.0,500.0]colordict=['#A6F28F','#3DBA3D','#61BBFF','#0000FF','#FA00FA','#800040']cmap=mcolors.ListedColormap(colordict)norm=mcolors.BoundaryNorm(colorlevel,cmap.N)fc=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), cax=ax,orientation='horizontal',extend='both') fc.ax.tick_params(which='major',labelsize=3, direction='out',width=0.5,length=1) fc.outline.set_linewidth(0.3) ax2=fc.axax2.xaxis.set_ticks_position('top')ax2.tick_params(labelsize=3,top=True,width=0.5,length=1)式,並使上有刻度 ax3=ax2.secondary_xaxis('bottom') ax3.tick_params(labelsize=3,width=0.5,length=1) ax3.spines['bottom'].set_bounds(0.1,500)ax3.set_xticks([40,120,210,290,380,460]) ax3.set_xticklabels(['小雨','中雨','大雨','暴雨','大暴雨','特大暴雨']) ax3.spines['bottom'].set_linewidth(0.3)4. 刻度列與colorbar主體分離
在使用中,實現刻度列於colorbar主體分離的視覺效果。
import numpy asnp importmatplotlib as mpl importmatplotlib.pyplot as plt importmatplotlib.colors as mcolors plt.rcParams['font.sans-serif']=['SimHei'] fig=plt.figure(figsize=(1.5,0.2),dpi=500) ax=fig.add_axes([0,0,1,0.5]) cmap =mpl.cm.cool norm = mpl.colors.Normalize(vmin=5,vmax=10) fc=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), cax=ax, orientation='horizontal') fc.ax.tick_params(which='major',labelsize=3, direction='out',width=0.5,length=1) fc.outline.set_linewidth(0.3) ax2=fc.axax2.xaxis.set_ticks_position('top')ax2.tick_params(labelsize=3,top=True,width=0.5,length=1) ax3=ax2.secondary_xaxis('bottom')ax3.tick_params(labelsize=3,width=0.5,length=3) ax3.spines['bottom'].set_bounds(5,10)ax3.set_xticks([5,10]) ax3.set_xticklabels(['0.1mm','500mm'])ax3.spines['bottom'].set_linewidth(0.3)labels=fc.ax.get_xticklabels() ax2.axis('off') fc.outline.set_color('none') ax3.spines['bottom'].set_position(('outward',10))5.使用Legend命令生成仿colorbar
在繪製圖片時,我們還可以使用Legend命令來生成仿colorbar,在一些氣象預報降雨量圖和文獻數值範圍參考比較常見。在levels較少的情況下,且已經知道全部顏色色號,可以手動添加圖例,如:
larger1=mpatches.Rectangle((0,0), 1, 1, facecolor="#A6F28F") larger2=mpatches.Rectangle((0,0), 1, 1, facecolor="#3DBA3D") larger3=mpatches.Rectangle((0,0), 1, 1, facecolor="#61BBFF") larger4=mpatches.Rectangle((0,0), 1, 1, facecolor="#0000FF") larger5=mpatches.Rectangle((0,0), 1, 1, facecolor="#FA00FA") labels=['0-10','10-25','25-50','50-100','100-250'] ax.legend([larger1,larger2,larger3,larger4,larger5],labels, fontsize=12,frameon=False, title='圖例(mm)',facecolor='none', loc='lower left', bbox_to_anchor=(-0.01,-0.02), fancybox=False)Legend的詳細參數在基礎繪圖命令章節,請互相參閱。
在不清楚每節具體色號時,我們可以使用循環的方法添加圖例,如:
cs=ax.contourf(cycle_LON,cycle_LAT, cycle_t, levels=np.arange(-60,60,10),cmap='RdBu_r',extend='both') cmap=cm.get_cmap('RdBu_r',len(cs.levels)-1) color_list=(cmap(range(len(cs.levels)-1))) Rectangles=[ ] text=[ ] for l incs.levels[::-1]: text.append(f'{int(l-10)}~{int(l)}') for color incolor_list[::-1]: Rectangles.append(mpatches.Rectangle((0,0), 1, 1, facecolor=color)) ax.legend(handles=Rectangles,labels=text,loc='lowerright', fontsize=3, title='溫度', frameon=False, fancybox=False, handletextpad=2, handlelength=3, handleheight=1.5, title_fontsize=5, bbox_to_anchor=(1.25,0), labelspacing=0.5)這裡使用的是直接操作colormap方式生成新的legend,你也可以使用get_facecolor來獲取當前contourf的顏色:
6. 一個數值間隔對應多個顏色問題的處理
這個問題是在不正確的使用顏色映射表時發生的,發生問題有兩個原因:一、不恰當的使用了顏色映射規則,如在定製化cmap與colorbar章節的norm中使用了Normalize規則,則必然使一個數值間隔對應多個顏色;二、由於colormap中存儲的顏色數組長度少於levels數組長度。其中後一個原因在使用cmaps引入NCL中的cmap時比較容易發生。例如:
pc1=ax1.contourf(olon,olat,rain_new, levels=np.arange(0,500, cmap=cmaps.sunshine_9lev) pc2=ax2.contourf(olon,olat,rain_new, levels=np.array([0,50,100,250,300,500]), cmap=cmaps.sunshine_9lev)這個時候,只能將levels的長度縮短,使其短於cmap存儲的顏色數組長度。NCL的顏色條存儲的顏色是不一致的,例如上面引入的sunshine_9lev,該cmap只存儲了10種顏色,而你設定的levels竟然達到100,則必然出現錯位的情況。
Matplotlib的colormap基本不存在這種情況,因為其每個cmap都存有256個顏色。
定製化cmap與colorbar
在實際數據可視化使用中,cmap其實只是一連串的存儲的代表顏色的數組,與數值沒有確實的聯繫。例如在contourf繪製填色等值線圖時,只要修改levels參數,就會使某個顏色代表的數值出現變化,為了體現這種變化,matplotlib的colors模塊提供了規範化類語句,以實現我們的客觀需求。讀者可以去官網參考全部的規範化類語句。這裡我們僅僅深入研討幾種常用類語句。
1.LinearSegmentedColormap
混色分割類函數,該命令可以針對使用者輸入的顏色列表和切割N值,調配出新的colormap。該命令類似美術中的調色板,同過原始顏色列表中的臨近顏色混合出新的顏色。而隨著顏色列表與N值的改變,生成的新cmap也會改變,如下面,給出兩個顏色'tab:red','tab:blue'和不同的N值來生成新cmap:
import matplotlib.colorsas mcolors color_list=['tab:red','tab:blue'] new_cmap=mcolors.LinearSegmentedColormap.from_list('new_cmap', color_list,N=2) norm=mpl.colors.Normalize(vmin=5,vmax=10) fc=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=new_cmap), cax=ax, orientation='horizontal')這裡我們只要求新生成的cmap只有兩個顏色即可,所以我們輸入的兩個顏色不經過調色,直接被輸出出來。當我們修改N為5,與數值間隔數量相同時:
new_cmap=mcolors.LinearSegmentedColormap.from_list('new_cmap', color_list,N=5)這時,colorbar中間部分明顯體現出兩種顏色混交出新的顏色。通過更改原始顏色列表和N值,我們可以調配出更多的colormap,如:
color_list=['tab:red','tab:blue','tab:green','new_cmap=mcolors.LinearSegmentedColormap.from_list('new_cmap', color_list,50) norm=mpl.colors.Normalize(vmin=5,vmax=10) fc=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=new_cmap), cax=ax, orientation='horizontal')這個命令默認只使臨近的兩個顏色之間調配出新的顏色。
2.ListedColormap
拼接顏色列表類函數,該命令只能單純拼接顏色列表裡的全部顏色,顏色分割線明顯,不會出現混調色。最常使用的是氣象降水量色條的定製,以及其他需要使用者自定義顏色條的情況。
colorlevel=[0.1,10.0,25.0,50.0,100.0,250.0,500.0] colorlist=[' 'cmap=mcolors.ListedColormap(colorlist) norm=mcolors.BoundaryNorm(colorlevel,cmap.N) fc=fig.colorbar(mpl.cm.ScalarMappable(norm=norm,cmap=cmap), cax=ax, orientation='horizontal')3.norm
正如之前提到的,我們需要針對顏色與數值來形成映射,這種映射規則在colors模塊中叫做norm。常規繪製contourf時,我們輸入的levels就叫做一種norm,此時的映射規則擁有最高的優先性,使用levels參數將會掩蓋其他norm命令。這是最常見的情況,在使用默認cmap時常用這種方式。當然在我們之後的使用中,因為繪圖的特殊需求,我們還需要其他的映射規則。
Matplotlib的開發者並沒有將設定等級的levels命令進行普遍化,造成該便捷命令只能在contour、contourf裡使用。而像其他的常見顏色映射命令scatter、pcolormesh是無法使用的,只有設定上下線的vmin、vmax可以限制範圍,這時我們可以使用norm來指定數值與顏色的映射規則,例如:
import numpy asnp import pandas aspd importmatplotlib.pyplot as plt importmatplotlib.colors as mcolors importmatplotlib.ticker as mticker importcartopy.crs as ccrs importcartopy.io.shapereader as shpreader importcartopy.mpl.ticker as cmt import cmaps importmatplotlib as mpl fromscipy.interpolate import Rbf plt.rcParams['font.sans-serif']=['FangSong'] plt.rcParams['axes.unicode_minus']=False pro=ccrs.PlateCarree() extent=[108.3,109.4,29.7,30.7] def create_map(ax,extent): ax.add_geometries(shpreader.Reader(r'E:\利川.shp').geometries(), crs=ccrs.PlateCarree(),linewidth=0.5, edgecolor='k',facecolor='none') ax.set_extent([108.3,109.35,29.7,30.7],crs=pro) ax.set_xticks(np.arange(extent[0],extent[1], 0.2)) ax.set_yticks(np.arange(extent[2],extent[3]+0.2, 0.2)) ax.tick_params(which='both',labelsize=4,top=True, ax.tick_params(which='both',labelsize=4,top=True, right=True,width=0.5) ax.xaxis.set_major_formatter(cmt.LongitudeFormatter()) ax.yaxis.set_major_formatter(cmt.LatitudeFormatter()) ax.grid(axis='both',which='major',linewidth=0.6,color='w', alpha=0.45,linestyle='--',zorder=5) ax.spines['geo'].set_linewidth(0.5) ax.minorticks_on() fig=plt.figure(figsize=(5,4.2),dpi=700) ax1=plt.subplot(221,projection=pro,facecolor='#E8E6E7') ax2=plt.subplot(222,projection=pro,facecolor='#E8E6E7') ax3=plt.subplot(223,projection=pro,facecolor='#E8E6E7') ax4=plt.subplot(224,projection=pro,facecolor='#E8E6E7') for ax in [ax1,ax2,ax3,ax4]: create_map(ax,extent) filename=r'C:\Users\lenovo\Desktop\3.xlsx' df=pd.read_excel(filename) lon=df['經度'] lat=df['緯度'] rain=df['rain'] as1=ax1.scatter(lon,lat,c=rain,s=24,cmap='plasma_r') cb1=fig.colorbar(as1,ax=[ax1],shrink=0.9) ax1.set_title('默認norm的scatter',fontsize=7) cmap =mpl.cm.plasma_r colorlevel=[0.1,10,25,50,100,200,250,500] norm=mcolors.BoundaryNorm(colorlevel,cmap.N) as2=ax2.scatter(lon,lat,c=rain,s=24,cmap='plasma_r',norm=norm) cb2=fig.colorbar(as2,ax=[ax2],shrink=0.9) ax2.set_title('指定norm的scatter',fontsize=7) olon=np.linspace(108,111,110) olat=np.linspace(29,32,110) olon,olat=np.meshgrid(olon,olat) func=Rbf(lon,lat,rain,function='linear') rain_new=func(olon,olat) pc1=ax3.pcolormesh(olon,olat,rain_new,cmap='viridis_r') cb3=fig.colorbar(pc1,ax=[ax3],shrink=0.9) ax3.set_title('默認norm的pcolormesh',fontsize=7) cmap2 =mpl.cm.viridis_r norm2=mcolors.BoundaryNorm(colorlevel,cmap2.N) pc2=ax4.pcolormesh(olon,olat,rain_new,norm=norm2,cmap='viridis_r') cb4=fig.colorbar(pc2,ax=[ax4],shrink=0.9) ax4.set_title('指定norm的pcolormesh',fontsize=7) for cb in[cb1,cb2,cb3,cb4]: cb.ax.tick_params(length=2,labelsize=4)
通過比較兩個默認norm的scatter、pcolormesh圖片我們可以看出,由於嚴格的線性映射,色階與數值的關係劣化嚴重,尤其是低數值面積對應的單色調區域佔圖片幅面面積較大。然後通過我們指定新的norm,使顏色與數值的對應映射關係得到改善。下面介紹常見的幾種norm:
3.1 Normalize
即默認的norm,即所有顏色均勻排列,與數值間隔大小無關係,數值之間沒有清晰的分界線,同一個數值間隔可以對應若干個顏色間隔,如:
3.2 BoundaryNorm
顧名思義,該命令下的顏色與數值間隔一一對應,擁有明顯的分界線,一個數值間隔只有一個顏色與之相匹配。這是我們常見的使用方法,如:
3.3 CenteredNorm
偏度中心映射規則,使我們的中心值附近鈍化,默認中心值為0,如:
該命令在matplotlib3.4.2版本中被注釋掉,暫時無法使用。
3.4 LogNorm
指數化數值顏色對應規則,詳參官網文檔。
3.5 PowerNorm
Power law映射規則,通過改變gamma值,來改變原本的Normalize對應關係,改變後,一個數值間隔仍然可能包含多個顏色,但比原來默認效果稍好,如:
3.6 TwoSlopeNorm
該命令使色條圍繞中心產生相同的刻度比例,如果是拼接色條,vcenter給出的值的位置就是拼接位置,如:
在上兩條colorbar中,設定vcenter=300時,則兩色條拼接處鉚定300;設定vcenter=0時,則兩色條拼接處鉚定0。
從這個特性來看,我們在繪製類似鉚定0值時的colormap時有特別的用處,如:
在使用上圖3顏色條時,我們需要使紅藍陰陽色關於0值對稱,但是可以發現,藍色的賦值其實都在0~-0.5的範圍,深藍色的部分實際上是沒有被使用過的。這時如果如圖1單純只改變vmin、vmax範圍,則會造成0刻度掉入藍色區,紅藍陰陽色不再關於0值對稱。如果使用TwoSlopeNorm,則可以鉚定0值為中心值,如圖二:
Reds=mpl.cm.get_cmap('Reds') Blues_r=mpl.cm.get_cmap('Blues_r') Redslist=Reds(np.linspace(0,1, 256)) Bluerlist=Blues_r(np.linspace(0.25,1,256)) new_list=np.vstack((Bluerlist,Redslist)) ncmap=mcolors.LinearSegmentedColormap(colors=new_list, name='newcmap') norm=mcolors.TwoSlopeNorm(vmin=-0.5,vcenter=0,vmax=1)3.7 FuncNorm
使用者自定義函數來映射顏色,詳參官網文檔,該命令matplotlib3.4.2版本中被注釋掉,暫時無法使用。