4、EOF分析方法

​


作者:小王同学呼啦啦
时间:2022-11-22
环境配置:Notebook+4核心CPU+16G内存

数据在文章末尾链接下载

EOF简介

EOF(经验正交函数分解方法)是大气科学以及海洋科学中常用的分析方法,本文以海温数据为例讲解如何在Python中快速实现资料的EOF处理,关于EOF原理及分析方法不再赘述,感兴趣的小伙伴可以参考南京信息工程大学李丽平老师的课件EOF分析原理。

案例讲解

表征海洋的多个变量之间是相互影响的,这些变量之间的关系是复杂的。利用EOF(即经验正交函数Empirical Orthogonal Function)分析方法,可以实现用少数变量反映原来多变量体现的信息,从而有效地提取物理场的主要信息,并排除随机的干扰信息。EOF方法能够比较客观地反映要素场的主要特征,而且不受数据的空间站点和区域范围等条件的限制,适合中、大尺度分析。
本文将对来自于HadISST的多年海表温度(SST)数据进行EOF分析练习,从而掌握NetCDF文件的读取、相关的数据预处理以及EOF分析的基本原理和实现方法。另外,在分析结果的可视化方面,除了使用Matplotlib绘图以外,还会使用Basemap或Cartopy进行地图相关的绘制。

海温数据信息查看

0.1导入软件包

import xarray as xr
import numpy as np
import datetime as dt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import cartopy.mpl.ticker as cticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.font_manager import FontProperties   
from eofs.standard import Eof
import numpy.ma as ma
import warnings
warnings.filterwarnings("ignore")

0.2读取海温数据

#读取海温数据并转换为np.array
with xr.open_dataset("./HadISST_npac.nc") as f:
    sst=f.sst
#查看数据信息
sst

0.3查看缺测值

num_nan=np.isnan(sst).sum()
print(num_nan) #可以看到sst数据中存在7135488个缺测数据
print(num_nan/1824) #总缺测数据个数/时间个数=缺测格点数,可以看到共有3912个缺测格点

0.4查看异常值

除了缺测值外,数据中的异常值也会对分析结果产生影响,因此数据处理前查看是否存在异常值也是十分重要的

sst_arr=np.array(sst)#将sst转化为ndarray
sst_arr=sst_arr.reshape(1,-1)#将sst_arr转化成1*26356800的二维数组
arr_u=np.unique(sst_arr)
print("去重后的数组为:\n",arr_u)#可以看到海温数据最小值为-1000,最大值为32.8,其中-1000为异常值,所以在分析时需要进行处理

1 修补异常值并去除缺测值后进行EOF分析

经过上述分析可以知道,数据中存在缺测值与异常值,所以接下来将对异常值进行修补并去除缺测值后进行EOF分解,查看效果

1.1 读取数据

import xarray as xr
import numpy as np
import datetime as dt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import cartopy.mpl.ticker as cticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.font_manager import FontProperties   
from eofs.standard import Eof
import numpy.ma as ma
import warnings
warnings.filterwarnings("ignore")

f=xr.open_dataset("./HadISST_npac.nc")
f
sst=np.array(f.sst)
sst.shape

1.2异常数据修补(利用周围格点均值代替)

处理思路(以区域1为例):
(1)首先,判断sst[time,lat,lon]格点是否存在异常值,如果存在则进行第(2)步,如果为正常值则直接跳过。
(2)sst[time,lat,lon]为异常值时,将其作为左上角点,判断其周围边长为i个格点的数据范围sst[time,lat:lat+i,lon:lon+i]是否存在正常值,如存在,则进行第(3)步,否则扩大数据范围,继续进行判断。
(3)使用sst[time,lat:lat+i,lon:lon+i]的平均值修补sst[time,lat,lon]。

说明:将全球范围分成4个区域是为了便于对异常值进行修补,其他区域修补方法见代码,思想都是一样

Image Name

