气象数据处理技巧—时间序列

[复制链接]

时间序列处理

由于气象上经常研究长期气候变化,这些数据动辄上十年,上百年的再分析数据也不少,如何提取这些时间序列,如何生成时间序列,便成为一个问题,之前看到摸鱼大佬作气候研究时使用xarray花式索引提取数据将我震的五体投地,于是也学习了一下时间序列的处理方法与经验。这里分为三部分,一是如何生成时间序列;二是使用xarray提取数据集里的时间序列;三是如何在绘图中使用定制化时间的显示方式。本章节是第一块的内容。

时间序列

作为一门以不间断观测,积累数据以进行研究的科学,长期保存的数据如何进行分析,这就牵扯到时间序列上了。以各气象观测站观测数据为例,常规六要素是每分钟采集一次,每小时报送一次整点报文,每天形成日数据,每月形成月报表,每年形成年报表。那么在这个过程中产生的时间序列就很恐怖了,时间序列的跨度也很大,从秒、时、日、月到年,处理时间序列成为一个不得不学习的内容。
这里还仅仅谈论观测数据,上面还有更多的再分析气候数据,动辄以十年为单位,这些数据也不好处理。
幸运的是,经过python多年发展,我们可以利用datetime、pandas、xarray甚至matplotlib方便快捷的处理时间序列,这些功能多种多样,而且互相之间多有联系,能掌握这项技能,搞科研可以事半功倍。
下面是简单介绍使用不同的库包进行时间序列的生成。

使用datetime生成时间序列

datetime库有三个最常使用的时间类,分别是date、time、datetime。

  • date是日期生成器,即年月日格式,常用参数有year、month、day。
  • time是时间生成器,即时分秒格式,常用参数有hour、minute、second。
  • datetime是日期时间生成器,即年月日时分秒格式,常用参数有year、month、day、hour、minute、second。

三种生成器是不一样的。即日期与时间不是一个类。date是可以含有年、月、日三个时间尺度,但是不含有时分秒;time同理,但datetime则既可以表示日期,又可以同时表示时间。
这里以date为例举出时间序列的生成。我们必须先生成首尾时间或其中的任意一个,才能进行时间序列的生成。我们可以使用关键字参数跟随的方式生成日期,例如:

date=pd.date_range(start='2023-01-01',periods=31,freq='D')date

也可以使用位置参数跟随的方式生成日期,这种方法简便些:

date=pd.date_range(end='2023-12',periods=12,freq='MS')date

然后,可以使用numpy.arange函数生成时间序列:

import pandas as pdimport numpy as npimport datetimeimport matplotlib.dates as mdatesstart=datetime.datetime(2023,1,1,8)end=datetime.datetime(2023,1,1,20)timedelta=datetime.timedelta(hours=1)datetime_array=mdates.drange(start,end,timedelta)datetime_array

cf69ef38cbbf367af19b662a9d477cc2.png 这里可以修改步长,使生成的时间序列变化步长。与Python规则一致,生成的时间序列是左闭右开的,所以没有4月7日。但是这种方法有个问题,即仅能以天day为唯一划分步长单位,不能生成其他的时间步长。上面这个时间生成序列的1,其实是1D,即

这是因为date函数的year、month、day都是必须参数,所以生成的单位就是日。为了解决这个问题,我们可以引入numpy的时间处理模块,将时间单位统一。

c3db1a0aa8a9fe3e3a439b86498d67ee.png 上述程序的含义是生成的date1、date2的时间单位强制变换为月,这时时间单位就统一为月,可以生成逐月序列而非逐日序列,然后再强制变换为日单位。
为何会这么复杂,是因为datetime库没有时间序列生成器,一定要借助其他方式才能生成时间序列。
还有一种列表推导的方式生成时间序列,这是和鲸社区上ID名为啸不露齿写的,应该还是南信的校友,似乎更好理解一些。

表中进行了嵌套推导,给每一年推导12个月,然后推导2年,形成时间序列。最后还是需要使用pandas将时间列表转换为时间序列。
说到底,就是因为datetime自身没有携带简便的时间序列生成器,所以需要变来变去。但是为啥仍然要列出这一节?不难猜出——datetime其实是下面这些简便方法的基底。

使用numpy生成时间序列

从上面我们已经不难看出,比datetime更厉害的其实就是numpy,numpy的array自身带有一个type属性,合理使用type属性可以花式变换时间的单位格式。numpy还可以直接使用字符串生成时间序列,并指定type。比如:

1a08e91dcccc854b811209af05393f87.png 通过给与不同的type,生成的数组格式也是不一样的,上面指定格式为日Day,若指定为月则

b37d586c90837e3139eabee929d9296a.png 数组值变为与月单位对应,不再含有日单位,同样还可以加上小时单位

86d015cd7e533e4d526003ca4c8046e0.png 这里数组值变为带小时数据,type也对应变化。
这样,我们可以更加方便的生成时间序列。例如生成一个月的每日数据:

bb6824f28750251f64c4e04028acd805.png 通过给与不同的dtype,可以方便的调整生成序列的步长,比如更换为以月为单位:

