Skip to content

Commit

Permalink
conventions: decode unsigned integers to signed if _Unsigned=false (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
d70-t authored Mar 12, 2021
1 parent 213e352 commit d4b7a60
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Bug fixes
By `Victor Negîrneac <https://github.com/caenrigen>`_.
- Don't allow passing ``axis`` to :py:meth:`Dataset.reduce` methods (:issue:`3510`, :pull:`4940`).
By `Justus Magin <https://github.com/keewis>`_.
- Decode values as signed if attribute `_Unsigned = "false"` (:issue:`4954`)
By `Tobias Kölling <https://github.com/d70-t>`_.

Documentation
~~~~~~~~~~~~~
Expand Down
8 changes: 8 additions & 0 deletions xarray/coding/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ def decode(self, variable, name=None):
if "_FillValue" in attrs:
new_fill = unsigned_dtype.type(attrs["_FillValue"])
attrs["_FillValue"] = new_fill
elif data.dtype.kind == "u":
if unsigned == "false":
signed_dtype = np.dtype("i%s" % data.dtype.itemsize)
transform = partial(np.asarray, dtype=signed_dtype)
data = lazy_elemwise_func(data, transform, signed_dtype)
if "_FillValue" in attrs:
new_fill = signed_dtype.type(attrs["_FillValue"])
attrs["_FillValue"] = new_fill
else:
warnings.warn(
"variable %r has _Unsigned attribute but is not "
Expand Down
28 changes: 28 additions & 0 deletions xarray/tests/test_coding.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,31 @@ def test_scaling_offset_as_list(scale_factor, add_offset):
encoded = coder.encode(original)
roundtripped = coder.decode(encoded)
assert_allclose(original, roundtripped)


@pytest.mark.parametrize("bits", [1, 2, 4, 8])
def test_decode_unsigned_from_signed(bits):
unsigned_dtype = np.dtype(f"u{bits}")
signed_dtype = np.dtype(f"i{bits}")
original_values = np.array([np.iinfo(unsigned_dtype).max], dtype=unsigned_dtype)
encoded = xr.Variable(
("x",), original_values.astype(signed_dtype), attrs={"_Unsigned": "true"}
)
coder = variables.UnsignedIntegerCoder()
decoded = coder.decode(encoded)
assert decoded.dtype == unsigned_dtype
assert decoded.values == original_values


@pytest.mark.parametrize("bits", [1, 2, 4, 8])
def test_decode_signed_from_unsigned(bits):
unsigned_dtype = np.dtype(f"u{bits}")
signed_dtype = np.dtype(f"i{bits}")
original_values = np.array([-1], dtype=signed_dtype)
encoded = xr.Variable(
("x",), original_values.astype(unsigned_dtype), attrs={"_Unsigned": "false"}
)
coder = variables.UnsignedIntegerCoder()
decoded = coder.decode(encoded)
assert decoded.dtype == signed_dtype
assert decoded.values == original_values

0 comments on commit d4b7a60

Please sign in to comment.