#区域1数据修补
num=43
for time in np.arange(1824):
    for lat in np.arange(0,43):
        for lon in np.arange(0,85):
            if sst[time,lat,lon]==-1000.0:
                for i in np.arange(2,num):
                    data=sst[time,lat:lat+i,lon:lon+i]
                    temp=data
                    a=np.isnan(data).sum()
                    b=np.sum(data==-1000)
                    if (a+b)==i**2:
                        pass
                    else:
                        data=data[~np.isnan(data)]
                        data=data[data!=-1000].mean()
                        sst[time,lat,lon]=data
                        break

#区域2数据修补
for time in np.arange(1824):
    for lat in np.arange(0,43):
        for lon in np.arange(85,170):
            if sst[time,lat,lon]==-1000:
                for i in np.arange(2,num):
                    data=sst[time,lat:lat+i,lon-i:lon]
                    a=np.isnan(data).sum()
                    b=np.sum(data==-1000)
                    if (a+b)==i**2:
                        pass
                    else:
                        data=data[~np.isnan(data)]
                        data=data[data!=-1000].mean()
                        sst[time,lat,lon]=data
                        break
#区域3数据修补
for time in np.arange(1824):
    for lat in np.arange(43,85):
        for lon in np.arange(0,85):
            if sst[time,lat,lon]==-1000:
                for i in np.arange(2,num):
                    data=sst[time,lat-i:lat,lon:lon+i]
                    a=np.isnan(data).sum()
                    b=np.sum(data==-1000)
                    if (a+b)==i**2:
                        pass
                    else:
                        data=data[~np.isnan(data)]
                        data=data[data!=-1000].mean()
                        sst[time,lat,lon]=data
                        break

#区域4数据修补
for time in np.arange(1824):
    for lat in np.arange(43,85):
        for lon in np.arange(85,170):
            if sst[time,lat,lon]==-1000:
                for i in np.arange(2,num):
                    data=sst[time,lat-i:lat,lon-i:lon]
                    a=np.isnan(data).sum()
                    b=np.sum(data==-1000)
                    if (a+b)==i**2:
                        pass
                    else:
                        data=data[~np.isnan(data)]
                        data=data[data!=-1000].mean()
                        sst[time,lat,lon]=data
                        break

1.3 去除季节趋势

#数据标准化之前需要去除季节变化对数据的影响
sst=sst.reshape(152,12,85,170)
climate_sst=np.empty((12,85,170))
for month in np.arange(12):
    for lat in np.arange(85):
        for lon in np.arange(170):
            climate_sst[month,lat,lon]=sst[:,month,lat,lon].mean()
for month in np.arange(12):
    for lat in np.arange(85):
        for lon in np.arange(170):
            sst[:,month,lat,lon]=sst[:,month,lat,lon]-climate_sst[month,lat,lon]
sst=sst.reshape(152*12,85,170)

1.4 EOF分解(不去除缺测值)

Image Name
从eofs.standard参考文档中可以看到针对规则的缺测值,即由于陆地造成的缺测,eof.standard在分解时可以自动进行处理,无需手工处理。

lat = f.latitude
lon = f.longitude
lat = np.array(lat)
coslat = np.cos(np.deg2rad(lat))
wgts = np.sqrt(coslat)[..., np.newaxis]
solver1 = Eof(sst,center=True,weights=wgts)
eof1 = solver1.eofsAsCorrelation(neofs=3)
pc1 = solver1.pcs(npcs=3, pcscaling=1)
var1 = solver1.varianceFraction()
print("var1:\n",var1[:3])

1.5 EOF分解(去除缺测值)

虽然eof.standard模块可以对规则的缺测值进行处理,但同时本文也对缺测数据进行了删除处理,并比较两种不同数据处理方法结果是否存在较大差别

sst=sst[~np.isnan(sst)]#去除缺测值
sst=sst.reshape(1824,-1)#将sst转化为(1824,10538)的二维数组
sst.shape
lat = f.latitude
lon = f.longitude
lat = np.array(lat)
coslat = np.cos(np.deg2rad(lat))
wgts = np.sqrt(coslat)[..., np.newaxis]
solver2 = Eof(sst,center=True,weights=wgts)
eof2 = solver2.eofsAsCorrelation(neofs=3)
pc2 = solver2.pcs(npcs=3, pcscaling=1)
var2 = solver2.varianceFraction()
print("var2:\n",var2[:3])

