Python气象数据处理进阶之Xarray(4):计算(完结)

最近开始忙了。更新的速度会放缓一些,见谅。
这部分主要讲一下Xarray内部的计算函数,可以直接对dataarray进行操作。
还是以举例子的方式来展示。
先创建一个dataarray

import xarray as xr
da = xr.DataArray(np.random.RandomState(0).randn(2, 3),[('x', ['a', 'b']), ('y', [10, 20, 30])])
print(da)
#<xarray.DataArray (x: 2, y: 3)>
#array([[ 1.76405235,  0.40015721,  0.97873798],
#       [ 2.2408932 ,  1.86755799, -0.97727788]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30
ex1 基本运算

首先是基本的加减乘除(以减法为例)

print(da - 3)
#<xarray.DataArray (x: 2, y: 3)>
#array([[-1.23594765, -2.59984279, -2.02126202],
#       [-0.7591068 , -1.13244201, -3.97727788]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

同时还支持python自身的一些函数

print(abs(da))
#<xarray.DataArray (x: 2, y: 3)>
#array([[1.76405235, 0.40015721, 0.97873798],
#       [2.2408932 , 1.86755799, 0.97727788]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

也支持numpy和scipy的一些计算函数(但并不是所有,很多涉及到维度或者什么的就不能了)

print(np.sin(arr))
#<xarray.DataArray (x: 2, y: 3)>
#array([[ 0.9813841 ,  0.38956314,  0.82979374],
#       [ 0.78376151,  0.95628847, -0.82897801]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

可以使用where函数进行条件更改

print(xr.where(da > 0, 'positive', 'negative'))
#<xarray.DataArray (x: 2, y: 3)>
#array([['positive', 'positive', 'positive'],
#       ['positive', 'positive', 'negative']], dtype='<U8')
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

使用@进行矩阵乘法,使用round进行四舍五入,使用.T数组转置等等。这部分略去代码了,可以自己试验

ex2 缺测处理

这部分是针对缺测的多种处理方法:
首先建立一个带有缺测的dataarray

x = xr.DataArray([0, 1, np.nan, np.nan, 2], dims=['x'])
print(x)
#<xarray.DataArray (x: 5)>
#array([ 0.,  1., nan, nan,  2.])
#Dimensions without coordinates: x

判断是否缺测,还有notnull()函数

print(x.isnull())
#<xarray.DataArray (x: 5)>
#array([False, False,  True,  True, False])
#Dimensions without coordinates: x

判断有几个未缺测

print(x.count())
#<xarray.DataArray ()>
#array(3)

去除缺测

print(x.dropna(dim='x'))
#<xarray.DataArray (x: 3)>
#array([0., 1., 2.])
#Dimensions without coordinates: x

将缺测填为-1

print(x.fillna(-1))
#<xarray.DataArray (x: 5)>
#array([ 0.,  1., -1., -1.,  2.])
#Dimensions without coordinates: x

还有一种方法是上一篇文章将的插值,将缺测插补。

ex3 改变shape的运算

当涉及到运算后维度shape发生变化后,xarray提供了自己的一些方法。
先建立数组:

da = xr.DataArray(np.random.RandomState(0).randn(2, 3),[('x', ['a', 'b']), ('y', [10, 20, 30])])
print(da)
#<xarray.DataArray (x: 2, y: 3)>
#array([[ 1.76405235,  0.40015721,  0.97873798],
#       [ 2.2408932 ,  1.86755799, -0.97727788]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

如果直接当作np数组进行sum,,mean等运算会报错,因为运算后数据结构发生了变化。
正确的操作是:

print(da.sum(dim='x'))
#<xarray.DataArray (y: 3)>
#array([4.00494555e+00, 2.26771520e+00, 1.46010423e-03])
#Coordinates:
#  * y        (y) int64 10 20 30

要指明求和的方式,而不是类似np.sum(),直接填充第几个轴。

特别需要注意的是求平均时缺测的处理
print(xr.DataArray([1, 2, np.nan, 3]).mean())
#<xarray.DataArray ()>
#array(2.)
print(xr.DataArray([1, 2, np.nan, 3]).mean(skipna=False))
#<xarray.DataArray ()>
#array(nan)

默认是跳过nan的

ex4 滑动运算

这部分主要是计算滑动平均或者滑动标准差的,这部分比较抽象,我尽可能表述的通俗一些。
新建数组:

da = xr.DataArray(np.arange(0, 7.5, 0.5).reshape(3, 5),dims=('x', 'y'))
print(da)
#<xarray.DataArray (x: 3, y: 5)>
#array([[0. , 0.5, 1. , 1.5, 2. ],
#       [2.5, 3. , 3.5, 4. , 4.5],
#       [5. , 5.5, 6. , 6.5, 7. ]])
#Dimensions without coordinates: x, y

