在了解了数据的结构之后,我们便可以开始着手处理数据了,那么如何才能从一个数据或数据集中拿到我们所需要的那部分数据呢?
Xarray官方提供了三种方法用来索引数据:
1.利用下标索引(index)
2.利用坐标值索引(coords)
3.利用标签索引(labels)
我对官方的表格实例做了修改,更符合我们气象专业的理解。
da指DataArray;ds指Dataset
虽然说给出了多种索引数据的方法,但是实际上通常用到的只有前两种。
具体通过一些例子来表现(均是由官方文档例子修改得到,为了更符合气象数据的结构):这建立了一个2000年1月1号-1月4号,且有三个纬度层
da = xr.DataArray(np.random.rand(4, 3),[('time', pd.date_range('2000-01-01', periods=4)),('lat', [30, 60, 90])])
索引方法
下边是各种花样索引:
ex1
print(da[:2])
#<xarray.DataArray (time: 2, lat: 3)>
#array([[0.64658774, 0.25076033, 0.7425908 ],
# [0.40127745, 0.08413761, 0.55898847]])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02
# * lat (lat) int64 30 60 90
可以看到这样的结果相当于da[0:2,:],纬度维相当于被省略了,这种形式也是python的特色,而不是xarray独有的。
ex2
print(da[:, [2, 1]])
#<xarray.DataArray (time: 4, lat: 2)>
#array([[0.7425908 , 0.25076033],
# [0.55898847, 0.08413761],
# [0.82131977, 0.52965844],
# [0.02323461, 0.80289017]])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04
# * lat (lat) int64 90 60
注意看会发现纬度维的顺序变了,实际上da[:,::-1]的操作会更实用,因为不同的资料纬度排列不同,有的是从南到北,有的是从北到南,可以通过::-1来实现颠倒。
ex3
print(da.loc['2000-01-01':'2000-01-02', 90])
#<xarray.DataArray (time: 2)>
#array([0.7425908 , 0.55898847])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02
# lat int64 90
通过.loc的方式定位标签,这种方法太实用了!比如说要选取区域数据,
a.loc['time1':'time2',lat1:lat2,lon1:lon2]
顺便说,索引出来的数据是直接可以修改或者计算的
da.loc['2000-01-01', [90, 60]] = -10
print(da)
#<xarray.DataArray (time: 4, lat: 3)>
#array([[ 0.64658774, -10. , -10. ],
# [ 0.40127745, 0.08413761, 0.55898847],
# [ 0.48245825, 0.52965844, 0.82131977],
# [ 0.69295939, 0.80289017, 0.02323461]])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04
# * lat (lat) int64 30 60 90
PS:关于后两种的.sel和.isel的使用,我觉得十分鸡肋,也可能我没抓住精髓,感兴趣的可以参考官方文档,我不在此展开了,浪费大家阅读时间。
有时候,我们无法精准确定我们的需求或者其他特殊的需要,这时候要使用到模糊索引。
同样的,我们新建一个简单的da(以下da均代表DataArray)
da = xr.DataArray([1, 2, 3], [('lat', [30, 60, 90])])
print(da)
#<xarray.DataArray (lat: 3)>
#array([1, 2, 3])
#Coordinates:
# * lat (lat) int64 30 60 90
ex4
print(da.sel(lat=[30.5, 60.9], method='nearest'))
#<xarray.DataArray (lat: 2)>
#array([1, 2])
#Coordinates:
# * lat (lat) int64 30 60
print(da.sel(lat=30.1, method='backfill'))
#<xarray.DataArray ()>
#array(2)
#Coordinates:
# lat int64 60
通过参数method='nearest'索引到了离30.5和60.9最近的纬度的数据
通过参数method='backfill'索引到了大于30.1的最近的纬度的数据
ex5
print(da.reindex(lat=[25,30,35,60,65,85,90,95], method='pad'))
#<xarray.DataArray (lat: 8)>
#array([nan, 1., 1., 2., 2., 2., 3., 3.])
#Coordinates:
# * lat (lat) int64 25 30 35 60 65 85 90 95
通过参数method='pad'展开得到了指定纬度的数据,这里用插值的说法不太合适,因为并没有插值,只是就近相等的原则。插值有具体的函数,我会在后边的文章中详细介绍。
ex6
print(da.reindex(lat=[32,35], method='nearest', tolerance=2))
通过 tolerance=2参数的设置,指定模糊索引的边界限制。
以上是对DataArray的索引方式,基本上对于Dataset的操作也大致相等,实际上在使用过程中对Dataset的直接操作是比较少的,通常是先取出Dataset中的某变量(DataArray)然后再对变量进行各种切片和索引。
数组掩码(多用于缺测处理)
Xarray也提供了用于数组掩码的操作:
ex7
print(da.where(da.lat < 60))
#<xarray.DataArray (time: 4, lat: 3)>
#array([[0.41845339, nan, nan],
# [0.82314399, nan, nan],
# [0.46403574, nan, nan],
# [0.90320489, nan, nan]])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04
# * lat (lat) int64 30 60 90
通过where()语句,将不满足条件的位置全部设置为缺测,我举几个实用的方法:比如结合shp文件将研究区域外的数据缺测,或是剔除数据异常点的数据等等。
除了掩码外,还提供了一种直接剔除数据的办法:
ex8
print(da.where(da.lat < 60, drop=True))
#<xarray.DataArray (time: 4, lat: 1)>
#array([[0.41845339],
# [0.82314399],
# [0.46403574],
# [0.90320489]])
#Coordinates:
# * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04
# * lat (lat) int64 30
可以看到,不满足条件的位置全部删掉了。个人感觉不太实用,这样改变了数组的shape,可能在后续的操作会遇到更多的麻烦。
根据自己需要选择合适方法即可。
子集索引
实在不知道该怎么描述,只好用子集索引来形容这种方法。实质就是自己定义一个范围,索引该范围之内的数据。
ex9
print(da.lat.isin([30,35]))
#<xarray.DataArray 'lat' (lat: 3)>
#array([ True, False, False])
#Coordinates:
# * lat (lat) int64 30 60 90
这种用法用的比较少,根据需要自己开发。
isin()函数同样支持drop=True参数的设置,效果同上。
官方文档还有更多的索引实例,也有一些更加复杂的,无非是上述几种索引方式的组合运用,如果熟悉了几种基本的索引方式后并理解了其中的原理的话,自己就能摸索出来那些复杂的组合运用,比如说从一个N年的逐日数据集中挑选每年某月某日到某月某日的数据,或者计算一个气候指数时,直接选出需要范围的数据,进行区域平均或者时间平均即可得到想要的结果。
个人感觉相比于NCL,Xarray为Python的数据索引提供了太多的便利,再结合上CDO对数据的预处理,可以让脚本的代码大大的简化,也减小了出错的机率,就运行速度而言,Xarray读取数据的速度是非常快的,内存限制也完全取决于计算机的最大内存,好评!
预告一下,下一节我会介绍Xarray中的数据插值功能。