前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python气象绘图教程(十四)

Python气象绘图教程(十四)

作者头像
气象学家
发布2020-06-17 17:22:57
2.6K0
发布2020-06-17 17:22:57
举报
文章被收录于专栏:气象学家气象学家

本节提要:图例 Legend与colorbar 一、图例Legend命令常用参数

作为成熟的科研图表,图例的重要性是不言而喻的。所谓一图敌千言,在气象科研领域,图表是进行数据可视化的利器,而图例是帮助阅读者理解图表信息的关键。绘图库matplotlib中专门辟出一个命令——Legend进行设置。下面首先介绍其常用关键字参数。

loc

设置图例位置,一般在图表内部

fontsize

字体大小

markerscale

图例标记相对于原始标记的相对大小

markerfirst

图例在标签左侧,bool值控制

numpoints

图例的标记数目

frameon

图例边框,bool值控制

fancybox

边框是否圆边

shadow

边框阴影

framealpha

边框透明度

edgecolor

边框边缘颜色

facecolor

边框内部填色

ncol

图例列数,int值

borderpad

边框内边距

labelspacing

图例之间的垂直间距

handlelength

图例的句柄长度

handleheight

图例的句柄高度

handletextpad

图例与句柄之间间距

columnspacing

列间距

title

图例标题

bbox_to_anchor

指定图例在轴的位置

在之前,我们制作了一个墒情图,本次即以此图展示legend命令。

两图相比较,修改了ncol为一列,修改了edgecolor为红色,修改了facecolor为绿色,修改了framealpha为1,修改了fancybox为圆角。

其他参数命令,读者可以自行实验,在jupyter notebook中实验是非常方便的。

二、Legend的位置调节命令——loc与bbox_to_anchor

Legend有两个可以调节位置的命令,使用方式各不相同。

loc是最常用的位置命令,两种使用方式,一是使用0~10数字,二是使用字符命令如'best','right',center','upper right'等,这种图例位置是在子图内部的,可能会出现遮挡图形的情况,所以就有bbox_to_anchor命令,这个命令类似于前面推荐的add_axes命令,指定的是图例的绝对位置,不妨通过上一个图片展示:

代码语言:javascript
复制
ax2.legend((bar1,bar2,line1,line2),('降水','蒸发','墒情','气温'),frameon=True,framealpha=1,ncol=1,shadow=True,bbox_to_anchor=(0,0))

可以看出,将绝对位置定为bbox_to_anchor=(0,0)后,图例可以被放置在子图外了。同样的,可以放置到我们常见的图例位置:

代码语言:javascript
复制
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),给予图例框的起始绝对位置和结束绝对位置:

代码语言:javascript
复制
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会自动生成图例):

代码语言:javascript
复制
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装载字符串作为名称:

代码语言:javascript
复制
plt.legend([line1,line2,line3,scatter1,scatter2,scatter3],['line1','line2','line3','scatter1','scatter2','scatter3'])

然后通过括号分类:

代码语言:javascript
复制
plt.legend([(line1,line2,line3),scatter1,scatter2,scatter3],['这是合并的线','scatter1','scatter2','scatter3']

可以发现,虽然合并了,但是合并的图例里只有一根绿线了,这时需要引进新的模块:

代码语言:javascript
复制
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
plt.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.legend(xxx)

ax.legend(ooo)

ax.legend(***)

三个命令,最终只能出现(***)这个图例。但是科研图表存在需要多个图例的情况,如果确实需要绘制时,可以通过ax.add_artist()命令添加。仍然以上一小节的图为例。首先,我们将所有line通过ax.legend()命令绘制出来:

代码语言:javascript
复制
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()添加到子图上:

代码语言:javascript
复制
from matplotlib.legend import Legend
legend2=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,通过填色变化来展示数据。这两种是最简单的使用方式,进一步的,散点图还可以将两个都设置为变量,都展示数据的变化。

这一节是源于一个小伙伴在群里问的问题。

首先读入数据:

代码语言:javascript
复制
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():绘制,这里略去):

代码语言:javascript
复制
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表示填色的含义

首先将sca传入colorbar命令,生成色条:

代码语言:javascript
复制
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)

这样就能展示填色的意义了,阅读者能明白填色代表累计降水量,然后,通过如下命令生成图例:

代码语言:javascript
复制
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'])前一部分为图例,后一部分为标签。

B、通过两个图例分别展示散点直径和散点颜色

前面的程序与A中完全相同,在第四节中已经讲了如何建立多个子图,这里马上就上手使用了,这次不使用colorbar展示颜色变化,而使用带颜色的散点:

代码语言:javascript
复制
from matplotlib.lines import Line2D
cmap=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命令。

接着,使用前面提到的添加第二个图例的方法,添加散点直径图例:

代码语言:javascript
复制
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)方法添加新的图例。

通过这幅图能看出什么呢?可以看出恩施州降水日数和降水量高值区都集中在利川市,而鹤峰的日数和降水量都偏少。再看宣恩县和恩施市,宣恩的降水日更少,但是降水量比恩施市多。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 气象学家 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档