c1d1a573fce12f763840e3e59d662093.png 这里已经被收束到月单位,所以没有-01这个结尾,如果想要这个结尾,可以再变为D格式

1c294c41d95d9d7bc42bba36df18072a.png
  • np.timedelta64

从这个函数名字不难看出,这是numpy库给出的一个专门计算时间差值的函数。datetime也有类似的,但是他最大的时间单位为小时,np.timedelta64不同,他可以计算日、月、年等更大的时间差。举一个简单的例子,如何简单的将世界时变换为北京时,我们知道绝大数再分析资料都是以UTC存储的,但是BJC和UTC相差8个小时,这时便可以使用这个函数轻松换算。
UTC= d243ed382362edf0f9b5fd29f28c895d.png

79d27d8a74f8181db727d98e4fb21a06.png 这样就在时间上变为北京时间了。
当然,与专门的程序员需要的精确时间相比还有一定差距,不过底线条件能用就行。

使用pandas生成时间序列

pandas是当年处理金融数据出名的,而金融数据时间性较强,所以pandas也有极强的时间序列处理能力。
pandas提供了一个内置函数pandas.date_range来生成时间序列。

  • 是一个多格式时间序列生成器,专门用于生成时间,其常用关键字参数如下
    start:开始时间,可以是时间字符串或者时间格式。
    end:结束时间,可以是时间字符串或者时间格式。
    periods:生成的时间序列长度,整数int。
    freq:时间的单位。
  • start与end很好理解,即这个时间序列的开始时刻,这个开始时刻可以是字符串格式的时间,例如‘2021-01-01’,即代表开始时间为2023年1月1日。除开字符串外,还可以使用datetime生成的时间,例如

上述两种时间格式都可被接受。

  • periods,类似于np.arange(s,e,n)的这个n,用于确定时间序列的具体数量,若指定这个参数,则生成等分时间,例如
8f73bb57a095762e02a1334c27fd6cd8.png
  • freq:时间序列差值的单位,但是start,end,periods,freq,不能四个同时使用,很好理解,比如上面这个序列,既指定开始又指定结束时间,还要生成固定数量时间,还要指定时间单位,这是不可能实现的,上述四个参数最多只能同时使用三个。
  • 使用pd.date_range生成逐时数据
    这里通过指定开始时间,结束时间,时间单位来生成一个时间序列:
72a08f8d7476373113cf37d849ec5835.png
  • 使用pd.date_range生成逐日数据
    这里通过指定开始时间,生成时间序列数量,时间单位来生成一个时间序列:
cd08a8e0c5c73526e434c21bfa84d461.png
  • 使用pd.date_range生成逐月数据
    这里通过指定结束时间,生成时间序列数量,时间单位来生成一个时间序列:
270fff70a1868fb3cff101a106f4d9fa.png
  • 关于啸不露齿提出的一个问题的解决方案

5d14c5c65414f28afe22e333de8256dd.png 其实这个问题源自freq的时间维的单位定位不一样,例如月单位缩写M,其实是month end的缩写,那么生成逐月数据,必为每月的最后一天,例如:

e513e0ff5f450fda58532f821d7bb4e2.png 只须更改freq的时间单位即可,这里修改为MS,即month start:

18b7aac95f603d42e0c8e9b3766d175f.png 上述完全一致的代码,通过修改freq的格式即可解决这个问题。不过这也有个问题,例如我想生成以12月为序列的逐年时间序列,应该怎么做呢,用freq='YS'肯定是不行了,因为会返回到每年的开始:

a05e393d338a11cae1fed2de40d103a9.png 但同样,一年的时间间隔等于12个月,于是可以灵活变换单位来实现啸不露齿的目的,设置12月时间间隔,而非一年时间间隔: 53cb734e650f7af30b212c0dd3b8ee3b.png

  • 使用pd.offsets对生成的时间数列进行修改
    假设,我需要生成每个月的2日为一年的时间序列,我们可以先生成每个月的1日,然后通过时间偏移对日期进行腾挪。 2c6790d2934cc448ce3409174fcee1e7.png

使用matplotlib.dates模块生成时间序列

没有看错,matplotlib竟然也打包了时间生成功能。不过,这个时间生成的是数字时间,而非标准时间,而且最大时间常用时间单位为hour小时,不能生成天、月、年。要生成时间序列,通过matplotlib.dates.drange函数来实现。

06980c4db8e5c9079da81796f813fe46.png 这些数字就是matplotlib时间,若人工识别,还需要变换:

7ba31c49527ae0a29207202a90f08db7.png

以上就是常用的时间序列的生成方式了,下一次推送,将是如何使用xarray对数据集的时间维度进行处理。

回复

举报 使用道具

相关帖子

全部回帖
暂无回帖,快来参与回复吧
懒得打字?点击右侧快捷回复 【吾爱海洋论坛发文有奖】
您需要登录后才可以回帖 登录 | 立即注册
超级爸爸
活跃在半小时前
快速回复 返回顶部 返回列表