Regrid xarray dataset to a grid that is 2.5 times coarser

Viewed 79

I have the following xarray Dataset, in which the latitude and longitude dimensions have a resolution of 0.1°:

print(data)

<xarray.Dataset>
Dimensions:    (latitude: 1801, longitude: 3600, time: 1)
Coordinates:
  * longitude  (longitude) float32 0.0 0.1 0.2 0.3 ... 359.6 359.7 359.8 359.9
  * latitude   (latitude) float32 90.0 89.9 89.8 89.7 ... -89.8 -89.9 -90.0
  * time       (time) datetime64[ns] 2013-08-09T12:00:00
Data variables:
    z          (time, latitude, longitude) float64 1.433 1.433 ... 2.711e+04

I want to coarsen it so that the resolution of the latitude and longitude dimensions becomes 0.25°. The desired output is:

print(desired_output)

<xarray.Dataset>
Dimensions:    (latitude: 721, longitude: 1440, time: 1)
Coordinates:
  * longitude  (longitude) float32 0.0 0.25 0.5 0.75 ... 359.0 359.25 359.5 359.75
  * latitude   (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
  * time       (time) datetime64[ns] 2013-08-09T12:00:00
Data variables:
    z          (time, latitude, longitude) float64 1.433 1.433 ... 2.711e+04

I have tried using the xarray's coarsen function:

data.coarsen(latitude=2.5, boundary='trim').mean().coarsen(longitude=2.5).mean()

but I get hit with the following error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_25536/849862177.py in <module>
----> 1 data.coarsen(latitude=2.5, boundary='trim').mean().coarsen(longitude=2.5).mean()

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\rolling.py in wrapped_func(self, **kwargs)
    827             for key, da in self.obj.data_vars.items():
    828                 reduced[key] = da.variable.coarsen(
--> 829                     self.windows, func, self.boundary, self.side, **kwargs
    830                 )
    831 

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in coarsen(self, windows, func, boundary, side, keep_attrs, **kwargs)
   2014             _attrs = None
   2015 
-> 2016         reshaped, axes = self._coarsen_reshape(windows, boundary, side)
   2017         if isinstance(func, str):
   2018             name = func

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in _coarsen_reshape(self, windows, boundary, side)
   2054             elif boundary[d] == "trim":
   2055                 if side[d] == "left":
-> 2056                     variable = variable.isel({d: slice(0, window * n)})
   2057                 else:
   2058                     excess = size - window * n

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in isel(self, indexers, missing_dims, **indexers_kwargs)
   1118 
   1119         key = tuple(indexers.get(dim, slice(None)) for dim in self.dims)
-> 1120         return self[key]
   1121 
   1122     def squeeze(self, dim=None):

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in __getitem__(self, key)
    766         array `x.values` directly.
    767         
--> 768         dims, indexer, new_order = self._broadcast_indexes(key)
    769         data = as_indexable(self._data)[indexer]
    770         if new_order:

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in _broadcast_indexes(self, key)
    605 
    606         if all(isinstance(k, BASIC_INDEXING_TYPES) for k in key):
--> 607             return self._broadcast_indexes_basic(key)
    608 
    609         self._validate_indexers(key)

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\variable.py in _broadcast_indexes_basic(self, key)
    633             dim for k, dim in zip(key, self.dims) if not isinstance(k, integer_types)
    634         )
--> 635         return dims, BasicIndexer(key), None
    636 
    637     def _validate_indexers(self, key):

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\indexing.py in __init__(self, key)
    382                 k = int(k)
    383             elif isinstance(k, slice):
--> 384                 k = as_integer_slice(k)
    385             else:
    386                 raise TypeError(

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\indexing.py in as_integer_slice(value)
    358 def as_integer_slice(value):
    359     start = as_integer_or_none(value.start)
--> 360     stop = as_integer_or_none(value.stop)
    361     step = as_integer_or_none(value.step)
    362     return slice(start, stop, step)

~\AppData\Roaming\Python\Python37\site-packages\xarray\core\indexing.py in as_integer_or_none(value)
    353 
    354 def as_integer_or_none(value):
--> 355     return None if value is None else operator.index(value)
    356 
    357 

TypeError: 'float' object cannot be interpreted as an integer

The problem is clear to me (the coarsen function does not support floats as slice dimensions); the solution, however, evades me. I've looked for solutions outside of xarray, for example the Regridder object from xESMF, but I use Windows and xESMF is only available for Linux and Mac OSX. I've also tried to use the cdo package for Python, but it throws some error I couldn't resolve when I try to import it.

Are there any other ways to achieve what I'm doing? They don't have to be xarray functions but they do need to be executable in Windows.

Thanks!

1 Answers

How about .interp? You’ll need to decide on a method, but so would you with any of the other methods:

minx = ds.lon.min().item()
maxx = ds.lon.max().item()
miny = ds.lat.min().item()
maxy = ds.lat.max().item()

# set up new lat/lon grid
new_grid_x = np.arange(
    np.ceil(minx / 0.25) * 0.25,
    (np.floor(maxx / 0.25) + 0.5) * 0.25,
    0.25
)
new_grid_y = np.arange(
    np.ceil(miny / 0.25) * 0.25,
    (np.floor(maxy / 0.25) + 0.5) * 0.25,
    0.25
)
# interpolate using nearest neighbor (can use linear, etc. if desired)
coarse = ds.interp(lon=new_grid_x, lat=new_grid_y, method="nearest")

See the xarray docs on interpolation for more info.

Related