1.6 结果分析

对比方差贡献var1(不去除缺测值时方差变量)与var2(去除缺测值时方差变量)可以看到两者差别并不大,说明缺测值的去除与否并不会对EOF分析结果产生大的影响。
同时对比参考答案,可以看到虽然本文结果与参考答案并非完全相同,但相差并不大,这可能是由于异常数据处理方法不同所导致的

print("题中参考答案:",[0.3257,0.0941,0.0824])
print("var1:",list(var1[:3]))
print("var2:",list(var2[:3]))

2 可视化与相关分析

2.1 EOF结果分析

def mapart(ax):
    '''添加地图元素'''
    ax.coastlines(color="k",lw=0.5)
    ax.add_feature(cfeature.LAND,facecolor="white")
    #设置地图范围
    ax.set_extent([105.5,275.5,-15,70],crs=ccrs.PlateCarree())#[leftlon,rightlon,lowerlat,upperlat]
    #设置经纬度标签
    ax.set_xticks([105,135,165,195,225,255],crs=ccrs.PlateCarree())
    #标注的位置是根据cartopy系统内部位置而定的,与数据本身无关
    ax.set_yticks([-15,-5,5,15,25,35,45,55,65],crs=ccrs.PlateCarree())
    lon_formatter = cticker.LongitudeFormatter()
    lat_formatter = cticker.LatitudeFormatter()
    ax.xaxis.set_major_formatter(lon_formatter)
    ax.yaxis.set_major_formatter(lat_formatter)


def eof_contourf(eofs,pcs,pers):
    
    plt.close
    fig=plt.figure(figsize=(18,10))
    projection=ccrs.PlateCarree(central_longitude=-180)
    year=range(10538)
    ax1=fig.add_subplot(3,2,1,projection=projection)
    mapart(ax1)
    lon=np.arange(105.5,275.5)
    lat=np.arange(69.5,-15.5,-1)
    p=ax1.contourf(lon,lat,eofs[0,:,:],transform=ccrs.PlateCarree())
    ax1.set_title('mode1 (%s'%(round(pers[0],2))+"%)",loc ='left')
    time=np.arange(1824)
    ax2=fig.add_subplot(3,2,2)
    ax2.plot(time,pcs[:,0],color="k",linewidth=2,linestyle="--")
    ax2.set_xticks(list(np.arange(120,1824,240)),[1880,1900,1920,1940,1960,1980,2000,2020])
    ax2.set_title('pc1 (%s'%(round(pers[0],2))+"%)",loc ='left')
    
    ax3 = fig.add_subplot(3,2, 3, projection=projection)
    mapart(ax3)
    pp = ax3.contourf(lon,lat,eofs[1,:,:],transform=ccrs.PlateCarree())
    ax3.set_title('mode2 (%s'%(round(pers[1],2))+"%)",loc ='left')
    
    ax4 = fig.add_subplot(3,2, 4)
    ax4.plot(time,pcs[:,1] ,color='k',linewidth=1.2,linestyle='--')
    ax4.set_xticks(list(np.arange(120,1824,240)),[1880,1900,1920,1940,1960,1980,2000,2020])
    ax4.set_title('pc2 (%s'%(round(pers[1],2))+"%)",loc ='left')
    
    ax5 = fig.add_subplot(3,2, 5, projection=projection)
    mapart(ax5)
    ppp = ax5.contourf(lon,lat,eofs[2,:,:],transform=ccrs.PlateCarree())
    ax5.set_title('mode3 (%s'%(round(pers[2],2))+"%)",loc ='left')
    
    ax6 = fig.add_subplot(3,2, 6)
    ax6.plot(time,pcs[:,2])
    ax6.set_xticks(list(np.arange(120,1824,240)),[1880,1900,1920,1940,1960,1980,2000,2020])
    ax6.set_title('pc3 (%s'%(round(pers[2],2))+"%)",loc ='left')
    
    #添加0线
    ax2.axhline(y=0,  linewidth=1, color = 'k',linestyle='-')
    ax4.axhline(y=0,  linewidth=1, color = 'k',linestyle='-')
    ax6.axhline(y=0,  linewidth=1, color = 'k',linestyle='-')
    
    #在图下边留白边放colorbar        
    fig.subplots_adjust(bottom=0.1)
    #colorbar位置: 左 下 宽 高 
    l = 0.25
    b = 0.04
    w = 0.6
    h = 0.015
    #对应 l,b,w,h;设置colorbar位置;
    rect = [l,b,w,h] 
    cbar_ax = fig.add_axes(rect) 
    
    c=plt.colorbar(pp, cax=cbar_ax,orientation='horizontal', aspect=20, pad=0.1)
    c.ax.tick_params(labelsize=6)
    # c.set_label('%s'%(labelname),fontsize=20)#手动设置色标
    # c.set_ticks(np.arange(1,6,1))
    plt.subplots_adjust( wspace=-0.12,hspace=0.3)

    
    plt.savefig('eof.tif',dpi=600,bbox_inches = 'tight',transparent=True, pad_inches = 0)
    plt.show()    
