xarray指南:合并数据 - 合并

目录

本文翻译自 xarray 官方文档 Combining data 的部分内容。

本文介绍如何使用 xarray 实现不同变量或索引的合并。

准备

本文使用《xarray指南:合并数据 - 连接》中的数据。

ds
<xarray.Dataset>
Dimensions:  (x: 2, y: 3)
Coordinates:
  * x        (x) <U1 'a' 'b'
  * y        (y) int64 10 20 30
Data variables:
    foo      (x, y) float64 0.8927 0.7334 -0.9644 0.05414 -2.151 -1.543

Merge

想要合并多个 DataArray 和/或 Dataset 对象的变量和坐标,请使用 merge()。 可以合并 DatasetDataArray 的列表,或者可以转换成 DataArray 的对象的字典。

xr.merge([
    ds, 
    ds.rename({'foo': 'bar'}),
])
<xarray.Dataset>
Dimensions:  (x: 2, y: 3)
Coordinates:
  * x        (x) <U1 'a' 'b'
  * y        (y) int64 10 20 30
Data variables:
    foo      (x, y) float64 0.8927 0.7334 -0.9644 0.05414 -2.151 -1.543
    bar      (x, y) float64 0.8927 0.7334 -0.9644 0.05414 -2.151 -1.543
xr.merge([xr.DataArray(n, name='var%d' % n) for n in range(5)])
<xarray.Dataset>
Dimensions:  ()
Data variables:
    var0     int64 0
    var1     int64 1
    var2     int64 2
    var3     int64 3
    var4     int64 4

如果合并另一个数据集(或包括数据数组对象的字典),默认情况下,结果数据集将在所有索引坐标的并集上对齐

other = xr.Dataset({
    'bar': ('x', [1, 2, 3, 4]), 
    'x': list('abcd'),
})
other
<xarray.Dataset>
Dimensions:  (x: 4)
Coordinates:
  * x        (x) <U1 'a' 'b' 'c' 'd'
Data variables:
    bar      (x) int64 1 2 3 4
xr.merge([ds, other])
<xarray.Dataset>
Dimensions:  (x: 4, y: 3)
Coordinates:
  * x        (x) object 'a' 'b' 'c' 'd'
  * y        (y) int64 10 20 30
Data variables:
    foo      (x, y) float64 0.8927 0.7334 -0.9644 0.05414 ... nan nan nan nan
    bar      (x) int64 1 2 3 4

这将确保 merge 是非破坏性的。 如果尝试合并两个有相同名称但数据值不同的变量,则会抛出 xarray.MergeError

# 将会抛出异常
# xr.merge([ds, ds + 1])

这种在 DataArray 索引坐标间非破坏性的合并被用在 Dataset 的构造函数中。

xr.Dataset({
    'a': arr[:-1], 
    'b': arr[1:],
})
<xarray.Dataset>
Dimensions:  (x: 2, y: 3)
Coordinates:
  * x        (x) object 'a' 'b'
  * y        (y) int64 10 20 30
Data variables:
    a        (x, y) float64 0.8927 0.7334 -0.9644 nan nan nan
    b        (x, y) float64 nan nan nan 0.05414 -2.151 -1.543

实战

获取 850hPa 的温度场和风场

file_path=find_local_file(
    "grapes_gfs_gmf/grib2/orig",
    start_time="2020031800",
    forecast_time="0h"
)
t850 = load_field_from_file(
    file_path,
    parameter="t",
    level_type="isobaricInhPa",
    level=850,
)
u850 = load_field_from_file(
    file_path,
    parameter="u",
    level_type="isobaricInhPa",
    level=850,
)
v850 = load_field_from_file(
    file_path,
    parameter="v",
    level_type="isobaricInhPa",
    level=850,
)

合并三个 DataArray 对象

