Skip to content

Commit 2357851

Browse files
authored
More support for missing_value. (#2973)
* More support for missing_value. Fixes #2871 * lint fixes. * Use not equivalent instead of not equals check. * lint fix. * if → elif so we don't call fillna twice * Better fix.
1 parent 3429ca2 commit 2357851

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ Bug fixes
7272
By `Deepak Cherian <https://github.com/dcherian`_.
7373
- A deep copy deep-copies the coords (:issue:`1463`)
7474
By `Martin Pletcher <https://github.com/pletchm>`_.
75+
- Increased support for `missing_value` (:issue:`2871`)
76+
By `Deepak Cherian <https://github.com/dcherian>`_.
7577
- Removed usages of `pytest.config`, which is deprecated (:issue:`2988`)
7678
By `Maximilian Roos <https://github.com/max-sixty>`_.
7779
- Fixed performance issues with cftime installed (:issue:`3000`)

xarray/coding/variables.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from ..core import dtypes, duck_array_ops, indexing
1010
from ..core.pycompat import dask_array_type
11+
from ..core.utils import equivalent
1112
from ..core.variable import Variable
1213

1314

@@ -145,11 +146,24 @@ class CFMaskCoder(VariableCoder):
145146
def encode(self, variable, name=None):
146147
dims, data, attrs, encoding = unpack_for_encoding(variable)
147148

148-
if encoding.get('_FillValue') is not None:
149+
fv = encoding.get('_FillValue')
150+
mv = encoding.get('missing_value')
151+
152+
if fv is not None and mv is not None and not equivalent(fv, mv):
153+
raise ValueError("Variable {!r} has multiple fill values {}. "
154+
"Cannot encode data. "
155+
.format(name, [fv, mv]))
156+
157+
if fv is not None:
149158
fill_value = pop_to(encoding, attrs, '_FillValue', name=name)
150159
if not pd.isnull(fill_value):
151160
data = duck_array_ops.fillna(data, fill_value)
152161

162+
if mv is not None:
163+
fill_value = pop_to(encoding, attrs, 'missing_value', name=name)
164+
if not pd.isnull(fill_value) and fv is None:
165+
data = duck_array_ops.fillna(data, fill_value)
166+
153167
return Variable(dims, data, attrs, encoding)
154168

155169
def decode(self, variable, name=None):

xarray/conventions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ def maybe_encode_nonstring_dtype(var, name=None):
8282
if dtype != var.dtype:
8383
if np.issubdtype(dtype, np.integer):
8484
if (np.issubdtype(var.dtype, np.floating) and
85-
'_FillValue' not in var.attrs):
85+
'_FillValue' not in var.attrs and
86+
'missing_value' not in var.attrs):
8687
warnings.warn('saving variable %s with floating '
8788
'point data as an integer dtype without '
8889
'any _FillValue to use for NaNs' % name,

xarray/tests/test_coding.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import xarray as xr
77
from xarray.coding import variables
88

9-
from . import assert_identical, requires_dask
9+
from . import assert_equal, assert_identical, requires_dask
1010

1111
with suppress(ImportError):
1212
import dask.array as da
@@ -20,6 +20,23 @@ def test_CFMaskCoder_decode():
2020
assert_identical(expected, encoded)
2121

2222

23+
def test_CFMaskCoder_missing_value():
24+
expected = xr.DataArray(np.array([[26915, 27755, -9999, 27705],
25+
[25595, -9999, 28315, -9999]]),
26+
dims=['npts', 'ntimes'],
27+
name='tmpk')
28+
expected.attrs['missing_value'] = -9999
29+
30+
decoded = xr.decode_cf(expected.to_dataset())
31+
encoded, _ = xr.conventions.cf_encoder(decoded, decoded.attrs)
32+
33+
assert_equal(encoded['tmpk'], expected.variable)
34+
35+
decoded.tmpk.encoding['_FillValue'] = -9940
36+
with pytest.raises(ValueError):
37+
encoded, _ = xr.conventions.cf_encoder(decoded, decoded.attrs)
38+
39+
2340
@requires_dask
2441
def test_CFMaskCoder_decode_dask():
2542
original = xr.Variable(('x',), [0, -1, 1], {'_FillValue': -1}).chunk()

0 commit comments

Comments
 (0)