通过指令进行滑动平均,mean换位std即为滑动标准差:

print(da.rolling(y=3).mean())
#<xarray.DataArray (x: 3, y: 5)>
#array([[nan, nan, 0.5, 1. , 1.5],
#       [nan, nan, 3. , 3.5, 4. ],
#       [nan, nan, 5.5, 6. , 6.5]])
#Dimensions without coordinates: x, y
print(da.rolling(y=3, center=True).mean())
#<xarray.DataArray (x: 3, y: 5)>
#array([[nan, 0.5, 1. , 1.5, nan],
#       [nan, 3. , 3.5, 4. , nan],
#       [nan, 5.5, 6. , 6.5, nan]])
#Dimensions without coordinates: x, y

这指的是对y轴(即列)取滑动步长为3进行平均,注意这个xr.rolling和np.roll是不一样的。xarray的rolling函数是指定滑动步长,即窗口,而不是指数组滚动。
以第一行数据为例子,原数组第一行为【0 , 0.5, 1 , 1.5, 2】
滑动后为[(0+0.5+1)/3=0.5,(0.5+1+1.5)/3=1,(1+1.5+2)/3=1.5]
而np.roll的效果是变成[1,1.5,2,0,0.5]
但是这种滑动平均的缺点是两侧变成了缺测。
xarray给出了一个参数可以规定最短窗口,也就是说在数据两端,窗口不足时,可以减小窗口,延长滑动后的结果。

print(da.rolling(y=3, center=True, min_periods=2).mean())
#<xarray.DataArray (x: 3, y: 5)>
#array([[0.25, 0.5 , 1.  , 1.5 , 1.75],
#       [2.75, 3.  , 3.5 , 4.  , 4.25],
#       [5.25, 5.5 , 6.  , 6.5 , 6.75]])
#Dimensions without coordinates: x, y

实现了滑动后长度不改变,但是两侧数据是存在误差的。

ex5 权重运算

依然还是新建一组数据:

coords = dict(month=('month', [1, 2, 3]))
print(coords)
#{'month': ('month', [1, 2, 3])}
prec = xr.DataArray([1.1, 1.0, 0.9], dims=('month', ), coords=coords)
print(prec)
#<xarray.DataArray (month: 3)>
#array([1.1, 1. , 0.9])
#Coordinates:
#  * month    (month) int64 1 2 3
weights = xr.DataArray([31, 28, 31], dims=('month', ), coords=coords)
print(weights)
#<xarray.DataArray (month: 3)>
#array([31, 28, 31])
#Coordinates:
#  * month    (month) int64 1 2 3

prec比如是降水数据,weight是权重数组
对prec进行权重处理:

#这两种方法等价
weighted_prec = prec.weighted(weights)
#weighted_sum = (prec * weights).sum()

加权求和:

print(weighted_prec.sum())
#<xarray.DataArray ()>
#array(90.)

结果相当于311.1+281+31*0.9

ex6 聚块

新建数组:

import pandas as pd
x = np.linspace(0, 10, 300)
t = pd.date_range('15/12/1999', periods=364)
da = xr.DataArray(np.sin(x) * np.cos(np.linspace(0, 1, 364)[:, np.newaxis]),
                  dims=['time', 'x'], coords={'time': t, 'x': x})
print(da)
#<xarray.DataArray (time: 364, x: 300)>
#array([[ 0.        ,  0.03343858,  0.06683976, ..., -0.48672119,
#        -0.51565952, -0.54402111],
#       [ 0.        ,  0.03343845,  0.06683951, ..., -0.48671934,
#        -0.51565756, -0.54401905],
#       [ 0.        ,  0.03343807,  0.06683875, ..., -0.4867138 ,
#        -0.51565169, -0.54401285],
#       ...,
#       [ 0.        ,  0.0182217 ,  0.03642301, ..., -0.26522911,
#        -0.28099849, -0.29645358],
#       [ 0.        ,  0.01814439,  0.03626848, ..., -0.26410385,
#        -0.27980632, -0.29519584],
#       [ 0.        ,  0.01806694,  0.03611368, ..., -0.26297658,
#        -0.27861203, -0.29393586]])
#Coordinates:
#  * time     (time) datetime64[ns] 1999-12-15 1999-12-16 ... 2000-12-12
#  * x        (x) float64 0.0 0.03344 0.06689 0.1003 ... 9.9 9.933 9.967 10.0

比如说气象上需要求逐周,逐旬,逐候的数据,处理起来很麻烦,我们则可以使用这个方法先将其聚块,再进行平均。

