EOF分析在气象分析中十分常见,幸运的是有前辈已经利用Python实现了EOF分析并且发布在github。
https://github.com/ajdawson/eofs
本文将直接介绍该库的安装及使用,关于EOF的原理不做介绍。
一、安装
conda install -c conda-forge eofs
二、使用介绍
首先import
from eofs.standard import Eof
该库有几个基本函数是必须掌握的,我们一一介绍。
solver = Eof(x, weights)eof = solver.eofsAsCorrelation(neofs=3)pc = solver.pcs(npcs=3, pcscaling=1)var = solver.varianceFraction()
solver = Eof()建立一个EOF分解器,x为要进行分解的变量,weights为权重,通常指纬度权重。
solver.eofsAsCorrelation,solver.pcs,solver.varianceFraction分别取出空间模态,PC和方差。
三、示例
我们以中国夏季降水三类雨型的分解为例,展示EOF分析完整的Python实现。

首先计算EOF的数据:
import matplotlib.pyplot as pltimport cartopy.crs as ccrsimport cartopy.feature as cfeaturefrom cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTERimport cartopy.mpl.ticker as ctickerimport cartopy.io.shapereader as shpreaderimport xarray as xrfrom eofs.standard import Eof#读取数据f = xr.open_dataset('./pre.nc')pre = np.array(f['pre'])lat = f['lat']lon = f['lon']#计算纬度权重lat = np.array(lat)coslat = np.cos(np.deg2rad(lat))wgts = np.sqrt(coslat)[..., np.newaxis]#创建EOF分解器solver = Eof(summer_mean_tmp, weights=wgts)#获取前三个模态,获取对应的PC序列和解释方差eof = solver.eofsAsCorrelation(neofs=3)pc = solver.pcs(npcs=3, pcscaling=1)var = solver.varianceFraction()
以下是绘图部分,我们先给出其中不重复的部分,文章最末会给出完整代码:
import matplotlib.pyplot as pltimport cartopy.crs as ccrsimport cartopy.feature as cfeaturefrom cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTERimport cartopy.mpl.ticker as ctickerimport cartopy.io.shapereader as shpreaderimport xarray as xrfrom eofs.standard import Eof#读取数据f = xr.open_dataset('./pre.nc')pre = np.array(f['pre'])lat = f['lat']lon = f['lon']#计算纬度权重lat = np.array(lat)coslat = np.cos(np.deg2rad(lat))wgts = np.sqrt(coslat)[..., np.newaxis]#创建EOF分解器solver = Eof(summer_mean_tmp, weights=wgts)#获取前三个模态,获取对应的PC序列和解释方差eof = solver.eofsAsCorrelation(neofs=3)pc = solver.pcs(npcs=3, pcscaling=1)var = solver.varianceFraction()
下面给出完整脚本的代码。看上去很长,但实际上大部分都是重复的:
import matplotlib.pyplot as pltimport cartopy.crs as ccrsimport cartopy.feature as cfeaturefrom cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTERimport cartopy.mpl.ticker as ctickerimport cartopy.io.shapereader as shpreaderimport xarray as xrfrom eofs.standard import Eoff = xr.open_dataset('./pre.nc')pre = np.array(f['pre'])lat = f['lat']lon = f['lon']lat = np.array(lat)coslat = np.cos(np.deg2rad(lat))wgts = np.sqrt(coslat)[..., np.newaxis]solver = Eof(pre, weights=wgts)eof = solver.eofsAsCorrelation(neofs=3)pc = solver.pcs(npcs=3, pcscaling=1)var = solver.varianceFraction()color1=[]color2=[]color3=[]for i in range(1961,2017):if pc[i-1961,0] >=0:color1.append('red')elif pc[i-1961,0] <0:color1.append('blue')if pc[i-1961,1] >=0:color2.append('red')elif pc[i-1961,1] <0:color2.append('blue')if pc[i-1961,2] >=0:color3.append('red')elif pc[i-1961,2] <0:color3.append('blue')fig = plt.figure(figsize=(15,15))proj = ccrs.PlateCarree(central_longitude=115)leftlon, rightlon, lowerlat, upperlat = (70,140,15,55)lon_formatter = cticker.LongitudeFormatter()lat_formatter = cticker.LatitudeFormatter()fig_ax1 = fig.add_axes([0.1, 0.8, 0.5, 0.3],projection = proj)fig_ax1.set_extent([leftlon, rightlon, lowerlat, upperlat], crs=ccrs.PlateCarree())fig_ax1.add_feature(cfeature.COASTLINE.with_scale('50m'))fig_ax1.add_feature(cfeature.LAKES, alpha=0.5)fig_ax1.set_xticks(np.arange(leftlon,rightlon+10,10), crs=ccrs.PlateCarree())fig_ax1.set_yticks(np.arange(lowerlat,upperlat+10,10), crs=ccrs.PlateCarree())fig_ax1.xaxis.set_major_formatter(lon_formatter)fig_ax1.yaxis.set_major_formatter(lat_formatter)china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax1.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)fig_ax1.set_title('(a) EOF1',loc='left',fontsize =15)fig_ax1.set_title( '%.2f%%' % (var[0]*100),loc='right',fontsize =15)c1=fig_ax1.contourf(pre_lon,pre_lat, eof[0,:,:], levels=np.arange(-0.9,1.0,0.1), zorder=0, extend = 'both',transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)fig_ax2 = fig.add_axes([0.1, 0.45, 0.5, 0.3],projection = proj)fig_ax2.set_extent([leftlon, rightlon, lowerlat, upperlat], crs=ccrs.PlateCarree())fig_ax2.add_feature(cfeature.COASTLINE.with_scale('50m'))fig_ax2.add_feature(cfeature.LAKES, alpha=0.5)fig_ax2.set_xticks(np.arange(leftlon,rightlon+10,10), crs=ccrs.PlateCarree())fig_ax2.set_yticks(np.arange(lowerlat,upperlat+10,10), crs=ccrs.PlateCarree())fig_ax2.xaxis.set_major_formatter(lon_formatter)fig_ax2.yaxis.set_major_formatter(lat_formatter)china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax2.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)fig_ax2.set_title('(c) EOF2',loc='left',fontsize =15)fig_ax2.set_title( '%.2f%%' % (var[1]*100),loc='right',fontsize =15)c2=fig_ax2.contourf(pre_lon,pre_lat, eof[1,:,:], levels=np.arange(-0.9,1.0,0.1), zorder=0, extend = 'both',transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)fig_ax3 = fig.add_axes([0.1, 0.1, 0.5, 0.3],projection = proj)fig_ax3.set_extent([leftlon, rightlon, lowerlat, upperlat], crs=ccrs.PlateCarree())fig_ax3.add_feature(cfeature.COASTLINE.with_scale('50m'))fig_ax3.add_feature(cfeature.LAKES, alpha=0.5)fig_ax3.set_xticks(np.arange(leftlon,rightlon+10,10), crs=ccrs.PlateCarree())fig_ax3.set_yticks(np.arange(lowerlat,upperlat+10,10), crs=ccrs.PlateCarree())fig_ax3.xaxis.set_major_formatter(lon_formatter)fig_ax3.yaxis.set_major_formatter(lat_formatter)china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax3.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)fig_ax3.set_title('(e) EOF3',loc='left',fontsize =15)fig_ax3.set_title( '%.2f%%' % (var[2]*100),loc='right',fontsize =15)c3=fig_ax3.contourf(pre_lon,pre_lat, eof[2,:,:], levels=np.arange(-0.9,1.0,0.1), zorder=0, extend = 'both',transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)fig_ax11 = fig.add_axes([0.525, 0.08, 0.072, 0.15],projection = proj)fig_ax11.set_extent([105, 125, 0, 25], crs=ccrs.PlateCarree())fig_ax11.add_feature(cfeature.COASTLINE.with_scale('50m'))china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax11.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)fig_ax22 = fig.add_axes([0.525, 0.43, 0.072, 0.15],projection = proj)fig_ax22.set_extent([105, 125, 0, 25], crs=ccrs.PlateCarree())fig_ax22.add_feature(cfeature.COASTLINE.with_scale('50m'))china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax22.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)fig_ax33 = fig.add_axes([0.525, 0.78, 0.072, 0.15],projection = proj)fig_ax33.set_extent([105, 125, 0, 25], crs=ccrs.PlateCarree())fig_ax33.add_feature(cfeature.COASTLINE.with_scale('50m'))china = shpreader.Reader('./bou2_4l.dbf').geometries()fig_ax33.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)cbposition=fig.add_axes([0.13, 0.04, 0.4, 0.015])fig.colorbar(c1,cax=cbposition,orientation='horizontal',format='%.1f',)fig_ax4 = fig.add_axes([0.65, 0.808, 0.47, 0.285])fig_ax4.set_title('(b) PC1',loc='left',fontsize = 15)fig_ax4.set_ylim(-2.5,2.5)fig_ax4.axhline(0,linestyle="--")fig_ax4.bar(np.arange(1961,2017,1),pc[:,0],color=color1)fig_ax5 = fig.add_axes([0.65, 0.458, 0.47, 0.285])fig_ax5.set_title('(d) PC2',loc='left',fontsize = 15)fig_ax5.set_ylim(-2.5,2.5)fig_ax5.axhline(0,linestyle="--")fig_ax5.bar(np.arange(1961,2017,1),pc[:,1],color=color2)fig_ax6 = fig.add_axes([0.65, 0.108, 0.47, 0.285])fig_ax6.set_title('(f) PC3',loc='left',fontsize = 15)fig_ax6.set_ylim(-2.5,2.5)fig_ax6.axhline(0,linestyle="--")fig_ax6.bar(np.arange(1961,2017,1),pc[:,2],color=color3)plt.show()
本众号内回复EOF数据可获得本文测试使用的数据和shp文件,方便大家练练手。EOF是领域科研最常用也是最古老的时空分离手段,很多SCI文献也都使用该方法,熟练掌握该方法是非常有必要的。
最后:大家都读到这里了,就麻烦大家花几秒钟动动手指点一下文末的广告然后关闭,这样可以给作者一些鼓励,十分感谢。




