From 9cf90183f6fda2c0627cd7ed6bea9005764ebeca Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 19 Dec 2019 00:44:57 +0100 Subject: [PATCH 01/29] remove xfail marks from median and cumprod --- xarray/tests/test_units.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index bef3af62d74..ef2e35070e3 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2171,8 +2171,8 @@ class TestDataArray: "with_dims", marks=pytest.mark.xfail(reason="units in indexes are not supported"), ), - pytest.param("with_coords"), - pytest.param("without_coords"), + "with_coords", + "without_coords", ), ) def test_init(self, variant, dtype): @@ -2246,10 +2246,7 @@ def test_repr(self, func, variant, dtype): function("argmin"), function("max"), function("mean"), - pytest.param( - function("median"), - marks=pytest.mark.xfail(reason="not implemented by xarray"), - ), + function("median"), function("min"), pytest.param( function("prod"), @@ -2259,10 +2256,7 @@ def test_repr(self, func, variant, dtype): function("std"), function("var"), function("cumsum"), - pytest.param( - function("cumprod"), - marks=pytest.mark.xfail(reason="not implemented by pint yet"), - ), + function("cumprod"), pytest.param( method("all"), marks=pytest.mark.xfail(reason="not implemented by pint yet"), @@ -2279,18 +2273,13 @@ def test_repr(self, func, variant, dtype): method("min"), pytest.param( method("prod"), - marks=pytest.mark.xfail( - reason="comparison of quantity with ndarrays in nanops not implemented" - ), + marks=pytest.mark.xfail(reason="not implemented by pint yet"), ), method("sum"), method("std"), method("var"), method("cumsum"), - pytest.param( - method("cumprod"), - marks=pytest.mark.xfail(reason="pint does not implement cumprod yet"), - ), + method("cumprod"), ), ids=repr, ) From a300a178f11b9e5f22d010fa0097b498e1535c0c Mon Sep 17 00:00:00 2001 From: Keewis Date: Sat, 21 Dec 2019 13:41:46 +0100 Subject: [PATCH 02/29] remove all xfails not related to indexes or external packages --- xarray/tests/test_units.py | 59 +++++--------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index ef2e35070e3..bf363fcee52 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2412,7 +2412,6 @@ def test_univariate_ufunc(self, units, error, dtype): assert_equal_with_units(expected, actual) - @pytest.mark.xfail(reason="xarray's `np.maximum` strips units") @pytest.mark.parametrize( "unit,error", ( @@ -2483,17 +2482,7 @@ def test_numpy_methods(self, func, dtype): assert_equal_with_units(expected, actual) @pytest.mark.parametrize( - "func", - ( - method("clip", min=3, max=8), - pytest.param( - method("searchsorted", v=5), - marks=pytest.mark.xfail( - reason="searchsorted somehow requires a undocumented `keys` argument" - ), - ), - ), - ids=repr, + "func", (method("clip", min=3, max=8), method("searchsorted", v=5)), ids=repr ) @pytest.mark.parametrize( "unit,error", @@ -2585,12 +2574,7 @@ def test_missing_value_filling(self, func, dtype): unit_registry.dimensionless, DimensionalityError, id="dimensionless" ), pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), - pytest.param( - unit_registry.cm, - None, - id="compatible_unit", - marks=pytest.mark.xfail(reason="fillna converts to value's unit"), - ), + pytest.param(unit_registry.cm, None, id="compatible_unit"), pytest.param(unit_registry.m, None, id="identical_unit"), ), ) @@ -2647,13 +2631,7 @@ def test_dropna(self, dtype): @pytest.mark.parametrize( "unit", ( - pytest.param( - 1, - id="no_unit", - marks=pytest.mark.xfail( - reason="pint's isin implementation does not work well with mixed args" - ), - ), + pytest.param(1, id="no_unit"), pytest.param(unit_registry.dimensionless, id="dimensionless"), pytest.param(unit_registry.s, id="incompatible_unit"), pytest.param(unit_registry.cm, id="compatible_unit"), @@ -2679,19 +2657,7 @@ def test_isin(self, unit, dtype): assert_equal_with_units(expected, actual) @pytest.mark.parametrize( - "variant", - ( - pytest.param( - "masking", - marks=pytest.mark.xfail(reason="array(nan) is not a quantity"), - ), - "replacing_scalar", - "replacing_array", - pytest.param( - "dropping", - marks=pytest.mark.xfail(reason="array(nan) is not a quantity"), - ), - ), + "variant", ("masking", "replacing_scalar", "replacing_array", "dropping") ) @pytest.mark.parametrize( "unit,error", @@ -3437,9 +3403,7 @@ def test_to_unstacked_dataset(self, dtype): method("transpose", "y", "x", "z"), method("stack", a=("x", "y")), method("set_index", x="x2"), - pytest.param( - method("shift", x=2), marks=pytest.mark.xfail(reason="strips units") - ), + method("shift", x=2), method("roll", x=2, roll_coords=False), method("sortby", "x2"), ), @@ -3478,13 +3442,7 @@ def test_stacking_reordering(self, func, dtype): marks=pytest.mark.xfail(reason="nanquantile not implemented"), ), method("reduce", func=np.sum, dim="x"), - pytest.param( - lambda x: x.dot(x), - id="method_dot", - marks=pytest.mark.xfail( - reason="pint does not implement the dot method" - ), - ), + pytest.param(lambda x: x.dot(x), id="method_dot"), ), ids=repr, ) @@ -3519,10 +3477,7 @@ def test_computation(self, func, dtype): method("groupby", "x"), method("groupby_bins", "y", bins=4), method("coarsen", y=2), - pytest.param( - method("rolling", y=3), - marks=pytest.mark.xfail(reason="rolling strips units"), - ), + pytest.param(method("rolling", y=3)), pytest.param( method("rolling_exp", y=3), marks=pytest.mark.xfail(reason="units not supported by numbagg"), From f5fd0cc76bf27987ed408fd9c63e5ed8fa9f0342 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 24 Dec 2019 00:21:03 +0100 Subject: [PATCH 03/29] switch away from using assert_equal_with_units --- xarray/tests/test_units.py | 78 +++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index bf363fcee52..1eca79303cd 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2295,7 +2295,7 @@ def test_aggregation(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -2313,7 +2313,7 @@ def test_unary_operations(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -2332,7 +2332,7 @@ def test_binary_operations(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "comparison", @@ -2382,7 +2382,7 @@ def test_comparison_operations(self, comparison, unit, error, dtype): strip_units(convert_units(to_compare_with, expected_units)), ) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "units,error", @@ -2410,7 +2410,7 @@ def test_univariate_ufunc(self, units, error, dtype): ) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit,error", @@ -2445,10 +2445,10 @@ def test_bivariate_ufunc(self, unit, error, dtype): ) actual = np.maximum(data_array, 0 * unit) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) actual = np.maximum(0 * unit, data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize("property", ("T", "imag", "real")) def test_numpy_properties(self, property, dtype): @@ -2464,7 +2464,7 @@ def test_numpy_properties(self, property, dtype): ) actual = getattr(data_array, property) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -2479,7 +2479,7 @@ def test_numpy_methods(self, func, dtype): expected = attach_units(strip_units(data_array), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", (method("clip", min=3, max=8), method("searchsorted", v=5)), ids=repr @@ -2522,7 +2522,7 @@ def test_numpy_methods_with_args(self, func, unit, error, dtype): ) actual = func(data_array, **kwargs) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", (method("isnull"), method("notnull"), method("count")), ids=repr @@ -2547,7 +2547,7 @@ def test_missing_value_detection(self, func, dtype): expected = func(strip_units(data_array)) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="ffill and bfill lose units in data") @pytest.mark.parametrize("func", (method("ffill"), method("bfill")), ids=repr) @@ -2564,7 +2564,7 @@ def test_missing_value_filling(self, func, dtype): ) actual = func(data_array, dim="x") - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit,error", @@ -2612,7 +2612,7 @@ def test_fillna(self, fill_value, unit, error, dtype): ) actual = func(data_array, value=value) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) def test_dropna(self, dtype): array = ( @@ -2626,7 +2626,7 @@ def test_dropna(self, dtype): expected = attach_units(strip_units(data_array).dropna(dim="x"), units) actual = data_array.dropna(dim="x") - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit", @@ -2654,7 +2654,7 @@ def test_isin(self, unit, dtype): ) & array.check(unit) actual = data_array.isin(values) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "variant", ("masking", "replacing_scalar", "replacing_array", "dropping") @@ -2707,7 +2707,7 @@ def test_where(self, variant, unit, error, dtype): ) actual = data_array.where(**kwargs) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="interpolate strips units") def test_interpolate_na(self, dtype): @@ -2722,7 +2722,7 @@ def test_interpolate_na(self, dtype): expected = attach_units(strip_units(data_array).interpolate_na(dim="x"), units) actual = data_array.interpolate_na(dim="x") - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit,error", @@ -2772,7 +2772,7 @@ def test_combine_first(self, unit, error, dtype): ) actual = data_array.combine_first(other) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit", @@ -2868,7 +2868,7 @@ def test_broadcast_like(self, unit, dtype): ) actual = arr1.broadcast_like(arr2) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "unit", @@ -2943,7 +2943,7 @@ def test_content_manipulation(self, func, dtype): expected = attach_units(func(strip_units(data_array), **stripped_kwargs), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", (pytest.param(method("copy", data=np.arange(20))),), ids=repr @@ -2969,7 +2969,7 @@ def test_content_manipulation_with_units(self, func, unit, dtype): ) actual = func(data_array, **kwargs) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "indices", @@ -2989,7 +2989,7 @@ def test_isel(self, indices, dtype): ) actual = data_array.isel(x=indices) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3032,7 +3032,7 @@ def test_sel(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.sel(x=values) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3075,7 +3075,7 @@ def test_loc(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.loc[{"x": values}] - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3118,7 +3118,7 @@ def test_drop_sel(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.drop_sel(x=values) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "shape", @@ -3146,7 +3146,7 @@ def test_squeeze(self, shape, dtype): strip_units(data_array).squeeze(), extract_units(data_array) ) actual = data_array.squeeze() - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) # try squeezing the dimensions separately names = tuple(dim for dim, coord in coords.items() if len(coord) == 1) @@ -3155,7 +3155,7 @@ def test_squeeze(self, shape, dtype): strip_units(data_array).squeeze(dim=name), extract_units(data_array) ) actual = data_array.squeeze(dim=name) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3177,7 +3177,7 @@ def test_head_tail_thin(self, func, dtype): ) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3217,7 +3217,7 @@ def test_interp(self, unit, error): ) actual = data_array.interp(x=new_coords) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes strip units") @pytest.mark.parametrize( @@ -3261,7 +3261,7 @@ def test_interp_like(self, unit, error): ) actual = data_array.interp_like(other) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3304,7 +3304,7 @@ def test_reindex(self, unit, error, dtype): ) actual = func(data_array, x=new_coords) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3350,7 +3350,7 @@ def test_reindex_like(self, unit, error, dtype): ) actual = data_array.reindex_like(other) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3372,7 +3372,7 @@ def test_stacking_stacked(self, func, dtype): expected = attach_units(func(strip_units(stacked)), {"data": unit_registry.m}) actual = func(stacked) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") def test_to_unstacked_dataset(self, dtype): @@ -3395,7 +3395,7 @@ def test_to_unstacked_dataset(self, dtype): ).rename({elem.magnitude: elem for elem in x}) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3429,7 +3429,7 @@ def test_stacking_reordering(self, func, dtype): expected = attach_units(func(strip_units(data_array)), {None: unit_registry.m}) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3469,7 +3469,7 @@ def test_computation(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3499,7 +3499,7 @@ def test_computation_objects(self, func, dtype): expected = attach_units(func(strip_units(data_array)).mean(), units) actual = func(data_array).mean() - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) def test_resample(self, dtype): array = np.linspace(0, 5, 10).astype(dtype) * unit_registry.m @@ -3513,7 +3513,7 @@ def test_resample(self, dtype): expected = attach_units(func(strip_units(data_array)).mean(), units) actual = func(data_array).mean() - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( "func", @@ -3552,7 +3552,7 @@ def test_grouped_operations(self, func, dtype): ) actual = func(data_array.groupby("y")) - assert_equal_with_units(expected, actual) + xr.testing.assert_identical(expected, actual) class TestDataset: From eed6c70b852be6e75eed273535ae7c20b5b46d14 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 24 Dec 2019 00:22:26 +0100 Subject: [PATCH 04/29] use assert_allclose in a few cases instead --- xarray/tests/test_units.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 1eca79303cd..35395461ccf 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2295,7 +2295,8 @@ def test_aggregation(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - xr.testing.assert_identical(expected, actual) + assert extract_units(expected) == extract_units(actual) + xr.testing.assert_allclose(expected, actual) @pytest.mark.parametrize( "func", @@ -3499,7 +3500,8 @@ def test_computation_objects(self, func, dtype): expected = attach_units(func(strip_units(data_array)).mean(), units) actual = func(data_array).mean() - xr.testing.assert_identical(expected, actual) + assert extract_units(expected) == extract_units(actual) + xr.testing.assert_allclose(expected, actual) def test_resample(self, dtype): array = np.linspace(0, 5, 10).astype(dtype) * unit_registry.m From 7198b46f3009b429462e6131bbe19edb4e9eea2b Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 24 Dec 2019 16:09:13 +0100 Subject: [PATCH 05/29] don't use a kwarg for searchsorted normally, this should work, but the documentation mismatches the implementation of searchsorted and names the keys as `keys` instead of `v` --- xarray/tests/test_units.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 35395461ccf..2be0d8fd231 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2483,7 +2483,7 @@ def test_numpy_methods(self, func, dtype): xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( - "func", (method("clip", min=3, max=8), method("searchsorted", v=5)), ids=repr + "func", (method("clip", min=3, max=8), method("searchsorted", 5)), ids=repr ) @pytest.mark.parametrize( "unit,error", @@ -2502,26 +2502,29 @@ def test_numpy_methods_with_args(self, func, unit, error, dtype): data_array = xr.DataArray(data=array) scalar_types = (int, float) + args = list(value * unit for value in func.args) kwargs = { key: (value * unit if isinstance(value, scalar_types) else value) for key, value in func.kwargs.items() } if error is not None: with pytest.raises(error): - func(data_array, **kwargs) + func(data_array, *args, **kwargs) return units = extract_units(data_array) - expected_units = extract_units(func(array, **kwargs)) + expected_units = extract_units(func(array, *args, **kwargs)) + stripped_args = [strip_units(convert_units(value, units)) for value in args] stripped_kwargs = { key: strip_units(convert_units(value, units)) for key, value in kwargs.items() } expected = attach_units( - func(strip_units(data_array), **stripped_kwargs), expected_units + func(strip_units(data_array), *stripped_args, **stripped_kwargs), + expected_units, ) - actual = func(data_array, **kwargs) + actual = func(data_array, *args, **kwargs) xr.testing.assert_identical(expected, actual) From 24775885a479a1ca6a73f13029ae214d3923344b Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 24 Dec 2019 23:44:28 +0100 Subject: [PATCH 06/29] move the tests for item into their own test function --- xarray/tests/test_units.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 2be0d8fd231..33bbb12157c 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2482,6 +2482,17 @@ def test_numpy_methods(self, func, dtype): xr.testing.assert_identical(expected, actual) + def test_item(self, dtype): + array = np.arange(10).astype(dtype) * unit_registry.m + data_array = xr.DataArray(data=array) + + func = method("item", 2) + + expected = func(strip_units(data_array)) * unit_registry.m + actual = func(data_array) + + assert np.all(expected == actual) + @pytest.mark.parametrize( "func", (method("clip", min=3, max=8), method("searchsorted", 5)), ids=repr ) @@ -2919,7 +2930,6 @@ def test_broadcast_equals(self, unit, dtype): method("reset_coords", names="x2"), method("copy"), method("astype", np.float32), - method("item", 1), ), ids=repr, ) From a660b4e444f74831e3c5d2d2aaaf5390973adad7 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 25 Dec 2019 01:58:44 +0100 Subject: [PATCH 07/29] move the searchsorted tests into their own test function --- xarray/tests/test_units.py | 68 +++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 33bbb12157c..b319d898aac 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2494,7 +2494,73 @@ def test_item(self, dtype): assert np.all(expected == actual) @pytest.mark.parametrize( - "func", (method("clip", min=3, max=8), method("searchsorted", 5)), ids=repr + "unit,error", + ( + pytest.param(1, DimensionalityError, id="no_unit"), + pytest.param( + unit_registry.dimensionless, DimensionalityError, id="dimensionless" + ), + pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), + pytest.param(unit_registry.cm, None, id="compatible_unit"), + pytest.param(unit_registry.m, None, id="identical_unit"), + ), + ) + @pytest.mark.parametrize( + "func", + ( + method("searchsorted", 5), + pytest.param( + function("searchsorted", 5), + marks=pytest.mark.xfail( + reason="xarray does not implement __array_function__" + ), + ), + ), + ids=repr, + ) + def test_searchsorted(self, func, unit, error, dtype): + array = np.arange(10).astype(dtype) * unit_registry.m + data_array = xr.DataArray(data=array) + + scalar_types = (int, float) + args = list(value * unit for value in func.args) + kwargs = { + key: (value * unit if isinstance(value, scalar_types) else value) + for key, value in func.kwargs.items() + } + if error is not None: + with pytest.raises(error): + func(data_array, *args, **kwargs) + + return + + units = extract_units(data_array) + expected_units = extract_units(func(array, *args, **kwargs)) + stripped_args = [strip_units(convert_units(value, units)) for value in args] + stripped_kwargs = { + key: strip_units(convert_units(value, units)) + for key, value in kwargs.items() + } + expected = attach_units( + func(strip_units(data_array), *stripped_args, **stripped_kwargs), + expected_units, + ) + actual = func(data_array, *args, **kwargs) + + np.testing.assert_allclose(expected, actual) + + @pytest.mark.parametrize( + "func", + ( + method("clip", min=3, max=8), + pytest.param( + function("clip", a_min=3, a_max=8), + marks=pytest.mark.xfail( + reason="xarray does not implement __array_function__" + ), + ), + ), + ids=repr, ) @pytest.mark.parametrize( "unit,error", From 41675cb9f0def01495dac3c86d304e81d87a3792 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 25 Dec 2019 21:37:22 +0100 Subject: [PATCH 08/29] remove a wrapping pytest.param --- xarray/tests/test_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index b319d898aac..0eb3ca5a692 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -3557,7 +3557,7 @@ def test_computation(self, func, dtype): method("groupby", "x"), method("groupby_bins", "y", bins=4), method("coarsen", y=2), - pytest.param(method("rolling", y=3)), + method("rolling", y=3), pytest.param( method("rolling_exp", y=3), marks=pytest.mark.xfail(reason="units not supported by numbagg"), From 570ad1d78c126e9839142d6fb0d50acbd5cbaf94 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 25 Dec 2019 21:38:26 +0100 Subject: [PATCH 09/29] treat objects implementing __array_function__ the same as ndarray --- xarray/core/arithmetic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xarray/core/arithmetic.py b/xarray/core/arithmetic.py index 571dfbe70ed..f01e32f3f28 100644 --- a/xarray/core/arithmetic.py +++ b/xarray/core/arithmetic.py @@ -35,7 +35,9 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): # See the docstring example for numpy.lib.mixins.NDArrayOperatorsMixin. out = kwargs.get("out", ()) for x in inputs + out: - if not isinstance(x, self._HANDLED_TYPES + (SupportsArithmetic,)): + if not isinstance( + x, self._HANDLED_TYPES + (SupportsArithmetic,) + ) and not hasattr(x, "__array_function__"): return NotImplemented if ufunc.signature is not None: From 308df3158e198810849d16cea7f91563b640b841 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 26 Dec 2019 00:42:16 +0100 Subject: [PATCH 10/29] mark numpy.median as xfailing --- xarray/tests/test_units.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 0eb3ca5a692..197feef7518 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2246,7 +2246,12 @@ def test_repr(self, func, variant, dtype): function("argmin"), function("max"), function("mean"), - function("median"), + pytest.param( + function("median"), + marks=pytest.mark.xfail( + reason="median does not work with dataarrays yet" + ), + ), function("min"), pytest.param( function("prod"), From 039d17d7a13381826cef2abd86140a83e9d6dd0a Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 12 Mar 2020 18:21:07 +0100 Subject: [PATCH 11/29] remove the xfail marks for the all and any tests --- xarray/tests/test_units.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 197feef7518..5f702c19704 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2234,14 +2234,8 @@ def test_repr(self, func, variant, dtype): @pytest.mark.parametrize( "func", ( - pytest.param( - function("all"), - marks=pytest.mark.xfail(reason="not implemented by pint yet"), - ), - pytest.param( - function("any"), - marks=pytest.mark.xfail(reason="not implemented by pint yet"), - ), + function("all"), + function("any"), function("argmax"), function("argmin"), function("max"), @@ -2262,14 +2256,8 @@ def test_repr(self, func, variant, dtype): function("var"), function("cumsum"), function("cumprod"), - pytest.param( - method("all"), - marks=pytest.mark.xfail(reason="not implemented by pint yet"), - ), - pytest.param( - method("any"), - marks=pytest.mark.xfail(reason="not implemented by pint yet"), - ), + method("all"), + method("any"), method("argmax"), method("argmin"), method("max"), From 796d5e7324cd6682df72508b94784338850d16a8 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 12 Mar 2020 18:48:38 +0100 Subject: [PATCH 12/29] use assert_units_equal to check the resulting units --- xarray/tests/test_units.py | 49 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 5f702c19704..2d639eb99fd 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2288,7 +2288,7 @@ def test_aggregation(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) - assert extract_units(expected) == extract_units(actual) + assert_units_equal(expected, actual) xr.testing.assert_allclose(expected, actual) @pytest.mark.parametrize( @@ -2307,6 +2307,7 @@ def test_unary_operations(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2326,6 +2327,7 @@ def test_binary_operations(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2376,6 +2378,7 @@ def test_comparison_operations(self, comparison, unit, error, dtype): strip_units(convert_units(to_compare_with, expected_units)), ) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2404,6 +2407,7 @@ def test_univariate_ufunc(self, units, error, dtype): ) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2439,9 +2443,11 @@ def test_bivariate_ufunc(self, unit, error, dtype): ) actual = np.maximum(data_array, 0 * unit) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) actual = np.maximum(0 * unit, data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize("property", ("T", "imag", "real")) @@ -2458,6 +2464,7 @@ def test_numpy_properties(self, property, dtype): ) actual = getattr(data_array, property) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2473,6 +2480,7 @@ def test_numpy_methods(self, func, dtype): expected = attach_units(strip_units(data_array), units) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) def test_item(self, dtype): @@ -2521,6 +2529,7 @@ def test_searchsorted(self, func, unit, error, dtype): key: (value * unit if isinstance(value, scalar_types) else value) for key, value in func.kwargs.items() } + if error is not None: with pytest.raises(error): func(data_array, *args, **kwargs) @@ -2540,6 +2549,7 @@ def test_searchsorted(self, func, unit, error, dtype): ) actual = func(data_array, *args, **kwargs) + assert_units_equal(expected, actual) np.testing.assert_allclose(expected, actual) @pytest.mark.parametrize( @@ -2596,6 +2606,7 @@ def test_numpy_methods_with_args(self, func, unit, error, dtype): ) actual = func(data_array, *args, **kwargs) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2621,6 +2632,7 @@ def test_missing_value_detection(self, func, dtype): expected = func(strip_units(data_array)) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="ffill and bfill lose units in data") @@ -2638,6 +2650,7 @@ def test_missing_value_filling(self, func, dtype): ) actual = func(data_array, dim="x") + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2686,6 +2699,7 @@ def test_fillna(self, fill_value, unit, error, dtype): ) actual = func(data_array, value=value) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) def test_dropna(self, dtype): @@ -2700,6 +2714,7 @@ def test_dropna(self, dtype): expected = attach_units(strip_units(data_array).dropna(dim="x"), units) actual = data_array.dropna(dim="x") + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2728,6 +2743,7 @@ def test_isin(self, unit, dtype): ) & array.check(unit) actual = data_array.isin(values) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2781,6 +2797,7 @@ def test_where(self, variant, unit, error, dtype): ) actual = data_array.where(**kwargs) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="interpolate strips units") @@ -2796,6 +2813,7 @@ def test_interpolate_na(self, dtype): expected = attach_units(strip_units(data_array).interpolate_na(dim="x"), units) actual = data_array.interpolate_na(dim="x") + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2846,6 +2864,7 @@ def test_combine_first(self, unit, error, dtype): ) actual = data_array.combine_first(other) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -2942,6 +2961,7 @@ def test_broadcast_like(self, unit, dtype): ) actual = arr1.broadcast_like(arr2) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3016,6 +3036,7 @@ def test_content_manipulation(self, func, dtype): expected = attach_units(func(strip_units(data_array), **stripped_kwargs), units) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3042,6 +3063,8 @@ def test_content_manipulation_with_units(self, func, unit, dtype): ) actual = func(data_array, **kwargs) + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3062,6 +3085,7 @@ def test_isel(self, indices, dtype): ) actual = data_array.isel(x=indices) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3105,6 +3129,8 @@ def test_sel(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.sel(x=values) + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3148,6 +3174,8 @@ def test_loc(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.loc[{"x": values}] + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3191,6 +3219,8 @@ def test_drop_sel(self, raw_values, unit, error, dtype): extract_units(data_array), ) actual = data_array.drop_sel(x=values) + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3219,6 +3249,8 @@ def test_squeeze(self, shape, dtype): strip_units(data_array).squeeze(), extract_units(data_array) ) actual = data_array.squeeze() + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) # try squeezing the dimensions separately @@ -3228,6 +3260,8 @@ def test_squeeze(self, shape, dtype): strip_units(data_array).squeeze(dim=name), extract_units(data_array) ) actual = data_array.squeeze(dim=name) + + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3250,6 +3284,7 @@ def test_head_tail_thin(self, func, dtype): ) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3290,6 +3325,7 @@ def test_interp(self, unit, error): ) actual = data_array.interp(x=new_coords) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes strip units") @@ -3334,6 +3370,7 @@ def test_interp_like(self, unit, error): ) actual = data_array.interp_like(other) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3377,6 +3414,7 @@ def test_reindex(self, unit, error, dtype): ) actual = func(data_array, x=new_coords) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3423,6 +3461,7 @@ def test_reindex_like(self, unit, error, dtype): ) actual = data_array.reindex_like(other) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3445,6 +3484,7 @@ def test_stacking_stacked(self, func, dtype): expected = attach_units(func(strip_units(stacked)), {"data": unit_registry.m}) actual = func(stacked) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @@ -3468,6 +3508,7 @@ def test_to_unstacked_dataset(self, dtype): ).rename({elem.magnitude: elem for elem in x}) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3502,6 +3543,7 @@ def test_stacking_reordering(self, func, dtype): expected = attach_units(func(strip_units(data_array)), {None: unit_registry.m}) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3542,6 +3584,7 @@ def test_computation(self, func, dtype): expected = attach_units(func(strip_units(data_array)), units) actual = func(data_array) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3572,7 +3615,7 @@ def test_computation_objects(self, func, dtype): expected = attach_units(func(strip_units(data_array)).mean(), units) actual = func(data_array).mean() - assert extract_units(expected) == extract_units(actual) + assert_units_equal(expected, actual) xr.testing.assert_allclose(expected, actual) def test_resample(self, dtype): @@ -3587,6 +3630,7 @@ def test_resample(self, dtype): expected = attach_units(func(strip_units(data_array)).mean(), units) actual = func(data_array).mean() + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @pytest.mark.parametrize( @@ -3626,6 +3670,7 @@ def test_grouped_operations(self, func, dtype): ) actual = func(data_array.groupby("y")) + assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) From b5c9a21a4aebab2828f57991b82c02fbda61cff2 Mon Sep 17 00:00:00 2001 From: Keewis Date: Sat, 21 Mar 2020 23:27:13 +0100 Subject: [PATCH 13/29] don't attempt to use interpolate_na with int dtype arrays --- xarray/tests/test_units.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 2d639eb99fd..501afc4e06f 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2801,13 +2801,13 @@ def test_where(self, variant, unit, error, dtype): xr.testing.assert_identical(expected, actual) @pytest.mark.xfail(reason="interpolate strips units") - def test_interpolate_na(self, dtype): + def test_interpolate_na(self): array = ( np.array([-1.03, 0.1, 1.4, np.nan, 2.3, np.nan, np.nan, 9.1]) * unit_registry.m ) x = np.arange(len(array)) - data_array = xr.DataArray(data=array, coords={"x": x}, dims="x").astype(dtype) + data_array = xr.DataArray(data=array, coords={"x": x}, dims="x") units = extract_units(data_array) expected = attach_units(strip_units(data_array).interpolate_na(dim="x"), units) From 741a97921ffaef9389b69bb3af68ff57227ee112 Mon Sep 17 00:00:00 2001 From: Keewis Date: Sat, 21 Mar 2020 23:28:06 +0100 Subject: [PATCH 14/29] update the xfail reason for DataArray.interpolate_na --- xarray/tests/test_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 501afc4e06f..e54dbca728e 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2800,7 +2800,7 @@ def test_where(self, variant, unit, error, dtype): assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) - @pytest.mark.xfail(reason="interpolate strips units") + @pytest.mark.xfail(reason="uses numpy.vectorize") def test_interpolate_na(self): array = ( np.array([-1.03, 0.1, 1.4, np.nan, 2.3, np.nan, np.nan, 9.1]) From 00a0b384250285d13a629df578635d0b1a8ef6d2 Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 01:13:04 +0100 Subject: [PATCH 15/29] xfail the compatible units bivariate_ufunc test and don't use 0 --- xarray/tests/test_units.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index e54dbca728e..7c4aa136a6f 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2418,7 +2418,12 @@ def test_univariate_ufunc(self, units, error, dtype): unit_registry.dimensionless, DimensionalityError, id="dimensionless" ), pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), - pytest.param(unit_registry.mm, None, id="compatible_unit"), + pytest.param( + unit_registry.mm, + None, + id="compatible_unit", + marks=pytest.mark.xfail(reason="pint converts to the wrong units"), + ), pytest.param(unit_registry.m, None, id="identical_unit"), ), ) @@ -2429,7 +2434,7 @@ def test_bivariate_ufunc(self, unit, error, dtype): if error is not None: with pytest.raises(error): - np.maximum(data_array, 0 * unit) + np.maximum(data_array, 1 * unit) return @@ -2437,16 +2442,16 @@ def test_bivariate_ufunc(self, unit, error, dtype): expected = attach_units( np.maximum( strip_units(data_array), - strip_units(convert_units(0 * unit, expected_units)), + strip_units(convert_units(1 * unit, expected_units)), ), expected_units, ) - actual = np.maximum(data_array, 0 * unit) + actual = np.maximum(data_array, 1 * unit) assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) - actual = np.maximum(0 * unit, data_array) + actual = np.maximum(1 * unit, data_array) assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) From a27fea7cf4d58cc8064b351f2b046584a211de9f Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:07:09 +0100 Subject: [PATCH 16/29] combine and expand the reindex and interp tests --- xarray/tests/test_units.py | 102 ++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 7c4aa136a6f..89ca457b80f 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -3292,6 +3292,38 @@ def test_head_tail_thin(self, func, dtype): assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) + @pytest.mark.parametrize("variant", ("data", "coords")) + @pytest.mark.parametrize( + "func", + ( + pytest.param( + method("interp"), marks=pytest.mark.xfail(reason="uses scipy") + ), + method("reindex"), + ), + ids=repr, + ) + def test_interp_reindex(self, variant, func, dtype): + variants = { + "data": (unit_registry.m, 1), + "coords": (1, unit_registry.m), + } + data_unit, coord_unit = variants.get(variant) + + array = np.linspace(1, 2, 10).astype(dtype) * data_unit + y = np.arange(10) * coord_unit + + x = np.arange(10) + new_x = np.arange(10) + 0.5 + data_array = xr.DataArray(array, coords={"x": x, "y": ("x", y)}, dims="x") + + units = extract_units(data_array) + expected = attach_units(func(strip_units(data_array), x=new_x), units) + actual = func(data_array, x=new_x) + + assert_units_equal(expected, actual) + xr.testing.assert_allclose(expected, actual) + @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( "unit,error", @@ -3305,30 +3337,30 @@ def test_head_tail_thin(self, func, dtype): pytest.param(unit_registry.m, None, id="identical_unit"), ), ) - def test_interp(self, unit, error): - array = np.linspace(1, 2, 10 * 5).reshape(10, 5) * unit_registry.degK - new_coords = (np.arange(10) + 0.5) * unit - coords = { - "x": np.arange(10) * unit_registry.m, - "y": np.arange(5) * unit_registry.m, - } - - data_array = xr.DataArray(array, coords=coords, dims=("x", "y")) + @pytest.mark.parametrize( + "func", (method("interp"), method("reindex")), ids=repr, + ) + def test_interp_reindex_indexing(self, func, unit, error, dtype): + array = np.linspace(1, 2, 10).astype(dtype) + x = np.arange(10) * unit_registry.m + new_x = (np.arange(10) + 0.5) * unit + data_array = xr.DataArray(array, coords={"x": x}, dims="x") if error is not None: with pytest.raises(error): - data_array.interp(x=new_coords) + func(data_array, x=new_x) return units = extract_units(data_array) expected = attach_units( - strip_units(data_array).interp( - x=strip_units(convert_units(new_coords, {None: unit_registry.m})) + func( + strip_units(data_array), + x=strip_units(convert_units(new_x, {None: unit_registry.m})), ), units, ) - actual = data_array.interp(x=new_coords) + actual = func(data_array, x=new_x) assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) @@ -3378,50 +3410,6 @@ def test_interp_like(self, unit, error): assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) - @pytest.mark.xfail(reason="indexes don't support units") - @pytest.mark.parametrize( - "unit,error", - ( - pytest.param(1, DimensionalityError, id="no_unit"), - pytest.param( - unit_registry.dimensionless, DimensionalityError, id="dimensionless" - ), - pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), - pytest.param(unit_registry.cm, None, id="compatible_unit"), - pytest.param(unit_registry.m, None, id="identical_unit"), - ), - ) - def test_reindex(self, unit, error, dtype): - array = ( - np.linspace(1, 2, 10 * 5).reshape(10, 5).astype(dtype) * unit_registry.degK - ) - new_coords = (np.arange(10) + 0.5) * unit - coords = { - "x": np.arange(10) * unit_registry.m, - "y": np.arange(5) * unit_registry.m, - } - - data_array = xr.DataArray(array, coords=coords, dims=("x", "y")) - func = method("reindex") - - if error is not None: - with pytest.raises(error): - func(data_array, x=new_coords) - - return - - expected = attach_units( - func( - strip_units(data_array), - x=strip_units(convert_units(new_coords, {None: unit_registry.m})), - ), - {None: unit_registry.degK}, - ) - actual = func(data_array, x=new_coords) - - assert_units_equal(expected, actual) - xr.testing.assert_identical(expected, actual) - @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( "unit,error", From 0fed2cca904b2677005da99e89c1e8754627075b Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:14:18 +0100 Subject: [PATCH 17/29] combine and expand the reindex_like and interp_like tests --- xarray/tests/test_units.py | 82 +++++++++++++++----------------------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 89ca457b80f..9b7164fbd05 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -3365,50 +3365,38 @@ def test_interp_reindex_indexing(self, func, unit, error, dtype): assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) - @pytest.mark.xfail(reason="indexes strip units") + @pytest.mark.parametrize("variant", ("data", "coords")) @pytest.mark.parametrize( - "unit,error", + "func", ( - pytest.param(1, DimensionalityError, id="no_unit"), pytest.param( - unit_registry.dimensionless, DimensionalityError, id="dimensionless" + method("interp_like"), marks=pytest.mark.xfail(reason="uses scipy") ), - pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), - pytest.param(unit_registry.cm, None, id="compatible_unit"), - pytest.param(unit_registry.m, None, id="identical_unit"), + method("reindex_like"), ), + ids=repr, ) - def test_interp_like(self, unit, error): - array = np.linspace(1, 2, 10 * 5).reshape(10, 5) * unit_registry.degK - coords = { - "x": (np.arange(10) + 0.3) * unit_registry.m, - "y": (np.arange(5) + 0.3) * unit_registry.m, + def test_interp_reindex_like(self, variant, func, dtype): + variants = { + "data": (unit_registry.m, 1), + "coords": (1, unit_registry.m), } + data_unit, coord_unit = variants.get(variant) - data_array = xr.DataArray(array, coords=coords, dims=("x", "y")) - other = xr.DataArray( - data=np.empty((20, 10)) * unit_registry.degK, - coords={"x": np.arange(20) * unit, "y": np.arange(10) * unit}, - dims=("x", "y"), - ) - - if error is not None: - with pytest.raises(error): - data_array.interp_like(other) + array = np.linspace(1, 2, 10).astype(dtype) * data_unit + coord = np.arange(10) * coord_unit - return + x = np.arange(10) + new_x = np.arange(-2, 2) + 0.5 + data_array = xr.DataArray(array, coords={"x": x, "y": ("x", coord)}, dims="x") + other = xr.DataArray(np.empty_like(new_x), coords={"x": new_x}, dims="x") units = extract_units(data_array) - expected = attach_units( - strip_units(data_array).interp_like( - strip_units(convert_units(other, units)) - ), - units, - ) - actual = data_array.interp_like(other) + expected = attach_units(func(strip_units(data_array), other), units) + actual = func(data_array, other) assert_units_equal(expected, actual) - xr.testing.assert_identical(expected, actual) + xr.testing.assert_allclose(expected, actual) @pytest.mark.xfail(reason="indexes don't support units") @pytest.mark.parametrize( @@ -3423,36 +3411,32 @@ def test_interp_like(self, unit, error): pytest.param(unit_registry.m, None, id="identical_unit"), ), ) - def test_reindex_like(self, unit, error, dtype): - array = ( - np.linspace(1, 2, 10 * 5).reshape(10, 5).astype(dtype) * unit_registry.degK - ) - coords = { - "x": (np.arange(10) + 0.3) * unit_registry.m, - "y": (np.arange(5) + 0.3) * unit_registry.m, - } + @pytest.mark.parametrize( + "func", (method("interp_like"), method("reindex_like")), ids=repr, + ) + def test_interp_reindex_like_indexing(self, func, unit, error, dtype): + array = np.linspace(1, 2, 10).astype(dtype) + x = np.arange(10) * unit_registry.m + new_x = (np.arange(-2, 2) + 0.5) * unit - data_array = xr.DataArray(array, coords=coords, dims=("x", "y")) - other = xr.DataArray( - data=np.empty((20, 10)) * unit_registry.degK, - coords={"x": np.arange(20) * unit, "y": np.arange(10) * unit}, - dims=("x", "y"), - ) + data_array = xr.DataArray(array, coords={"x": x}, dims="x") + other = xr.DataArray(np.empty_like(new_x), {"x": new_x}, dims="x") if error is not None: with pytest.raises(error): - data_array.reindex_like(other) + func(data_array, other) return units = extract_units(data_array) expected = attach_units( - strip_units(data_array).reindex_like( - strip_units(convert_units(other, units)) + func( + strip_units(data_array), + strip_units(convert_units(other, {None: unit_registry.m})), ), units, ) - actual = data_array.reindex_like(other) + actual = func(data_array, other) assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) From d30c6dd0fa550f9e653576c1953044b772d3ca2b Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:23:35 +0100 Subject: [PATCH 18/29] xfail the quantile tests if pint is not recent enough --- xarray/tests/test_units.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 9b7164fbd05..be054f29008 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -1885,7 +1885,10 @@ def test_squeeze(self, dtype): method("coarsen", windows={"y": 2}, func=np.mean), pytest.param( method("quantile", q=[0.25, 0.75]), - marks=pytest.mark.xfail(reason="nanquantile not implemented"), + marks=pytest.mark.xfail( + LooseVersion(pint.__version__) < "0.12", + reason="quantile / nanquantile not implemented yet", + ), ), pytest.param( method("rank", dim="x"), @@ -3531,7 +3534,10 @@ def test_stacking_reordering(self, func, dtype): method("integrate", dim="x"), pytest.param( method("quantile", q=[0.25, 0.75]), - marks=pytest.mark.xfail(reason="nanquantile not implemented"), + marks=pytest.mark.xfail( + LooseVersion(pint.__version__) < "0.12", + reason="quantile / nanquantile not implemented yet", + ), ), method("reduce", func=np.sum, dim="x"), pytest.param(lambda x: x.dot(x), id="method_dot"), @@ -3618,7 +3624,10 @@ def test_resample(self, dtype): method("last"), pytest.param( method("quantile", q=[0.25, 0.5, 0.75], dim="x"), - marks=pytest.mark.xfail(reason="nanquantile not implemented"), + marks=pytest.mark.xfail( + LooseVersion(pint.__version__) < "0.12", + reason="quantile / nanquantile not implemented yet", + ), ), ), ids=repr, From bbd6b90cdc5a99ac89fcdac6ace9617a0fb1e93a Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:25:22 +0100 Subject: [PATCH 19/29] xfail the rolling tests --- xarray/tests/test_units.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index be054f29008..46cc4419151 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -3576,7 +3576,12 @@ def test_computation(self, func, dtype): method("groupby", "x"), method("groupby_bins", "y", bins=4), method("coarsen", y=2), - method("rolling", y=3), + pytest.param( + method("rolling", y=3), + marks=pytest.mark.xfail( + reason="numpy.lib.stride_tricks.as_strided converts to ndarray" + ), + ), pytest.param( method("rolling_exp", y=3), marks=pytest.mark.xfail(reason="units not supported by numbagg"), From db987f167c0e2b8977ea13448e725f088016aefc Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:26:49 +0100 Subject: [PATCH 20/29] don't xfail combine_first it currently does not test indexing, so probably will need a new test for that. --- xarray/tests/test_units.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 46cc4419151..b7c3caebd61 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2832,18 +2832,8 @@ def test_interpolate_na(self): unit_registry.dimensionless, DimensionalityError, id="dimensionless" ), pytest.param(unit_registry.s, DimensionalityError, id="incompatible_unit"), - pytest.param( - unit_registry.cm, - None, - id="compatible_unit", - marks=pytest.mark.xfail(reason="depends on reindex"), - ), - pytest.param( - unit_registry.m, - None, - id="identical_unit", - marks=pytest.mark.xfail(reason="depends on reindex"), - ), + pytest.param(unit_registry.cm, None, id="compatible_unit",), + pytest.param(unit_registry.m, None, id="identical_unit",), ), ) def test_combine_first(self, unit, error, dtype): From 7a1da5a5ebd02f8d2f863eaa93a16cc69881117e Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:32:15 +0100 Subject: [PATCH 21/29] use numpy's assert_allclose --- xarray/tests/test_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index b7c3caebd61..aad52fea279 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2500,7 +2500,7 @@ def test_item(self, dtype): expected = func(strip_units(data_array)) * unit_registry.m actual = func(data_array) - assert np.all(expected == actual) + np.testing.assert_allclose(expected, actual) @pytest.mark.parametrize( "unit,error", From 6a7f22e43514fa0253d1b211dee4fbd5097a9053 Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 15:34:23 +0100 Subject: [PATCH 22/29] don't add dimension coordinates if they're not necessary --- xarray/tests/test_units.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index aad52fea279..33639201526 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2632,10 +2632,7 @@ def test_missing_value_detection(self, func, dtype): ) * unit_registry.degK ) - x = np.arange(array.shape[0]) * unit_registry.m - y = np.arange(array.shape[1]) * unit_registry.m - - data_array = xr.DataArray(data=array, coords={"x": x, "y": y}, dims=("x", "y")) + data_array = xr.DataArray(data=array) expected = func(strip_units(data_array)) actual = func(data_array) From 1b74c042bea0e9fb9127fe88ea1f2252fb291fc7 Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 22 Mar 2020 16:33:17 +0100 Subject: [PATCH 23/29] add the PR to the list of related PRs --- doc/whats-new.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 86272cf8710..cbc7cfd275a 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -35,7 +35,8 @@ New Features - Support new h5netcdf backend keyword `phony_dims` (available from h5netcdf v0.8.0 for :py:class:`~xarray.backends.H5NetCDFStore`. By `Kai Mühlbauer `_. -- Support unit aware arrays with pint. (:issue:`3594`, :pull:`3706`, :pull:`3611`) +- Support unit aware arrays with pint. (:issue:`3594`, :pull:`3706`, + :pull:`3611`, :pull:`3643`) By `Justus Magin `_. - :py:meth:`Dataset.groupby` and :py:meth:`DataArray.groupby` now raise a `TypeError` on multiple string arguments. Receiving multiple string arguments From c4ece7e8399bc024a62996ce59810e17c6e9839d Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 29 Mar 2020 20:23:17 +0200 Subject: [PATCH 24/29] move the whats-new.rst entry to 0.16.0 --- doc/whats-new.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index c968b21f54b..560fda856b0 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -41,6 +41,8 @@ New Features - Implement :py:meth:`DataArray.idxmax`, :py:meth:`DataArray.idxmin`, :py:meth:`Dataset.idxmax`, :py:meth:`Dataset.idxmin`. (:issue:`60`, :pull:`3871`) By `Todd Jennings `_ +- More support for unit aware arrays with pint (:pull:`3643`) + By `Justus Magin `_. Bug fixes @@ -100,8 +102,6 @@ New Features - Support new h5netcdf backend keyword `phony_dims` (available from h5netcdf v0.8.0 for :py:class:`~xarray.backends.H5NetCDFStore`. By `Kai Mühlbauer `_. -- Support unit aware arrays with pint. (:issue:`3594`, :pull:`3706`, - :pull:`3611`, :pull:`3643`) - Add partial support for unit aware arrays with pint. (:pull:`3706`, :pull:`3611`) By `Justus Magin `_. - :py:meth:`Dataset.groupby` and :py:meth:`DataArray.groupby` now raise a From c91c2a9a539d559e1019fb0d119f1f8c91b9b69b Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 2 Apr 2020 23:01:20 +0200 Subject: [PATCH 25/29] check for __array_ufunc__ to decide if the type is supported --- xarray/core/arithmetic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/arithmetic.py b/xarray/core/arithmetic.py index f01e32f3f28..dde9b58255f 100644 --- a/xarray/core/arithmetic.py +++ b/xarray/core/arithmetic.py @@ -37,7 +37,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): for x in inputs + out: if not isinstance( x, self._HANDLED_TYPES + (SupportsArithmetic,) - ) and not hasattr(x, "__array_function__"): + ) and not hasattr(x, "__array_ufunc__"): return NotImplemented if ufunc.signature is not None: From c3cd732c0e08d45fdb5f0dbf9c5a5ab9153dc0d5 Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 6 Apr 2020 15:54:38 +0200 Subject: [PATCH 26/29] xfail the bivariate ufunc tests --- xarray/tests/test_units.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 548f2b283a8..6045c729c43 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2403,6 +2403,7 @@ def test_univariate_ufunc(self, units, error, dtype): assert_units_equal(expected, actual) xr.testing.assert_identical(expected, actual) + @pytest.mark.xfail(reason="needs the type register system for __array_ufunc__") @pytest.mark.parametrize( "unit,error", ( From c09b6ed9d8992e6e8c65ba2a0826f9100a7261ac Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 6 Apr 2020 15:55:26 +0200 Subject: [PATCH 27/29] remove the check for __array_ufunc__ --- xarray/core/arithmetic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xarray/core/arithmetic.py b/xarray/core/arithmetic.py index dde9b58255f..571dfbe70ed 100644 --- a/xarray/core/arithmetic.py +++ b/xarray/core/arithmetic.py @@ -35,9 +35,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): # See the docstring example for numpy.lib.mixins.NDArrayOperatorsMixin. out = kwargs.get("out", ()) for x in inputs + out: - if not isinstance( - x, self._HANDLED_TYPES + (SupportsArithmetic,) - ) and not hasattr(x, "__array_ufunc__"): + if not isinstance(x, self._HANDLED_TYPES + (SupportsArithmetic,)): return NotImplemented if ufunc.signature is not None: From 28ec56615698772fc6b4ce3c8267534099e41dc0 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 21 Apr 2020 18:25:01 +0200 Subject: [PATCH 28/29] skip the DataArray.identical tests --- xarray/tests/test_units.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 6045c729c43..3d971814c59 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -1660,7 +1660,7 @@ def test_missing_value_fillna(self, unit, error): method("equals"), pytest.param( method("identical"), - marks=pytest.mark.skip(reason="behaviour of identical is unclear"), + marks=pytest.mark.skip(reason="behavior of identical is undecided"), ), ), ids=repr, @@ -2873,7 +2873,17 @@ def test_combine_first(self, unit, error, dtype): "coords", ), ) - @pytest.mark.parametrize("func", (method("equals"), method("identical")), ids=repr) + @pytest.mark.parametrize( + "func", + ( + method("equals"), + pytest.params( + method("identical"), + marks=pytest.mark.skip(reason="the behavior of identical is undecided"), + ), + ), + ids=repr, + ) def test_comparisons(self, func, variation, unit, dtype): def is_compatible(a, b): a = a if a is not None else 1 From 4303d6b92a33b67045b7d84a2856ff165fa9a7fd Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 21 Apr 2020 18:58:48 +0200 Subject: [PATCH 29/29] use pytest.param --- xarray/tests/test_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 3d971814c59..5dd4a42cff0 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -2877,7 +2877,7 @@ def test_combine_first(self, unit, error, dtype): "func", ( method("equals"), - pytest.params( + pytest.param( method("identical"), marks=pytest.mark.skip(reason="the behavior of identical is undecided"), ),