print(da.coarsen(time=7).mean())
#<xarray.DataArray (time: 52, x: 300)>
#array([[ 0.        ,  0.03343693,  0.06683647, ..., -0.48669718,
#        -0.51563408, -0.54399428],
#       [ 0.        ,  0.03342539,  0.06681339, ..., -0.48652913,
#        -0.51545604, -0.54380644],
#       [ 0.        ,  0.03340141,  0.06676547, ..., -0.48618016,
#        -0.51508632, -0.54341639],
#       ...,
#       [ 0.        ,  0.0193641 ,  0.03870654, ..., -0.28185754,
#        -0.29861556, -0.31503961],
#       [ 0.        ,  0.01883484,  0.03764862, ..., -0.2741539 ,
#        -0.29045391, -0.30642905],
#       [ 0.        ,  0.01829859,  0.03657671, ..., -0.26634833,
#        -0.28218424, -0.29770455]])
#Coordinates:
#  * time     (time) datetime64[ns] 1999-12-18 1999-12-25 ... 2000-12-09
#  * x        (x) float64 0.0 0.03344 0.06689 0.1003 ... 9.9 9.933 9.967 10.0

可以看到,coarsen(time=7)将时间轴每7天固定在一起,通过平均函数,得到了逐周的平均结果。
但是这里364是可以整除7的,如果不能刚好整除的话,还提供了一些参数来处理边界结果。
boundary='trim',或者boundary='pad'.
trim是指末尾多余的不参与计算,或者叫修建多余条目。
pad指填充nan到不足的地方,补全为可以整除的更大数据再进行聚块。

还可以通过coord_func函数返回不同的坐标信息。
比如说默认下,7天平均,结果的时间是第4天,就是中间那天,但是通过coord_func={'time': 'min'}可以指定时间为最小的那天。

ex6 差分和积分计算

新建数据:

da = xr.DataArray(np.arange(8).reshape(4, 2), dims=['x', 'y'],coords={'x': [0.1, 0.11, 0.2, 0.3]})
print(da)
#<xarray.DataArray (x: 4, y: 2)>
#array([[0, 1],
#       [2, 3],
#       [4, 5],
#       [6, 7]])
#Coordinates:
#  * x        (x) float64 0.1 0.11 0.2 0.3
#Dimensions without coordinates: y

中央差计算差分:

print(da.differentiate('x'))
#<xarray.DataArray (x: 4, y: 2)>
#array([[200.        , 200.        ],
#       [182.22222222, 182.22222222],
#       [ 21.16959064,  21.16959064],
#       [ 20.        ,  20.        ]])
#Coordinates:
#  * x        (x) float64 0.1 0.11 0.2 0.3
#Dimensions without coordinates: y

这里补充说一句,那些可选参数大部分是通用的,你可以通过参数的设置达到不同的目的。

梯形原则计算积分:

print(da.integrate('x'))
#<xarray.DataArray (y: 2)>
#array([0.78, 0.98])
#Dimensions without coordinates: y
ex7 广播计算

广播的意思大致可以理解为扩展,主要是用于两个不同大小(指shape的不同)数组的计算,原理同线性代数的矩阵计算。并不是所有不同大小的数组都可以计算,广播也要遵循一定的规则。
我们先建立两个大小不等的数组:

a = xr.DataArray([1, 2], [('x', ['a', 'b'])])
b = xr.DataArray([-1, -2, -3], [('y', [10, 20, 30])])
print(a)
#<xarray.DataArray (x: 2)>
#array([1, 2])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
print(b)
#<xarray.DataArray (y: 3)>
#array([-1, -2, -3])
#Coordinates:
#  * y        (y) int64 10 20 30

我们可以计算矩阵乘法:

print(a * b)
#<xarray.DataArray (x: 2, y: 3)>
#array([[-1, -2, -3],
#       [-2, -4, -6]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30

实际上原理是这样的。a(n,m) * b(m) = a(n,m) * b(1,m) b默认扩充了,也就是广播了。

a2, b2 = xr.broadcast(a, b)
print(a2,b2)
#<xarray.DataArray (x: 2, y: 3)>
#array([[1, 1, 1],
#       [2, 2, 2]])
#Coordinates:
#  * x        (x) <U1 'a' 'b'
#  * y        (y) int64 10 20 30 <xarray.DataArray (x: 2, y: 3)>
#array([[-1, -2, -3],
#       [-1, -2, -3]])
#Coordinates:
#  * y        (y) int64 10 20 30
#  * x        (x) <U1 'a' 'b'

通过broadcast函数可以更清晰的看出广播的原理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容