eof_contourf(eof1,pc1,var1[:3]*100)

在这里插入图片描述

2.2 相关分析

#由于本人对dat格式的数据并不熟练,所以首先将数据导出成了excel文件,然后利用excel软件进行处理,最后将文件(PDO.xlsx)上传,利用程序读取
import pandas as pd
df=pd.read_excel("./PDO_index.xlsx")
df.head()
df=df.iloc[16:-1,:]
df.drop(["Year"],axis=1,inplace=True)
pdo_index=np.array(df).reshape(152*12,-1)
pdo_index.shape
#计算相关系数
corr = np.corrcoef(pc1[:,0],pdo_index[:].reshape(1824))
corr
import matplotlib.pyplot as plt
time=np.arange(1824)
fig,ax=plt.subplots(figsize=(12,6))
ax.plot(time,pc1[:,0],color="red",linewidth=2,linestyle="--")
ax.plot(time,pdo_index[:],color="blue",linewidth=2,linestyle="-")
ax.set_xticks(list(np.arange(120,1824,240)),[1880,1900,1920,1940,1960,1980,2000,2020])
ax.set_title("R=0.548",loc ='left')

在这里插入图片描述

数据链接

百度网盘数据链接

(保姆式教程:从下数据到画图)python如何利用EOF分析SSTA海温异常现象并画图 保姆式教程,教你如何利用python分析1979-2004年的太平洋地区的海温异常SSTA,并且绘制出空间模态和时间序列的图片。 阅读详情

相关推荐

气象中EOF的理解

  EOF方法在气象分析中经常用到,接触好长时间,也没有弄清楚他到底在干嘛。为什么这么做是可行的?这里面的计算都有什么意义呢?   看了一些矩阵理解方面的内容后,对EOF算是有了一些粗浅的理解,现在试着去阐述一下其中的院里,好厘清自己对EOF理解。 文章目录几篇参考文章EOF分析——Empirical Orthogonal Function analysisEOF的计算过程EOF的理解内积与投影线...

留下的,留不下的 2万+

【Antlr】ANTLR 4中何时需要EOF?

最近要分析一个G4文件,想看看这个是怎么设计的,然后在第一行,遇到如下的G4文件定义// enrty 代表语法入口 enrty : expression EOF;然后看到EOF,然后在整个文件里面没有查到这个文件的定义,所以这个应该是内置的,既然是内置的,那么这个代表什么意思呢?经过查询发现ANTLRWorks2中的TestDriver在何时接受不带语法和显式EOF的语法以及何时不接受语法的方法似乎有些挑剔。

九师兄 635

EOF_EOFMatlab_;SST_EOF分析_eof_经验正交函数_

对海表面温度进行经验正交函数分解,和大气环流指数进行滑动相关,进行EOF分析。

