前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >气象处理技巧—时间序列处理2

气象处理技巧—时间序列处理2

作者头像
自学气象人
发布2023-06-21 15:04:02
7490
发布2023-06-21 15:04:02
举报
文章被收录于专栏:自学气象人

时间序列处理2

在前面一个章节,我们学习了常用的时间序列的生成方法,这一节,则是非常方便的如何使用xarray进行数据集的时间维度的抽取合并操作。逐步的学习,摸鱼咯大佬的花式索引学会也不是什么难事。

这一章的框架是按照xarray提供的不同的数据抽取方式,逐项讲解xarray下的时间序列的抽取,在最后,还会涉及一些不同数据集按照时间维进行合并的方法。当然,我还是才刚刚才探讨这个方向,肯定有些贻笑于方家的地方,希望和大家一起讨论学习。

ps:在后续写文章的时候,发现还有可以啰嗦的,那就干脆再开一章,下一章来讨论groupby、merge等合并归类拼接时间等操作。

数字索引取值法

数字索引切片是最基础的切片方式,逻辑理论完全基于列表切片和numpy的array切片,这里,我们就不得不简单回顾一下数字索引切片。数字索引切片的基本逻辑有

  • 切片是左闭右开的,切片的右侧是不包含在内的。
  • 如果数字索引超过总长度,则默认将元素全部取出。
  • 使用数字索引时,你无需知道内部元素具体是什么。下面先导入这个做实验的再分析资料:
代码语言:javascript
复制
import xarray as xr
import numpy as np
import pandas as pd
file=r'F:\aaaa\air.mon.mean.nc'
ds=xr.open_dataset(file)
ds

该数据集是气温的水平空间、时间数据集。 然后提取单独提取时间序列以方便操作,实际上使用时一般是直接在上述air的相关维度进行操作。

代码语言:javascript
复制
ds.time

这是一个长度为867的时间序列,每个序列以纳秒为最小单位,宏观分辨率为月,起于1948年1月,终于2020年3月。 数字索引取值法的语法规则非常类似于列表、ndarray,只须给出start、end、step三个参数即可,这三个参数不是全部必须的。 例如我想提取前15个元素值:

代码语言:javascript
复制
ds.time[0:15]

我想提取前30个元素,但每两月取一次值:

代码语言:javascript
复制
ds.time[0:30:2]

上面1948-01后面直接是1948-03,2月被跳过了。数字索引的劣势是,不依靠内部元素,则取值不方便,例如我现在需要提取1989-01到1999-04,则数字索引方法还需要计算1948-01到1989-01的距离才能确定索引值。 若数字索引超过范围,则会默认将内部元素全部取出,以下代码,要求提取到1500索引,但我们知道这个序列仅有867长度,程序则会将现有值全部取出。

代码语言:javascript
复制
ds.time[0:1500]

. loc 取值法

重量级的来咧。loc取值法可以说才是xarray对时间序列取值的神,通过简单了解,你就可以飞速处理时间序列。 loc是xarray基于pandas的loc语句进行开发的,所以完全遵循pandas的loc语句的规则,loc语句拥有两种确定取值范围的方法,一是以内部存放值为单位进行取值,二是以一个布尔值表确定取值,下面分别介绍之。 loc按照存放值可能性的切片法

  • 要求为用来索引的值应该是这个时间序列含有的,不能存在不含有的情况
  • 时间格式从视觉上是否一致不重要,程序会自动判断。下栏使用的索引时间是字符串格式,以日为单位,程序会自动识别到相同的时间
  • loc切片遵循最终结果与索引对应原则,比如下面程序,右端要求取到1949年12月,则最终结果有1949年12月,左闭右闭
  • loc切片与列表切片类似,拥有切片步长参数,可以按照切片步长跳着跳着切取
代码语言:javascript
复制
data=ds.time.loc['1948-01-01':'1949-12-01']
data
代码语言:javascript
复制
data=ds.time.loc['1948-01-01':'1949-12-01':2]
data
  • 按照需要数据提前封装一个列表进行loc索引切片的方法
  • 该方法适用于提取某些特定值,而非连续阶段值或可按固定步长提取的值
  • 例如我要提取1949-10-01,1950-10-01,1999-10-01,显然不可能连续切取或者用步长切取,于是我们可以提前封一个列表,然后再用loc切取
  • 至于说这个列表要怎么生成,那就有很多方法