xr.merge([t850, u850, v850])
<xarray.Dataset>
Dimensions:        (latitude: 720, longitude: 1440)
Coordinates:
    time           datetime64[ns] 2020-03-18
    step           timedelta64[ns] 00:00:00
    isobaricInhPa  int64 850
  * latitude       (latitude) float64 89.88 89.62 89.38 ... -89.38 -89.62 -89.88
  * longitude      (longitude) float64 0.0 0.25 0.5 0.75 ... 359.2 359.5 359.8
    valid_time     datetime64[ns] 2020-03-18
Data variables:
    t              (latitude, longitude) float32 ...
    u              (latitude, longitude) float32 ...
    v              (latitude, longitude) float32 ...

Combine

实例方法 combine_first() 合并两个数据集/数据数组,并使用被调用对象中的值填充缺失数据,默认情况下调用对象中的值为非空值。 结果坐标是坐标标签的并集。 由于外部连接而导致的空单元被 NaN 填充。例如

ar0 = xr.DataArray(
    [
        [0, 0], 
        [0, 0]
    ], 
    [
        ('x', ['a', 'b']), 
        ('y', [-1, 0])
    ]
)

ar1 = xr.DataArray(
    [
        [1, 1], 
        [1, 1]
    ], 
    [
        ('x', ['b', 'c']), 
        ('y', [0, 1])
    ]
)
ar0.combine_first(ar1)
<xarray.DataArray (x: 3, y: 3)>
array([[ 0.,  0., nan],
       [ 0.,  0.,  1.],
       [nan,  1.,  1.]])
Coordinates:
  * x        (x) object 'a' 'b' 'c'
  * y        (y) int64 -1 0 1
ar1.combine_first(ar0)
<xarray.DataArray (x: 3, y: 3)>
array([[ 0.,  0., nan],
       [ 0.,  1.,  1.],
       [nan,  1.,  1.]])
Coordinates:
  * x        (x) object 'a' 'b' 'c'
  * y        (y) int64 -1 0 1

对于数据集,ds0.combine_first(ds1)xr.merge([ds0, ds1]) 效果类似。但在合并的变量中有冲突数据时,xr.merge 会抛出 MergeError 异常,而 .combine_first 默认使用被调用对象的值。

Update

merge 相比,update() 在不检查冲突的情况下,就地修改数据集,使用新的数据覆盖任何已存在的变量。

arr = xr.DataArray(
    np.random.randn(2, 3),
    [
        ('x', ['a', 'b']), 
        ('y', [10, 20, 30])
    ]
)
ds = arr.to_dataset(name='foo')
ds
arr = xr.DataArray(
    np.random.randn(2, 3),
    [
        ('x', ['a', 'b']), 
        ('y', [10, 20, 30])
    ]
)
ds = arr.to_dataset(name='foo')
ds
arr = xr.DataArray(
    np.random.randn(2, 3),
    [
        ('x', ['a', 'b']), 
        ('y', [10, 20, 30])
    ]
)
ds = arr.to_dataset(name='foo')
ds
<xarray.Dataset>
Dimensions:  (x: 2, y: 3)
Coordinates:
  * x        (x) <U1 'a' 'b'
  * y        (y) int64 10 20 30
Data variables:
    foo      (x, y) float64 0.4881 0.1385 0.8559 -0.5746 0.9708 0.3979
ds.update({
    'space': ('space', [10.2, 9.4, 3.9])
})
<xarray.Dataset>
Dimensions:  (space: 3, x: 2, y: 3)
Coordinates:
  * x        (x) <U1 'a' 'b'
  * y        (y) int64 10 20 30
  * space    (space) float64 10.2 9.4 3.9
Data variables:
    foo      (x, y) float64 0.4881 0.1385 0.8559 -0.5746 0.9708 0.3979

然而,不同 Dataset 变量的维度仍然需要保持一致,所以无法改变维度的大小,除非替换所有使用该维度的变量。

update 在必要时候会自动对齐。 不同于 mergeupdate 保持原始数组的对齐方式,而不是合并索引。

