气象编程 | PyAOS基础教程三:自定义函数

1、概要

文章解答以下疑问:

第一:如何自定义函数。

文章的目标

第一:定义一个带参数的函数;
第二:使用文档字符串来记录函数;
第三:把绘图代码切分成更小、功能更加单一的函数。

2、内容回顾

我们先来回顾上节完整绘图代码

1 ACCESS-CM2 historical precipitation climatology using the following commands:
2
3import xarray as xr
4import cartopy.crs as ccrs
5import matplotlib.pyplot as plt
6import numpy as np
7import cmocean
8
9accesscm2_pr_file = 'data/pr_Amon_ACCESS-CM2_historical_r1i1p1f1_gn_201001-201412.nc'
10
11dset = xr.open_dataset(accesscm2_pr_file)
12
13clim = dset['pr'].groupby('time.season').mean('time', keep_attrs=True)
14
15clim.data = clim.data * 86400
16clim.attrs['units'] = 'mm/day'
17
18fig = plt.figure(figsize=[12,5])
19ax = fig.add_subplot(111, projection=ccrs.PlateCarree(central_longitude=180))
20clim.sel(season='JJA').plot.contourf(ax=ax,
21                                     levels=np.arange(0, 13.5, 1.5),
22                                     extend='max',
23                                     transform=ccrs.PlateCarree(),
24                                     cbar_kwargs={'label': clim.units},
25                                     cmap=cmocean.cm.haline_r)
26ax.coastlines()
27
28model = dset.attrs['source_id']
29title = f'{model} precipitation climatology (JJA)'
30plt.title(title)
31
32plt.show()

3、自定义函数

如果需要为其他不同的模式绘制一个相似的图片,那么我们需要复制、粘贴这段代码,然后做相应的修改就可以了,但是这种方式却可能提升出错的概率,比如数据替换成其他模式了,同时我们也要调整季节JJA为DJF,而忘记了调整title,这样就出现标题与数据内容不匹配的问题。

另外,复制、粘贴代码的方法可能更加消耗时间,未来,如果我们找到了更好的方法来绘图,比如用函数:plt.gca().gridlines()打开网格线,我们就需要对所有的代码段进行调整,这种方法太累了,因此我们推荐使用自定义函数来实现这个功能。

 1def plot_pr_climatology(pr_file, season, gridlines=False): 2    '''Plot the precipitation climatology. 3 4    Args: 5      pr_file (str): Precipitation data file 6      season (str): Season (3 letter abbreviation, e.g. JJA) 7      gridlines (bool): Select whether to plot gridlines 8 9    '''1011    dset = xr.open_dataset(pr_file)1213    clim = dset['pr'].groupby('time.season').mean('time', keep_attrs=True)1415    clim.data = clim.data * 8640016    clim.attrs['units'] = 'mm/day'1718    fig = plt.figure(figsize=[12,5])19    ax = fig.add_subplot(111, projection=ccrs.PlateCarree(central_longitude=180))20    clim.sel(season=season).plot.contourf(ax=ax,21                                          levels=np.arange(0, 13.5, 1.5),22                                          extend='max',23                                          transform=ccrs.PlateCarree(),24                                          cbar_kwargs={'label': clim.units},25                                          cmap=cmocean.cm.haline_r)26    ax.coastlines()27    if gridlines:28        plt.gca().gridlines()2930    model = dset.attrs['source_id']31    title = f'{model} precipitation climatology ({season})'32    plt.title(title)

然后,我们使用帮助命令help,我们就可以看到对于当前函数实现的功能,以及几个参数的解释。

1help(plot_pr_climatology)
2
3Help on function plot_pr_climatology in module __main__:
4
5plot_pr_climatology(pr_file, season, gridlines=False)
6    Plot the precipitation climatology.
7
8    Args:
9      pr_file (str): Precipitation data file
10      season (str): Season (3 letter abbreviation, e.g. JJA)
11      gridlines (bool): Select whether to plot gridlines

然后,我们就可以用这个自定义函数来绘图了:

1plot_pr_climatology('data/pr_Amon_ACCESS-CM2_historical_r1i1p1f1_gn_201001-201412.nc', 'JJA')2plt.show()

如果要绘制DJF的图片,只需要修改参数就可以了:

