一、色条Colorbar的基础
在我们绘制有色阶的图片时,多会用到colorbar这个关联利器,色条可以直接将数值与颜色连接在一起。常用的scatter、contourf是非常适合使用的。第一节我们来简要谈谈常用的colorbar参数,以后例子都基于contourf命令。
第一个参数为colorbar传入参数,代表colorbar所关联的contourf,这种方式是最简单的默认传入,绘制出来的colorbar和cf是相匹配的,展示的也是cf的信息。
代码语言:javascript复制cf=ax.contourf(... ...)
fig.colorbar(cf)
第二个参数为colorbar绘制的默认子图位置参数,代表当前这个colorbar将要摆放的子图位置。
代码语言:javascript复制import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False#负号
def data():
datax=np.linspace(-10,10,200)
datay=np.linspace(-10,10,200)
X,Y=np.meshgrid(datax,datay)
Z=np.sqrt(X**2 Y**2)
return X,Y,Z
x,y,z=data()
fig=plt.figure(figsize=(5,2),dpi=500)
ax1=fig.add_axes([0,0,0.4,1])
ax1.set_title('ax1')
acf1=ax1.contourf(x,y,z)
ax2=fig.add_axes([0.5,0,0.4,1])
ax2.set_title('ax2')
acf2=ax2.contourf(x,y,z,cmap='Spectral_r')
fig.colorbar(acf1,ax=ax2)
在上面这段程序中最后一句,fig.colorbar(acf1,ax=ax2),虽然我们传入了acf1即ax1里的等值线,但是我们指定colorbar中ax=ax2,所以绘制出来的colorbar将被放置在ax2旁边。
第三个为指定色条位置参数cax,我们放在后面专开一节讲。
第四个为使色条展示尖角的参数extend,他可以使色条展现出角的形状,其可选命令both表示两头都变尖,max表示数值大的那头变尖,min表示小的那头变尖。
代码语言:javascript复制cf=ax.contourf(x,y,z,extend='both')
fig.colorbar(cf,extend='both')
第五个参数为缩放参数shrink,从0-1,色条将会按照输入值被缩放:
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,shrink=0.5)
第六个参数为距离参数pad,该参数控制色条与子图的间距:
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,pad=0.005)
第七个为色条方向参数orientation,控制色条时横纵方向,当为horizontal时,色条将被平放在下方:
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,orientation='horizontal')
第八个为ticks,你可以传入一个列表,显示你想展示的刻度,其他刻度将消失。类似于ax.set_yticks( ).
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,ticks=[0,2,4,16])
第九个为format,用于控制色条上刻度的格式,比如将其保留两位小数:
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,format='%.2f')
第十个为label,简单的给色条一个标签:
代码语言:javascript复制cf=ax.contourf(x,y,z)
fig.colorbar(cf,label='色条')
当然,上面的都是最为基础的参数,你还可以进一步的做美化,其中,最常用的就是将色条作为一个子图来进行操作。
代码语言:javascript复制cf=ax.contourf(x,y,z)
fc=fig.colorbar(cf)#使用fc省称
ax2=fc.ax#调出colorbar的ax属性
ax2.set_title('这是色条的标题',fontsize=5)
ax2.tick_params(which='major',direction='in',labelsize=4,length=7.5)
ax2.tick_params(which='minor',direction='in')
ax2.yaxis.set_minor_locator(mticker.MultipleLocator(0.5))#显示x轴副刻度
接下来就要讲一讲关于cax的应用了,这个参数在fig.add_axes下特别好用,可以实现非常棒的色条插入效果。从前面简单的参数来看,colorbar自身很难实现在多子图之间挪动,而cax则可以轻松实现。
代码语言:javascript复制import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False#负号
def data():
datax=np.linspace(-10,10,200)
datay=np.linspace(-10,10,200)
X,Y=np.meshgrid(datax,datay)
Z=np.sqrt(X**2 Y**2)
return X,Y,Z
x,y,z=data()
fig=plt.figure(figsize=(5,3),dpi=500)
ax1=fig.add_axes([0,0.3,0.4,0.7])
ax1.set_title('ax1')
acf1=ax1.contourf(x,y,z)
ax2=fig.add_axes([0.5,0.3,0.4,0.7])
ax2.set_title('ax2')
acf2=ax2.contourf(x,y,z)
ax3=fig.add_axes([0.2,0.17,0.5,0.05])
fig.colorbar(acf1,cax=ax3,orientation='horizontal')
将上面这幅图与本文第一张图比较,如果使用fig.colorbar直接生成色条,那么图像上将会有两个子图,生成的colorbar不算子图。而cax方式相当于有三个子图,ax1,ax2与ax3,其中ax3用来存放色条。而只要更改添加子图的位置参数,就可以在图上随意移动。这在多子图上添加规范色条时非常方便。
关于指数标签,一般来说,在contourf中使用了指数标签命令后,色条会自动变成指数模式。
代码语言:javascript复制import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from matplotlib.colors import LogNorm
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False#负号
def data():
datax=np.linspace(-10,10,200)
datay=np.linspace(-10,10,200)
X,Y=np.meshgrid(datax,datay)
Z=np.sqrt(X**2 Y**2)
return X,Y,Z
x,y,z=data()
fig=plt.figure(figsize=(2.6,2),dpi=500)
ax=fig.add_axes([0,0,1,1])
cf=ax.contourf(x,y,z*100,norm=LogNorm())###在这里有不同
fc=fig.colorbar(cf)
ax2=fc.ax
ax2.set_title('指数色条',fontsize=5)
ax2.tick_params(which='major',direction='in',labelsize=4,length=7.5)
ax2.tick_params(which='minor',direction='in')
ax.set_title('使用指数标签')
接下来,是一些比较没多大用处,但很有意思的colorbar操作。
一、如何使色条两侧各有一种刻度
比如这张图的色条,左边是数值刻度,右边是文字刻度。当然,两边是否对称,是否数值文字都取决于你的设置,你还可以做出螺旋上升的色条标签。
代码语言:javascript复制import numpy as np
from matplotlib.colors import Normalize
import matplotlib as mpl
import pandas as pd
import cartopy.crs as ccrs
import cartopy.feature as cfeat
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from cartopy.io.shapereader import Reader
from scipy.interpolate import Rbf
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import sys
sys.path.append(r"C:UserslenovoDesktop")
import maskout
plt.rcParams['font.sans-serif']=['SimHei']
extent=[108.2,110.8,29.1,31.401]
proj= ccrs.PlateCarree()
fig = plt.figure(figsize=(7, 10),dpi=500)
ax = fig.subplots(1, 1, subplot_kw={'projection': proj})
reader = Reader(r'E:家园区划-省界恩施.shp')
cities = cfeat.ShapelyFeature(reader.geometries(), proj, edgecolor='k', facecolor='none')
ax.add_feature(cities, linewidth=0.7)
ax.set_extent(extent, crs=proj)
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=0.7, color='k', alpha=0.5, linestyle='--')
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlocator = mticker.FixedLocator(np.arange(extent[0], extent[1] 0.5, 0.4))
gl.ylocator = mticker.FixedLocator(np.arange(extent[2], extent[3] 0.5, 0.4))
gl.xlabel_style={'size':10}
gl.ylabel_style={'size':10}
###############################以下为添加县市名称和点########################################
nameandstation={"恩施":[109.5,30.2],"利川":[109,30.3],"巴东":[110.34,31.04],"建始":[109.72,30.6],"宣恩":[109.49,29.987],"来凤":[109.407,29.493],"咸丰":[109.14,29.665],"鹤峰":[110.034,29.89]}
for key,value in nameandstation.items():
ax.scatter(value[0] , value[1] , marker='.' , s=90 , color = "k" , zorder = 3)
ax.text(value[0]-0.07 , value[1] 0.03 , key , fontsize = 12 , color = "k")
##############################################读取文件打包数据###########################################
filename=r'C:UserslenovoDesktop累年降水数据.xlsx'#数据文件地址,这个数据是我捏造的,没有实际意义
df=pd.read_excel(filename)#读取文件
lon=df['lon']#读取站点经度
lat=df['lat']#读取站点纬度
rain=df['precipitation']#读取站点累计年降水量
olon=np.linspace(108,111,30)#设置网格经度
olat=np.linspace(29,32,30)#设置网格纬度
olon,olat=np.meshgrid(olon,olat)#网格化
func=Rbf(lon,lat,rain,function='linear')#定义插值函数
rain_new=func(olon,olat)#获得插值后的网格累计降水量
cs= ax.contourf(olon,olat,rain_new,levels=np.arange(900,2000,100),cmap='GnBu',extend='both')#画图
clip=maskout.shp2clip(cs, ax,r'E:enshi恩施.shp' ,0)
plt.title('恩施州去年累计降水',size=20)
##########################################################################################
position=fig.add_axes([0.97,0.25,0.04,0.5])
cb=fig.colorbar(cs,cax=position,shrink=0.4,extend='both')#绘制colorbar并省称为cb
ax2=cb.ax#召唤出cb的ax属性并省称为ax2,这时ax2即视为一个子图
ax2.yaxis.set_ticks_position('left')#将数值刻度移动到左侧
ax2.tick_params(labelsize=10,left=True,right=True)#修改刻度样式,并使左右都有刻度
ax3=ax2.secondary_yaxis('right')#新建ax3,使ax3与ax2完全相同
ax3.spines['right'].set_bounds(900,1900)#截去多余的部分
ax3.set_yticks([900,1100,1300,1500,1700,1900])
ax3.set_yticklabels(['缺水','一般性缺水','收支充足','降水丰沛','有可能涝灾','成灾'])#将ax3上的定量数值转化为定性文字
我在文章里插入了读者讨论,貌似可以充当互动,有问题可以直接留言,也可以后台留言。
二、如何实现colorbar与其他子图的互动操作
这个是好像有一位小伙伴问过的,于是简单的做了一个,使折线图与色条在视觉上共用一个坐标轴(实际上是没有的)。
代码语言:javascript复制import numpy as np
from matplotlib.colors import Normalize
import matplotlib as mpl
import pandas as pd
import cartopy.crs as ccrs
import cartopy.feature as cfeat
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from cartopy.io.shapereader import Reader
from scipy.interpolate import Rbf
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import sys
sys.path.append(r"C:UserslenovoDesktop")
import maskout
plt.rcParams['font.sans-serif']=['SimHei']
extent=[108.2,110.8,29.1,31.401]
proj= ccrs.PlateCarree()
fig = plt.figure(figsize=(7, 5),dpi=500)
ax = fig.add_axes([0,0,0.6,1],projection=proj)
reader = Reader(r'E:家园区划-省界恩施.shp')
cities = cfeat.ShapelyFeature(reader.geometries(), proj, edgecolor='k', facecolor='none')
ax.add_feature(cities, linewidth=0.7)
ax.set_extent(extent, crs=proj)
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=0.7, color='k', alpha=0.5, linestyle='--')
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlocator = mticker.FixedLocator(np.arange(extent[0], extent[1] 0.5, 0.4))
gl.ylocator = mticker.FixedLocator(np.arange(extent[2], extent[3] 0.5, 0.4))
gl.xlabel_style={'size':10}
gl.ylabel_style={'size':10}
###############################以下为添加县市名称和点########################################
nameandstation={"恩施":[109.5,30.2],"利川":[109,30.3],"巴东":[110.34,31.04],"建始":[109.72,30.6],"宣恩":[109.49,29.987],"来凤":[109.407,29.493],"咸丰":[109.14,29.665],"鹤峰":[110.034,29.89]}
for key,value in nameandstation.items():
ax.scatter(value[0] , value[1] , marker='.' , s=90 , color = "k" , zorder = 3)
ax.text(value[0]-0.07 , value[1] 0.03 , key , fontsize = 12 , color = "k")
##############################################读取文件打包数据###########################################
filename=r'C:UserslenovoDesktop累年降水数据.xlsx'#数据文件地址
df=pd.read_excel(filename)#读取文件
lon=df['lon']#读取站点经度
lat=df['lat']#读取站点纬度
rain=df['precipitation']#读取站点累计年降水量
olon=np.linspace(108,111,30)#设置网格经度
olat=np.linspace(29,32,30)#设置网格纬度
olon,olat=np.meshgrid(olon,olat)#网格化
func=Rbf(lon,lat,rain,function='linear')#定义插值函数
rain_new=func(olon,olat)#获得插值后的网格累计降水量
cs= ax.contourf(olon,olat,rain_new,levels=np.arange(900,2000,100),cmap='GnBu')#画图
clip=maskout.shp2clip(cs, ax,r'E:enshi恩施.shp' ,0)#白化
plt.title('恩施州去年累计降水',size=20)
##########################################################################################
position=fig.add_axes([0.66,0.13,0.03,0.74])#添加子图用来存放色条
cb=fig.colorbar(cs,cax=position,shrink=0.4)#绘制colorbar并省称为cb
ax2=cb.ax#召唤出cb的ax属性并省称为ax2,这时ax2即视为一个子图
ax2.yaxis.set_ticks_position('left')#将数值刻度移动到左侧
ax2.tick_params(which='both',labelsize=10,left=True,direction='in')#修改刻度样式,并使左右都有刻度
ax2.yaxis.set_minor_locator(mticker.MultipleLocator(50))
#############################################################################################
ax3=fig.add_axes([0.69,0.13,0.25,0.74])
ax3.tick_params(which='both',direction='in',left=False,right=False)
ax3.yaxis.set_ticks_position('right')#将数值刻度移动到左侧
ax3.yaxis.set_major_locator(mticker.NullLocator())#去掉y轴坐标
ax3.xaxis.set_minor_locator(mticker.MultipleLocator(100))#显示x轴副刻度
ax3x=np.array([200,400,800,1400,1200,900,700,450,200,100])
ax3y=np.arange(900,1900,100)
ax3.plot(ax3x,ax3y)
ax3.set(title='累计降水分布面积',xlim=(0,1500),xlabel='分布面积(平方千米)')
以上数据和各种标准都是我虚构的,没有实际意义。
三、不等距的色条
这是matplotlib官网上的一个例子,比较有意思,于是就搬过来了。基本上照源代码修改就能用了。
代码语言:javascript复制position=fig.add_axes([0.9,0,0.05,1])
cmap = mpl.colors.ListedColormap(colordict)
cmap.set_over('0.25')
cmap.set_under('0.75')
bounds = colorlevel
norm2 = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
mpl.cm.ScalarMappable(cmap=cmap, norm=norm2),
cax=position,
boundaries=[0] bounds [501],
extend='both',
ticks=bounds,
spacing='proportional')