代码语言:javascript
复制
i_need_month=['1949-10-01','1950-10-01','1999-10-01']
data=ds.time.loc[i_need_month]
data=ds.time.loc[['1949-10-01','1950-10-01','1999-10-01']]#与上面效果一致,只是代码合并了
data

loc按照布尔值表的切片法 该方法允许使用者给loc传入一个布尔值表(True、False),然后按照这个布尔值表确定取值,真则取,假则弃。例如我们生成一个仅第一个为真,其余全为假的布尔值表,则仅会提取第一个真对应的1948-01的数据:

代码语言:javascript
复制
bool_array=[True]+[False]*866
data=ds.time.loc[bool_array]
data

那么压力来到如何生成需要的布尔值表,因为我们不可能像上面例子这样手动逐个生成布尔值表。这里不妨看这样一个例子:

代码语言:javascript
复制
bool_data=np.array('1949-01-01').astype('datetime64[D]')<np.array('1949-05-01').astype('datetime64[D]')
bool_data
True

在python中,允许时间进行比较,并生成布尔值,上面判定1949年1月是小于1949年5月的,所以上式成立,返回真。那下面就简单了,我们假定对时间序列进行是否大于1949年1月的判定,并返回一个布尔值表。

代码语言:javascript
复制
bool_array=np.array('1949-01-01').astype('datetime64[D]')<ds.time

接下来只要将这个表放进loc语句里,就可以提取对应数据了:

代码语言:javascript
复制
data=ds.time.loc[bool_array]
data

但是我们发现缺失了1949年1月,这是因为在生成布尔值表时,我们给出的逻辑为绝对的小于,1949-01是=1949-01的,所以返回假,要提取到1月,我们可以提前到1948-12,或者将逻辑符号改为<=。

代码语言:javascript
复制
bool_array=np.array('1949-01-01').astype('datetime64[D]')<=ds.time
data=ds.time.loc[bool_array]
data

下面就是运用逻辑运算,进行更加复杂的一波流取值。和【&】逻辑就是数学里的取交集,或【|】逻辑就是数学里的取并集。我们先提前用两个简单的布尔表学习一下。

代码语言:javascript
复制
a=np.array([True,False,False])
b=np.array([True,True,False])
c=a&b
c
array([ True, False, False])

和逻辑下,当两个对应位置的逻辑值都为真时,才返回真,其余全假。

代码语言:javascript
复制
a=np.array([True,False,False])
b=np.array([True,True,False])
c=a|b
c
array([ True,  True, False])

或逻辑下,当两个对应位置的逻辑值只要一个为真,就返回真。 提取1955年1月到1966年12月,1988年1月到1988年6月,1999年5月到2001年1月的数据,我们就可以在一条命令中实现。这个命令是嵌套过的,先进行和运算,再或运算,如果不能理解,可以用初中数学那个在x轴上画取值范围的方法去套:

代码语言:javascript
复制
import datetime
t1=pd.to_datetime(datetime.date(1955,1,1))
t2=pd.to_datetime(datetime.date(1967,1,1))
t3=pd.to_datetime(datetime.date(1988,1,1))
t4=pd.to_datetime(datetime.date(1988,7,1))
t5=pd.to_datetime(datetime.date(1999,5,1))
t6=pd.to_datetime(datetime.date(2001,2,1))
data=ds.time.loc[((ds.time>=t1)&(ds.time<=t2))|
                 ((ds.time>=t3)&(ds.time<=t4))|
                 ((ds.time>=t5)&(ds.time<=t6))]
data

如何返回固定月份的数据 在实验中,我们要求仅返回12月的数据,怎么进行呢,最先想到的,就是使用步长为12,每十二个月进行一次切片:

代码语言:javascript
复制
data=ds.time.loc['1949-12-01'::12]
data

在实验中,我们要求仅返回11、12月的数据,又怎么进行呢,显然切片法解决不了,下面引入xarray继承pandas的isin方法。isin返回的是布尔值,刚好满足loc取值参数的要求。

代码语言:javascript
复制
data=ds.time.loc[ds.time.dt.month.isin([11,12])]

dt.month可以直接在time内部比较时间,dt有许多模块,这里month只有1-12可选,若是day,则对应为1-31。例如:

代码语言:javascript
复制
data=ds.time.loc[ds.time.dt.day.isin([1])]

