使用Python-Xarray重新网格化坐标

4

我有一个NetCDF文件,其中变量存储在0到360度经度之间。我想将其转换为-180到180度。这应该是一个相当简单的任务,但由于某种原因,我似乎无法让教程中给出的一些示例起作用。

ds = xr.open_dataset(file_)   
>ds
<xarray.Dataset>
Dimensions:  (lev: 1, lon: 720, time: 1460)
Coordinates:
* lon      (lon) float64 0.0 0.5 1.0 1.5 2.0 2.5 ... -2.5 -2.0 -1.5 -1.0 -0.5
* lev      (lev) float32 1.0
* time     (time) datetime64[ns] 2001-01-01 ... 2001-12-31T18:00:00
Data variables:
 V        (time, lev, lon) float32 13.281297 11.417505 ... -19.312767

我尝试使用Dataset.assign_coord来帮助。
ds.V.assign_coords(lon=((ds.V.lon + 180) % 360 - 180)) 
#gives me a new array with lon -180 to 180
ds['V'] = ds.V.assign_coords(lon=((ds.V.lon + 180) % 360 - 180))
# didn't modify the V for some reason?

因此,assign_coords可以正常运行,但将变量设置回Dataset无法正常工作。经过多次尝试,我发现直接修改坐标"lon"是可行的,因为它们通过字典与Datavariable“V”相关联。

ds.coords['lon'] = (ds.coords['lon'] + 180) % 360 - 180
#solves the problem!

第二个问题是在按照上述修改后的经度对我的数据变量进行排序时遇到了困难。我尝试过

 ds['V'] = ds.V.sortby(ds.lon)
 >ds.V 

 # the array is not sorted according to -180 to 180 values

但是当我对数据集进行排序并分配后,它就可以正常工作了。
ds = ds.sortby(ds.lon) # now my dataset is sorted to -180 to 180 degrees lon

如果有人能指出为什么我的第一种方法对于两个问题都不起作用,那么对我理解xarrays会非常有帮助。
4个回答

12

我很抱歉这只是一个简短的语句,但这正是我解决这个问题的方法:

d = d.assign_coords(longitude=(((d.longitude + 180) % 360) - 180)).sortby('longitude')

你应该在 Dataset 级别而不是 DataArray 级别工作。


谢谢您的建议。如果我的数据中还有其他坐标,比如纬度或压力水平,那么使用dataset.assign_coords会自动删除其他坐标并将经度作为新的坐标吗? - Light_B
1
在xarray文档中,您可以阅读assign_coords的说明:“返回一个新对象,其中包含所有原始数据以及新坐标。”。然后,其他坐标不会改变,在这种特定情况下,您只需使用修改后的版本“覆盖”经度即可... - Matteo De Felice

3
有一个原则可以解释为什么你的两种初始方法都不起作用。在数据集中,变量沿坐标轴具有值。这些坐标轴在数据集中与变量分开存在。你可能有三个变量UVW,它们都沿着数据集内的某个longitude坐标轴变化。单独看,UVlongitude值顺序不同是可以的,但在数据集中,它们必须有相同的顺序。
当你将一个变量分配给已经拥有该变量坐标轴的数据集时,xarray将自动重新排序该变量,使其与数据集具有相同的顺序。它还会做一些好事情,例如在变量没有给定坐标轴的值时添加nan值。
下面是一个例子,我创建了一个DatasetDataArray,它们都有一个经度坐标轴,但方向相反。当我将DataArray分配给Dataset时,坐标轴会自动反转。
In[17]: ds
Out[17]: 
<xarray.Dataset>
Dimensions:    (longitude: 10)
Coordinates:
  * longitude  (longitude) float64 360.0 320.0 280.0 240.0 200.0 160.0 120.0 ...
Data variables:
    *empty*

In [18]: da
Out[18]: 
<xarray.DataArray (longitude: 10)>
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])
Coordinates:
  * longitude  (longitude) float64 0.0 40.0 80.0 120.0 160.0 200.0 240.0 ...

In [19]: ds['v'] = da

In [20]: ds['v']
Out[20]: 
<xarray.DataArray 'v' (longitude: 10)>
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])
Coordinates:
  * longitude  (longitude) float64 360.0 320.0 280.0 240.0 200.0 160.0 120.0 ...

这是一个类似的例子,它会自动添加nan
In [27]: ds
Out[27]: 
<xarray.Dataset>
Dimensions:    (longitude: 10)
Coordinates:
  * longitude  (longitude) float64 360.0 320.0 280.0 240.0 200.0 160.0 120.0 ...
Data variables:
    *empty*

In [28]: da
Out[28]: 
<xarray.DataArray (longitude: 3)>
array([ 0.,  0.,  0.])
Coordinates:
  * longitude  (longitude) float64 0.0 40.0 80.0

In [29]: ds['v'] = da

In [30]: ds['v']
Out[30]: 
<xarray.DataArray 'v' (longitude: 10)>
array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,   0.,   0.,   0.])
Coordinates:
  * longitude  (longitude) float64 360.0 320.0 280.0 240.0 200.0 160.0 120.0 ...

谢谢,我有点困惑,因为文档说坐标不是存储为有序字典,这让我认为每个变量通过字典单独链接到坐标。当然,现在我明白,在数据集中这是没有意义的。 - Light_B

2

虽然这不是一个Python解决方案,但如果你使用的是Linux系统并且安装了NCO,你可以输入以下命令:

ncap2 -O -s 'where(lon>180) lon=lon-360' ifile ofile

根据如何更改 NetCDF 中的经度范围这个答案,您可以进行以下操作:

当然,正如我在帖子开头所承认的那样,但也许原帖作者(或其他寻找解决此问题的方案但可能不固守于Python的人)并不知道有一个一行代码的shell替代方案 - 我认为将一个简短高效的替代方案添加到Python答案列表中并不会有害(我已经点赞了),因此从未理解为什么人们会在SO上投票反对回答问题但不使用请求的语言。答案不仅是为了原帖作者,也是为了整个社区。 - ClimateUnboxed
当然,我也很高兴知道这种方法是可行的。如果数据集较大,使用nco比python更快,这是我的假设? - Light_B
1
嗨,light_B,感谢您的积极评论。如果您想要打开文件,更改范围,然后在Python中进行进一步处理,那么使用Python可能会更快,因为使用nco/cdo解决方案时,您需要打开、读取、写入磁盘,然后再次打开并在Python中读取以进行处理。如果您想要进行一次更改,然后重复使用该文件,则可能更有效率。说实话,我不是专家。但通常情况下,nco/cdo解决方案在您的时间方面更加高效,这往往更为重要;-) - ClimateUnboxed

2
对于那些问题,cdo很好而且快速,比如:
cdo sellonlatbox,-180,180,-90,90 a.nc b.nc

a.nc是你的数据,b.nc是你想要的结果。


对我来说效果很好,谢谢。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接