从多项式到神经网络:DPD算法演进史及其在RRU中的工程实现

本文系统梳理了数字预失真(DPD)算法从经典多项式模型、查表法到神经网络驱动的演进历程,并深入探讨了其在基站射频单元(RRU)中面临的硬件资源约束与工程实现挑战。文章为通信设备的技术决策者提供了算法选型、性能权衡及混合架构设计的实用评估框架,助力在性能、功耗与成本间找到最优平衡。

cherry的博客 1073

DeepSeek提示词实战:从零到百的AI角色扮演指南(附100+场景模板)

本文提供了一份详尽的DeepSeek提示词实战指南,重点讲解AI角色扮演的核心技巧。通过“角色扮演四要素法”和五大核心场景模板,帮助用户从零开始掌握如何设计高质量提示词,有效提升AI输出的深度与精度,并附赠100+实用场景模板供参考。

weixin_29305337的博客 567

MFC工具栏增加EditBox和Button

1. 在资源视图中增加EditBox, Button控件,ID分别为ID_EDIT_SEED, ID_BTN_SEED 2. 在CMainFrame中增加成员变量 CMFCToolBarEditBoxButton *m_editSeed; CMFCToolBarButton *m_btnSeed; 3. 在CMainFrame::OnCreate中创建控件 m_editSeed = new CMFCToolBarEditBoxButton(ID_EDIT_SEED, GetCmdMgr()-&.

学习是为了探索这个世界的本质 799

光耦合MOSFET(Optocoupled MOSFET,简称OCMOS)

光耦合MOSFET(Optocoupled MOSFET,简称OCMOS)是一种将光电耦合器和MOSFET结合在一起的器件,用于实现电气隔离和信号传输。选择合适的光耦合MOSFET时,还需要考虑具体应用的需求和环境条件,以确保最佳性能和可靠性。- 最大输出电流和电压:确保所选型号能够满足电路的最大电流和电压需求。- 最大输出电流和电压:确保所选型号能够满足电路的最大电流和电压需求。- 隔离电压:根据应用环境的电气隔离要求选择合适的隔离电压。- 隔离电压:根据应用环境的电气隔离要求选择合适的隔离电压。

eydj2008的专栏 1719

别再手动抄表了!用PaddleOCR超轻量模型5分钟搞定数字仪表识别(附完整Python代码)

本文介绍如何利用PaddleOCR超轻量模型快速实现工业仪表数字识别自动化。通过5行核心代码和关键预处理步骤,显著提升识别准确率至98.2%,解决传统OCR在工业场景中的体积大、抗干扰弱等问题,并提供完整Python代码和边缘部署优化方案。

weixin_31062533的博客 344

PDS联合Modelsim仿真报错(Error loading design) line6报错 -novopt已禁用 (全网最详细!!!!!)

发现这个文档是Pango自动生成的,也就是说每一次我们点击"Run Behavior Simulation"的时候,PDS都会自动生成一次这个./run_behav_simulate.tcl文件,所以就算我们这一次把这个文件修改了,我点击"Run Behavior Simulation"想让它再跑一次正确的仿真,它此时又重新生成了这个带有-novopt的错误tcl文件,仿真依旧不能成功。问题真的是出在这里,那我们修改这个./run_behav_simulate.tcl文件就好了,但事实并非如此!

weixin_74058783的博客 3185

RH850从0搭建Autosar开发环境【6】- Davinci Configurator之CanIf模块配置详解(下)

RH850从0搭建Autosar开发环境【6】- Davinci Configurator之CanIf模块配置详解(下)

weixin_42412049的博客 2577

基于YOLOv10的建筑图纸物体检测系统:从数据集构建到UI界面实现

高度结构化的图形元素标准化的符号和标注系统多层次的细节表达精确的尺寸和位置关系。

YOLO 386

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

上一篇: 14、基于TensorFlow的房价预测
下一篇: LaTeX系列|2、LaTeX中参考文献的管理
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱转呼啦圈的小兔子

觉得文章不错?请小编喝杯咖啡吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值