1plot_pr_climatology('data/pr_Amon_ACCESS-ESM1-5_historical_r1i1p1f1_gn_201001-201412.nc', 'DJF')
2plt.show()

gridlines这个参数模式是关闭的,我们也可以打开:

1plot_pr_climatology('data/pr_Amon_ACCESS-ESM1-5_historical_r1i1p1f1_gn_201001-201412.nc',2                    'DJF', gridlines=True)3plt.show()

4、代码改进

上面的代码超过16行,稍微有点长了。一般来说,人对于7-12行的代码理解起来比较容易,而且理解速度也比较快点,而且越精简的代码,其可靠性也越高。人们对于代码理解的速度多数依赖于代码量,代码量越大,理解速度越慢,代码量越小,理解速度越快,而且帮助文档写得好,也能帮助快速理解。因此我们把上面那段代码进一步细分,提取出功能更加独立、代码量更少的自定义函数。

1def plot_pr_climatology(pr_file, season, gridlines=False):
2    '''Plot the precipitation climatology.
3
4    Args:
5      pr_file (str): Precipitation data file
6      season (str): Season (3 letter abbreviation, e.g. JJA)
7      gridlines (bool): Select whether to plot gridlines
8
9    '''
10
11    dset = xr.open_dataset(pr_file)
12    clim = dset['pr'].groupby('time.season').mean('time', keep_attrs=True)
13    clim = convert_pr_units(clim)
14    create_plot(clim, dset.attrs['source_id'], season, gridlines=gridlines)
15    plt.show()

这也就意味着,我们需要定义新的函数:convert_pr_units 和create_plot,

 1def convert_pr_units(darray): 2    '''Convert kg m-2 s-1 to mm day-1. 3 4    Args: 5      darray (xarray.DataArray): Precipitation data 6 7    ''' 8 9    darray.data = darray.data * 8640010    darray.attrs['units'] = 'mm/day'1112    return darray131415def create_plot(clim, model, season, gridlines=False):16    '''Plot the precipitation climatology.1718    Args:19      clim (xarray.DataArray): Precipitation climatology data20      model (str) : Name of the climate model21      season (str): Season 2223    Kwargs:  24      gridlines (bool): Select whether to plot gridlines    2526    '''2728    fig = plt.figure(figsize=[12,5])29    ax = fig.add_subplot(111, projection=ccrs.PlateCarree(central_longitude=180))30    clim.sel(season=season).plot.contourf(ax=ax,31                                          levels=np.arange(0, 13.5, 1.5),32                                          extend='max',33                                          transform=ccrs.PlateCarree(),34                                          cbar_kwargs={'label': clim.units},35                                          cmap=cmocean.cm.haline_r)36    ax.coastlines()37    if gridlines:38        plt.gca().gridlines()3940    title = f'{model} precipitation climatology ({season})'41    plt.title(title)424344def plot_pr_climatology(pr_file, season, gridlines=False):45    '''Plot the precipitation climatology.4647    Args:48      pr_file (str): Precipitation data file49      season (str): Season (3 letter abbreviation, e.g. JJA)50      gridlines (bool): Select whether to plot gridlines5152    '''5354    dset = xr.open_dataset(pr_file)55    clim = dset['pr'].groupby('time.season').mean('time', keep_attrs=True)56    clim = convert_pr_units(clim)57    create_plot(clim, dset.attrs['source_id'], season, gridlines=gridlines)58    plt.show()

5、自定义模块

对于我们经常用的自定义函数,比如上面的函数:convert_pr_units,如何避免在不同的项目或者python编辑器中反复复制粘贴呢?答案就是把这块代码放到独立的py文件中去,比如命名一个叫unit_conversion.py的python文件,把convert_pr_units函数拷贝其中,然后下次用的时候,我们只要导入unit_conversion模块就可以了。

1import unit_conversion
2clim.data = unit_conversion.convert_pr_units(clim.data)

6、总结

本文的主要知识点:

  • 第一:使用def自定义函数;

  • 第二:函数体需要整体缩进;

  • 第三:函数的调用;

  • 第四:help命令的使用;

  • 第五:将文档字符串放到函数中,提供帮助命令;

  • 第六:缺省参数的默认值和非默认值;

  • 第七:短小且功能单一的函数可靠性较高;

  • 第八:写自定义模块,与如何使用自定义模块

(0)

相关推荐