diff --git a/doc/whats-new.rst b/doc/whats-new.rst index add40bb6b81..39c6a8924f4 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -30,6 +30,9 @@ Bug fixes - Fix ``KeyError`` when passing a ``dim`` argument different from the default to ``convert_calendar`` (:pull:`10544`). By `Eric Jansen `_. +- Fix transpose of boolean arrays read from disk. (:issue:`10536`) + By `Deepak Cherian `_. + Documentation ~~~~~~~~~~~~~ diff --git a/xarray/coding/variables.py b/xarray/coding/variables.py index eff08c74500..bff0f529124 100644 --- a/xarray/coding/variables.py +++ b/xarray/coding/variables.py @@ -70,6 +70,9 @@ def __getitem__(self, key) -> Self: def get_duck_array(self): return duck_array_ops.astype(self.array.get_duck_array(), dtype=self.dtype) + def transpose(self, order): + return type(self)(self.array.transpose(order)) + class BoolTypeArray(indexing.ExplicitlyIndexedNDArrayMixin): """Decode arrays on the fly from integer to boolean datatype @@ -111,6 +114,9 @@ def __getitem__(self, key) -> Self: def get_duck_array(self): return duck_array_ops.astype(self.array.get_duck_array(), dtype=self.dtype) + def transpose(self, order): + return type(self)(self.array.transpose(order)) + def _apply_mask( data: np.ndarray, diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 6997be200b1..93329b2297d 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -308,7 +308,15 @@ def create_encoded_unsigned_false_masked_scaled_data(dtype: np.dtype) -> Dataset def create_boolean_data() -> Dataset: attributes = {"units": "-"} - return Dataset({"x": ("t", [True, False, False, True], attributes)}) + return Dataset( + { + "x": ( + ("t", "x"), + [[False, True, False, True], [True, False, False, True]], + attributes, + ) + } + ) class TestCommon: @@ -726,6 +734,9 @@ def test_roundtrip_boolean_dtype(self) -> None: with self.roundtrip(actual) as actual2: assert_identical(original, actual2) assert actual2["x"].dtype == "bool" + with self.roundtrip(actual) as actual3: + # GH10536 + assert_identical(original.transpose(), actual3.transpose()) def test_orthogonal_indexing(self) -> None: in_memory = create_test_data() diff --git a/xarray/tests/test_conventions.py b/xarray/tests/test_conventions.py index ce792c83740..4b3729e2724 100644 --- a/xarray/tests/test_conventions.py +++ b/xarray/tests/test_conventions.py @@ -37,6 +37,10 @@ def test_booltype_array(self) -> None: assert bx.dtype == bool assert_array_equal(bx, np.array([True, False, True, True, False], dtype=bool)) + x = np.array([[1, 0, 1], [0, 1, 0]], dtype="i1") + bx = coding.variables.BoolTypeArray(x) + assert_array_equal(bx.transpose((1, 0)), x.transpose((1, 0))) + class TestNativeEndiannessArray: def test(self) -> None: @@ -47,6 +51,11 @@ def test(self) -> None: assert a.dtype == expected[:].dtype assert_array_equal(a, expected) + y = np.arange(6, dtype=">i8").reshape((2, 3)) + b = coding.variables.NativeEndiannessArray(y) + expected2 = np.arange(6, dtype="int64").reshape((2, 3)) + assert_array_equal(b.transpose((1, 0)), expected2.transpose((1, 0))) + def test_decode_cf_with_conflicting_fill_missing_value() -> None: expected = Variable(["t"], [np.nan, np.nan, 2], {"units": "foobar"})