Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Bug Fixes
:py:func:`open_dataset` is called with a non-existent local file path
(:issue:`10896`).
By `Kristian Kollsgård <https://github.com/kkollsga>`_.
- Coerce masked dask arrays to filled (:issue:`9374` :pull:`11157`).
By `Julia Signell <https://github.com/jsignell>`_

Documentation
~~~~~~~~~~~~~
Expand Down
4 changes: 4 additions & 0 deletions xarray/core/duck_array_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ def fail_on_dask_array_input(values, msg=None, func_name=None):
"masked_invalid", eager_module=np.ma, dask_module="dask.array.ma"
)

getmaskarray = _dask_or_eager_func(
"getmaskarray", eager_module=np.ma, dask_module="dask.array.ma"
)


def sliding_window_view(array, window_shape, axis=None, **kwargs):
# TODO: some libraries (e.g. jax) don't have this, implement an alternative?
Expand Down
14 changes: 7 additions & 7 deletions xarray/core/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from xarray.namedarray.core import NamedArray, _raise_if_any_duplicate_dimensions
from xarray.namedarray.parallelcompat import get_chunked_array_type
from xarray.namedarray.pycompat import (
array_type,
async_to_duck_array,
integer_types,
is_0d_dask_array,
Expand Down Expand Up @@ -292,13 +293,12 @@ def convert_non_numpy_type(data):
else:
data = pandas_data

if isinstance(data, np.ma.MaskedArray):
mask = np.ma.getmaskarray(data)
if mask.any():
_dtype, fill_value = dtypes.maybe_promote(data.dtype)
data = duck_array_ops.where_method(data, ~mask, fill_value)
else:
data = np.asarray(data)
if isinstance(data, np.ma.MaskedArray) or (
isinstance(data, array_type("dask"))
and isinstance(getattr(data, "_meta", None), np.ma.MaskedArray)
):
mask = duck_array_ops.getmaskarray(data)
data = duck_array_ops.where_method(data, ~mask)

if isinstance(data, np.matrix):
data = np.asarray(data)
Expand Down
23 changes: 23 additions & 0 deletions xarray/tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
mock,
network,
parametrize_zarr_format,
raise_if_dask_computes,
requires_cftime,
requires_dask,
requires_fsspec,
Expand Down Expand Up @@ -2231,6 +2232,28 @@ def test_create_default_indexes(self, tmp_path, create_default_indexes) -> None:
else:
assert len(loaded_ds.xindexes) == 0

@requires_dask
def test_encoding_masked_arrays(self, tmp_path) -> None:
store_path = tmp_path / "tmp.nc"

with raise_if_dask_computes():
ds = xr.DataArray(
dask.array.from_array(
np.ma.masked_array(
np.array([[np.nan, np.nan], [np.nan, 2]]),
np.array([[True, True], [True, False]]),
)
).astype("float32"),
dims=("x", "y"),
).to_dataset(name="mydata")

expected = ds.mean("x")
expected.to_netcdf(
store_path, encoding=dict(mydata=dict(_FillValue=np.float32(1e20)))
)
with open_dataset(store_path, engine=self.engine) as actual:
assert_identical(expected.compute(), actual.compute())


@requires_netCDF4
class TestNetCDF4Data(NetCDF4Base):
Expand Down
2 changes: 1 addition & 1 deletion xarray/tests/test_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2757,7 +2757,7 @@ def test_masked_array(self):
expected = np.arange(5)
actual: Any = as_compatible_data(original)
assert_array_equal(expected, actual)
assert np.dtype(int) == actual.dtype
assert np.dtype(float) == actual.dtype

original1: Any = np.ma.MaskedArray(np.arange(5), mask=4 * [False] + [True])
expected1: Any = np.arange(5.0)
Expand Down
Loading