other
<xarray.Dataset>
Dimensions:  (x: 4)
Coordinates:
  * x        (x) <U1 'a' 'b' 'c' 'd'
Data variables:
    bar      (x) int64 1 2 3 4
ds.update(other)
<xarray.Dataset>
Dimensions:  (space: 3, x: 2, y: 3)
Coordinates:
  * x        (x) object 'a' 'b'
  * y        (y) int64 10 20 30
  * space    (space) float64 10.2 9.4 3.9
Data variables:
    foo      (x, y) float64 0.4881 0.1385 0.8559 -0.5746 0.9708 0.3979
    bar      (x) int64 1 2

使用 __setitem__ 语法设置变量时,使用完全相同的对齐逻辑:

ds['baz'] = xr.DataArray(
    [9, 9, 9, 9, 9], 
    coords=[('x', list('abcde'))]
)
ds.baz
<xarray.DataArray 'baz' (x: 2)>
array([9, 9])
Coordinates:
  * x        (x) object 'a' 'b'

Equals and identical

可以使用 equals()identical()broadcast_equals() 方法比较 xarray 对象。 这些方法被 concatmerge 的可选参数 compat 使用。

equals 检查维度名称,索引和数据值。

arr.equals(arr.copy())
True

identical 还检查属性,以及每个对象的名称

arr.identical(arr.rename("bar"))
False

broadcast_equals 进行更宽松的检查,允许变量有不同的维度,只要这些新维度的数值是常数。

left = xr.Dataset(coords={'x': 0})
right = xr.Dataset({'x': [0, 0, 0]})
left.broadcast_equals(right)
True

类似 pandas 对象,如果相同位置的缺失值被标记为 NaN,两个 xarray 对象仍然 equal 或 identical。

相反,== 操作执行逐元素比较(类似 numpy):

arr == arr.copy()
<xarray.DataArray (x: 2, y: 3)>
array([[ True,  True,  True],
       [ True,  True,  True]])
Coordinates:
  * x        (x) <U1 'a' 'b'
  * y        (y) int64 10 20 30

注意,在逐元素对比中,NaNNaN 不相等。可能需要明确处理缺失值。

Merging with ’no_conflicts'

compat 参数 no_conflicts 仅在使用 merge 合并 xarray 对象时候才可用。 除上述比较方法外,它还允许将两个都具有 NaN 值位置的 xarray 对象合并。 只要任何非缺失值一致或不相交,就可以将其与重叠坐标的数据合并使用:

ds1 = xr.Dataset(
    {
        'a': ('x', [10, 20, 30, np.nan])
    }, 
    {
        'x': [1, 2, 3, 4]
    }
)
ds1
<xarray.Dataset>
Dimensions:  (x: 4)
Coordinates:
  * x        (x) int64 1 2 3 4
Data variables:
    a        (x) float64 10.0 20.0 30.0 nan
ds2 = xr.Dataset(
    {
        'a': ('x', [np.nan, 30, 40, 50])
    }, 
    {
        'x': [2, 3, 4, 5]
    }
)
ds2

<xarray.Dataset>
Dimensions:  (x: 4)
Coordinates:
  * x        (x) int64 2 3 4 5
Data variables:
    a        (x) float64 nan 30.0 40.0 50.0
xr.merge([ds1, ds2])
<xarray.Dataset>
Dimensions:  (x: 5)
Coordinates:
  * x        (x) int64 1 2 3 4 5
Data variables:
    a        (x) float64 10.0 20.0 30.0 40.0 50.0
xr.merge([ds1, ds2], compat='no_conflicts')
<xarray.Dataset>
Dimensions:  (x: 5)
Coordinates:
  * x        (x) int64 1 2 3 4 5
Data variables:
    a        (x) float64 10.0 20.0 30.0 40.0 50.0

请注意,由于缺失值的基本表示为浮点数(NaN),因此以这种方式合并时并不总是保留可变数据类型。

参考

http://xarray.pydata.org/en/stable/combining.html

https://github.com/nwpc-oper/nwpc-data