From b81a624c49a14dde59d7f9831ac8ff7a2790ed54 Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Sat, 16 May 2020 13:21:06 +0900 Subject: [PATCH 1/6] Fixes 2223 --- doc/whats-new.rst | 5 +++++ xarray/core/missing.py | 15 ++++++++++++++- xarray/testing.py | 7 +------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index cd30fab0160..9fa828bb7ea 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -34,6 +34,11 @@ Breaking changes (:pull:`3274`) By `Elliott Sales de Andrade `_ +Enhancements +~~~~~~~~~~~~ +- Performance improvement of :py:meth:`DataArray.interp` and :py:func:`Dataset.interp` (:issue:`2223`) + By `Keisuke Fujii `_. + New Features ~~~~~~~~~~~~ - Added :py:meth:`DataArray.polyfit` and :py:func:`xarray.polyval` for fitting polynomials. (:issue:`3349`) diff --git a/xarray/core/missing.py b/xarray/core/missing.py index f973b4a5468..374eaec1fa7 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -619,6 +619,19 @@ def interp(var, indexes_coords, method, **kwargs): # default behavior kwargs["bounds_error"] = kwargs.get("bounds_error", False) + # check if the interpolation can be done in orthogonal manner + if ( + len(indexes_coords) > 1 + and method in ["linear", "nearest"] + and all(dest[1].ndim == 1 for dest in indexes_coords.values()) + and len(set([d[1].dims[0] for d in indexes_coords.values()])) + == len(indexes_coords) + ): + # interpolate sequentially + for dim, dest in indexes_coords.items(): + var = interp(var, {dim: dest}, method, **kwargs) + return var + # target dimensions dims = list(indexes_coords) x, new_x = zip(*[indexes_coords[d] for d in dims]) @@ -659,7 +672,7 @@ def interp_func(var, x, new_x, method, kwargs): New coordinates. Should not contain NaN. method: string {'linear', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic'} for - 1-dimensional itnterpolation. + 1-dimensional interpolation. {'linear', 'nearest'} for multidimensional interpolation **kwargs: Optional keyword arguments to be passed to scipy.interpolator diff --git a/xarray/testing.py b/xarray/testing.py index ac189f7e023..e7bf5f9221a 100644 --- a/xarray/testing.py +++ b/xarray/testing.py @@ -10,12 +10,7 @@ from xarray.core.indexes import default_indexes from xarray.core.variable import IndexVariable, Variable -__all__ = ( - "assert_allclose", - "assert_chunks_equal", - "assert_equal", - "assert_identical", -) +__all__ = ("assert_allclose", "assert_chunks_equal", "assert_equal", "assert_identical") def _decode_string_data(data): From 7c1919f08d33c98db9b213fa993f64c5e113822b Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Mon, 18 May 2020 05:55:50 +0900 Subject: [PATCH 2/6] more tests --- xarray/tests/test_interp.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 0502348160e..157055eb95c 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -699,3 +699,19 @@ def test_3641(): times = xr.cftime_range("0001", periods=3, freq="500Y") da = xr.DataArray(range(3), dims=["time"], coords=[times]) da.interp(time=["0002-05-01"]) + + +def test_decompose(): + da = xr.DataArray( + np.arange(6).reshape(3, 2), + dims=["x", "y"], + coords={"x": [0, 1, 2], "y": [-0.1, -0.3]}, + ) + x_new = xr.DataArray([0.5, 1.5, 2.5], dims=["x1"]) + y_new = xr.DataArray([-0.15, -0.25], dims=["y1"]) + x_broadcast, y_broadcast = xr.broadcast(x_new, y_new) + assert x_broadcast.ndim == 2 + + actual = da.interp(x=x_new, y=y_new) + expected = da.interp(x=x_broadcast, y=y_broadcast) + assert_allclose(actual, expected) From 4a4e2953e54d426bccc8a8f803d064afdde6dc4e Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Mon, 18 May 2020 07:11:14 +0900 Subject: [PATCH 3/6] add @requires_scipy to test --- xarray/tests/test_interp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 157055eb95c..cd7504fea8a 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -701,6 +701,7 @@ def test_3641(): da.interp(time=["0002-05-01"]) +@requires_scipy def test_decompose(): da = xr.DataArray( np.arange(6).reshape(3, 2), From 238a08dc742a5d4f21b9a90cb28f313531678f32 Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Mon, 18 May 2020 08:09:47 +0900 Subject: [PATCH 4/6] fix tests --- xarray/tests/test_interp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index cd7504fea8a..1a3a7159a1a 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -713,6 +713,8 @@ def test_decompose(): x_broadcast, y_broadcast = xr.broadcast(x_new, y_new) assert x_broadcast.ndim == 2 - actual = da.interp(x=x_new, y=y_new) - expected = da.interp(x=x_broadcast, y=y_broadcast) + actual = da.interp(x=x_new, y=y_new).drop(('x', 'y')) + expected = da.interp(x=x_broadcast, y=y_broadcast).drop(('x', 'y')) + print(actual) + print(expected) assert_allclose(actual, expected) From 200daccc8672e4794dff9b5df9e6aa581405253c Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Mon, 18 May 2020 08:10:13 +0900 Subject: [PATCH 5/6] black --- xarray/tests/test_interp.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 1a3a7159a1a..be816c3ae49 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -713,8 +713,6 @@ def test_decompose(): x_broadcast, y_broadcast = xr.broadcast(x_new, y_new) assert x_broadcast.ndim == 2 - actual = da.interp(x=x_new, y=y_new).drop(('x', 'y')) - expected = da.interp(x=x_broadcast, y=y_broadcast).drop(('x', 'y')) - print(actual) - print(expected) + actual = da.interp(x=x_new, y=y_new).drop(("x", "y")) + expected = da.interp(x=x_broadcast, y=y_broadcast).drop(("x", "y")) assert_allclose(actual, expected) From 1a7d738ea82cf714a28b4b2f8dcdc711d5c39fc6 Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Mon, 25 May 2020 06:17:07 +0900 Subject: [PATCH 6/6] update whatsnew. Added a test for nearest --- doc/whats-new.rst | 4 +++- xarray/tests/test_interp.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 9fa828bb7ea..cc40985c64d 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -36,7 +36,9 @@ Breaking changes Enhancements ~~~~~~~~~~~~ -- Performance improvement of :py:meth:`DataArray.interp` and :py:func:`Dataset.interp` (:issue:`2223`) +- Performance improvement of :py:meth:`DataArray.interp` and :py:func:`Dataset.interp` + For orthogonal linear- and nearest-neighbor interpolation, we do 1d-interpolation sequentially + rather than interpolating in multidimensional space. (:issue:`2223`) By `Keisuke Fujii `_. New Features diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index be816c3ae49..7a0dda216e2 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -702,7 +702,8 @@ def test_3641(): @requires_scipy -def test_decompose(): +@pytest.mark.parametrize("method", ["nearest", "linear"]) +def test_decompose(method): da = xr.DataArray( np.arange(6).reshape(3, 2), dims=["x", "y"], @@ -713,6 +714,6 @@ def test_decompose(): x_broadcast, y_broadcast = xr.broadcast(x_new, y_new) assert x_broadcast.ndim == 2 - actual = da.interp(x=x_new, y=y_new).drop(("x", "y")) - expected = da.interp(x=x_broadcast, y=y_broadcast).drop(("x", "y")) + actual = da.interp(x=x_new, y=y_new, method=method).drop(("x", "y")) + expected = da.interp(x=x_broadcast, y=y_broadcast, method=method).drop(("x", "y")) assert_allclose(actual, expected)