上面要求判定每个时间是不是1号,前面我们知道确实全部都是1号,则整个时间序列全部符合要求,全部返回True,全部被提取出来了。若判定为2号,则全部不符合要求,全部返回False,数据全部舍弃,返回一个空数组。

代码语言:javascript
复制
data=ds.time.loc[ds.time.dt.day.isin([2])]

进一步的,我要看这些时间,是不是在15点,则:

代码语言:javascript
复制
data=ds.time.loc[ds.time.dt.hour.isin([15])]

在前面我们已经知道,每个时间都是1日零时零分零秒的,则全部不是15点,全部不符合要求,故返回一个全为假的布尔表,loc根据这个全为假的布尔表,返回一个空数组。 如何对数据进行操作 上面对时间序列的处理,都是讲明原理,仅仅对时间序列进行操作,下面我们将对air进行相关操作。在loc语句中,各维相互之间不干扰,用自己的方法提取即可,唯一需记住,维度的相关位置非常重要,时间是第一维,则时间切片也在第一维:

代码语言:javascript
复制
air_1949_1950=ds['air'].loc['1949-01-01':'1950-12-01',:,:]
air_1949_1950

下面这个与上面相同,要求切取1955年1月到1966年12月,1988年1月到1988年6月,1999年5月到2001年1月,北纬10-50,东经90-100的数据,则提取语句为:

代码语言:javascript
复制
import datetime
t1=pd.to_datetime(datetime.date(1955,1,1))
t2=pd.to_datetime(datetime.date(1967,1,1))
t3=pd.to_datetime(datetime.date(1988,1,1))
t4=pd.to_datetime(datetime.date(1988,7,1))
t5=pd.to_datetime(datetime.date(1999,5,1))
t6=pd.to_datetime(datetime.date(2001,2,1))
air=ds['air'].loc[((ds.time>=t1)&(ds.time<=t2))|
                 ((ds.time>=t3)&(ds.time<=t4))|
                 ((ds.time>=t5)&(ds.time<=t6)),50:10,90:100]
air

很明显,时间切片用的布尔值表,经纬切片用的元素值索引,但是互不干扰。上面就是loc常用的一些取值套路了,当然根据官网介绍,还有一些方法,但是这些方法很少使用例如:

代码语言:javascript
复制
data=ds.time.loc[dict(time=slice('1949-01-01','1950-12-01'))]

感兴趣的可以去https://docs.xarray.dev/en/stable/user-guide/indexing.html#assigning-values-with-indexing 这个网址学习,这是xarray官网关于索引和筛选的介绍。

. sel 取值法

该取值法与loc不同,不接受直接切片,必须指出对应维,允许模糊搜索。

代码语言:javascript
复制
data=ds.time.sel(time='1949-01-01')
data

上面的括号里必须指出这是对time的操作,否则报错。sel也允许传入一个列表来提取数据

代码语言:javascript
复制
data=ds.time.sel(time=['1949-01-01','1950-01-01'])

上面提到的不接受直接切片仅仅相对于loc的快捷直接切片如来说的,sel还是接受间接切片的:

代码语言:javascript
复制
data=ds.time.sel(time=slice('1949-01-01','1950-01-01'))

sel取值,最常用的是模糊搜索这个参数,这个参数是loc没有的,通过不同的关键字,实现模糊搜索。举个例子,现在有1959-12-29,假定我需要一个最近的数据来代替这一日的数据,则可以通过sel来实现,默认情况下sel的该参数为None,这时因为时间序列里没有和1959-12-29一致的将会报错,并提示你修改method参数搜索方式。

代码语言:javascript
复制
data=ds.time.sel(time='1959-12-29',method=None)

下面将这个参数修改为最接近:

代码语言:javascript
复制
data=ds.time.sel(time='1959-12-29',method='nearest')

程序说最接近这个日期的为1960年1月,这时没有问题的,因为仅相差两天,而12-29与上一个日期节点12-01相差28天。这里引发一个问题,就是跨月又跨年,如果规定12月某天仅能用12月数据代表,那就不合适了,于是继续修改method为pad,他的意思是向搜索这个日期最近的前一个日期完成搜索,那么1959-12-29的前一个时间节点就为1959-12-01,符合要求了:

代码语言:javascript
复制
data=ds.time.sel(time='1959-12-29',method='pad')
  • 总的来说,sel与其名字相匹配,更加主打select,直接加了一个method搜索参数,而loc更加强调locate,强调点对点定位
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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