python气象学习笔记(七):EOF分析运用及代码分析
1.EOF介绍
经验正交函数分析方法(empirical orthogonal function,缩写为EOF),也称特征向量分析(eigenvector analysis),或者主成分分析(principal component annlysis,缩写为PCA),是一种分析矩阵数据中的结构特征,提取主要数据特征量的一种方法。特征向量对应的是空间样本,也称空间特征向量或者空间模态(EOF),在一定程度上反映要素场的空间分布特点;主成分对应的是时间变化,也称时间系数(PC),反映相应空间模态随时间的权重变化。
2.所需要的库
conda install -c conda-forge eofs
库的使用:
from eofs.standard import Eof
3.代码编写
1.导入部分
import numpy as np
import xarray as xr
from eofs.standard import Eof
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import cartopy.io.shapereader as shpreader
import cartopy.mpl.ticker as cticker
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
2.数据加载
plt.rcParams['font.sans-serif'] = ['Times New Roman']
plt.rcParams['axes.unicode_minus'] = False
f = xr.open_dataset(r'...\hgt.mon.mean.nc')
hgt_Eu = f.hgt.loc[f.time.dt.month.isin([6]), 850].loc['1948':'2008']
coslat = np.cos(np.deg2rad(hgt_Eu.lat.values))
solver = Eof(hgt_Eu.values, weights=coslat[..., np.newaxis])
其中对于下面代码:
hgt_Eu = f.hgt.loc[f.time.dt.month.isin([6]), 850].loc['1948':'2008']
- f.hgt: 这里 f 是一个通过 xarray.open_dataset() 打开的数据集,该数据集中的一个变量是 hgt,可能代表大气高度场。
- loc[…] 选择器:xarray 提供了类似 pandas 的 loc 索引器,可以过滤数据集中的维度。
- f.time.dt.month.isin([6]): f.time 代表时间维度的索引,dt 是日期时间的访问器,用于从时间维度中提取各个时间单位(月)。
通过 .isin([6]) 方法筛选出所有时间维度为6月的数据,这步操作限制了提取出的数据仅为6月份的。 - , 850: 这一部分表示选择高度场数据中,压强层为 850 hPa 的数据。这通常用于大气科学领域,850 hPa 是对流层中一个常用的等压面,因为它通常位于天气系统的重要活动层。
- .loc[‘1948’:‘2008’]: 这是对时间进行进一步筛选,只提取从 1948 年到 2008 年之间的数据。xarray 支持基于时间序列的索引,这使得可以很容易从数据集中提取某个时间段内的数据。
coslat = np.cos(np.deg2rad(hgt_Eu.lat.values))
- hgt_Eu.lat.values:
这是从之前步骤中提取的气象数据的纬度值。xarray 对象的 .values 属性将这些数据提取为 numpy 数组。 - np.deg2rad(…):
np.deg2rad() 函数用于将角度从度转换为弧度,因为 numpy 的三角函数如 cos 是基于弧度的。 - np.cos(…):
计算弧度值的余弦。余弦函数用于得到纬度的权重。
3.提取信息
eof = solver.eofsAsCorrelation(neofs=3)
pc = solver.pcs(npcs=3, pcscaling=1)
var = solver.varianceFraction()
4.定义颜色
color1, color2, color3 = [], [], []
for i in range(1948, 2009):
color1.append('red' if pc[i - 1948, 0] >= 0 else 'blue')
color2.append('red' if pc[i - 1948, 1] >= 0 else 'blue')
color3.append('red' if pc[i - 1948, 2] >= 0 else 'blue')
5.图形设置
fig = plt.figure(figsize=(15, 15), dpi=500)
proj = ccrs.PlateCarree(central_longitude=115)
leftlon, rightlon, lowerlat, upperlat = (70, 140, 15, 55)
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
- plt.figure(): Matplotlib 的 figure 函数用于创建一个新的图形窗口。
- figsize: 设置图形窗口的大小。本例中为 (15, 15),单位是英寸。这表示图形的宽高比都是 15 英寸。
- dpi: 表示每英寸的点数(dots per inch)。在这里设置为 500,这意味着图形将非常高分辨率(即适合于精细的地理数据可视化),这对于打印或详细查看是非常合适的。
- ccrs.PlateCarree(): 这是 Cartopy 中的一个投影类,用于创建等距纬度-经度平面图(Plate Carree 投影)。
- central_longitude=115: 设置地图的中心经度为 115°。这意味着地图将在水平方向上居中于 115°经线,通常用来使特定地区(例如中国)在渲染时位于地图的中央部分。
- leftlon, rightlon: 分别是地理区域的左边界和右边界经度,范围是从 70°E 到 140°E。
- lowerlat, upperlat: 分别是地理区域的下边界和上边界纬度,范围从 15°N 到 55°N。(这些坐标限定了地图绘制的地理范围。对于亚洲尤其是东亚地区的研究,使用这样的范围可以聚焦关注目标地区。)
- LongitudeFormatter 和 LatitudeFormatter: Cartopy 提供的格式器,用于将经纬度显示为标准的格式(一般是度,如 120°E, 30°N)。(这些格式器会在后续设置轴标签时使用,以确保地图上显示的经纬度标记格式可读且标准化。)
6.EOF 模态场绘制((以下代码段的解释可以重复类似理解,因为结构相同,只是涉及的 EOF 不同,末尾会附上完整代码))
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(r'D:\shp\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(hgt_Eu.lon,hgt_Eu.lat, eof[0,:,:], levels=np.arange(-0.9,1.0,0.1), zorder=0, extend = 'both',transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)
7.添加小型地理子图((以下代码段的解释可以重复类似理解,因为结构相同,只是涉及的 EOF 不同,末尾会附上完整代码))
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(r'...\bou2_4l.dbf').geometries()
fig_ax11.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)
其中:0.525 和 0.08:分别表示插图的左边缘和下边缘离图形左边缘和底部的相对距离。0.072 和 0.15:分别表示插图的宽度和高度,其单位也是相对比例。
8. EOF 分析的第一个主成分(PC1)的时间序列(后面的主成分分析结构一样,只需要修改数值即可,末尾附上完整代码)
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(min(pc[:,0]),max(pc[:,0]))
fig_ax4.axhline(0,linestyle="--")
fig_ax4.bar(np.arange(1948,2009,1),pc[:,0],color=color1)
9.显示图形
plt.show()
完整代码一:
import numpy as np
import xarray as xr
from eofs.standard import Eof
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.ticker import LongitudeFormatter,LatitudeFormatter
import cartopy.io.shapereader as shpreader
import cartopy.mpl.ticker as cticker
import warnings
warnings.filterwarnings("ignore",category=DeprecationWarning)
# 更改字体
plt.rcParams['font.sans-serif'] = ['Times New Roman']
plt.rcParams['axes.unicode_minus'] = False
f = xr.open_dataset(r'...\hgt.mon.mean.nc') # 读取数据集
hgt_Eu = f.hgt.loc[f.time.dt.month.isin([6]), 850].loc['1948':'2008'] # 中国地区一月高度场,压强为850pa
coslat = np.cos(np.deg2rad(hgt_Eu.lat.values)) # 纬度权重
solver = Eof(hgt_Eu.values, weights=coslat[..., np.newaxis]) # EOF分解器
##获取前三个模态,获取对应的PC序列和解释方差
eof = solver.eofsAsCorrelation(neofs=3) # 模态场
pc = solver.pcs(npcs=3,pcscaling=1) # 时间序列
var = solver.varianceFraction() # 解释方差
#####以上是EOF构建及处理步骤
color1, color2, color3 = [], [], []
for i in range(1948, 2009):
color1.append('red' if pc[i - 1948, 0] >= 0 else 'blue')
color2.append('red' if pc[i - 1948, 1] >= 0 else 'blue')
color3.append('red' if pc[i - 1948, 2] >= 0 else 'blue')
fig = plt.figure(figsize=(15,15),dpi=500)
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(r'...\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(hgt_Eu.lon,hgt_Eu.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(r'...\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(hgt_Eu.lon,hgt_Eu.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(r'...\bou2_4l.dbf').geometries()
fig_ax3.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)
fig_ax3.set_title('(d) EOF3',loc='left',fontsize =15)
fig_ax3.set_title( '%.2f%%' % (var[2]*100),loc='right',fontsize =15)
c2=fig_ax3.contourf(hgt_Eu.lon,hgt_Eu.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(r'...\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(r'...\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(r'...\bou2_4l.dbf').geometries()
fig_ax33.add_geometries(china, ccrs.PlateCarree(),facecolor='none', edgecolor='black',zorder = 1)
cbposition=fig.add_axes([0.102, 0.04, 0.5, 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(min(pc[:,0]),max(pc[:,0]))
fig_ax4.axhline(0,linestyle="--")
fig_ax4.bar(np.arange(1948,2009,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(min(pc[:,1]),max(pc[:,1]))
fig_ax5.axhline(0,linestyle="--")
fig_ax5.bar(np.arange(1948,2009,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(min(pc[:,2]),max(pc[:,2]))
fig_ax6.axhline(0,linestyle="--")
fig_ax6.bar(np.arange(1948,2009,1),pc[:,2],color=color3)
plt.show()
出图结果
作者:剑拭锋芒