From 3b79c76cd00b7fec22a37ca2c0c14845c74c1f0f Mon Sep 17 00:00:00 2001 From: hollymandel Date: Sat, 5 Oct 2024 12:50:05 -0700 Subject: [PATCH 01/23] nd interpolants added --- doc/user-guide/interpolation.rst | 5 +- xarray/core/dataarray.py | 147 ++++++++++++++++--------- xarray/core/dataset.py | 139 ++++++++++++++--------- xarray/core/missing.py | 29 +++-- xarray/core/types.py | 12 +- xarray/tests/test_interp.py | 183 +++++++++++++++++++++++++------ 6 files changed, 367 insertions(+), 148 deletions(-) diff --git a/doc/user-guide/interpolation.rst b/doc/user-guide/interpolation.rst index 53d8a52b342..3bc055ae78e 100644 --- a/doc/user-guide/interpolation.rst +++ b/doc/user-guide/interpolation.rst @@ -132,10 +132,11 @@ It is now possible to safely compute the difference ``other - interpolated``. Interpolation methods --------------------- -We use :py:class:`scipy.interpolate.interp1d` for 1-dimensional interpolation. +We use either :py:class:`scipy.interpolate.interp1d` or special interpolants from +:py:class:`scipy.interpolate` for 1-dimensional interpolation (see :py:meth:`~xarray.Dataset.interp`). For multi-dimensional interpolation, an attempt is first made to decompose the interpolation in a series of 1-dimensional interpolations, in which case -:py:class:`scipy.interpolate.interp1d` is used. If a decomposition cannot be +the relevant 1-dimensional interpolator is used. If a decomposition cannot be made (e.g. with advanced interpolation), :py:func:`scipy.interpolate.interpn` is used. diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index c6bc082f5ed..494a0fe1508 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2224,19 +2224,44 @@ def interp( coords: Mapping[Any, Any] | None = None, method: InterpOptions = "linear", assume_sorted: bool = False, + reduce: bool = True, kwargs: Mapping[str, Any] | None = None, **coords_kwargs: Any, ) -> Self: - """Interpolate a DataArray onto new coordinates + """ + Interpolate a DataArray onto new coordinates. + + Performs univariate or multivariate interpolation of a Dataset onto new coordinates, + utilizing either NumPy or SciPy interpolation routines. + + When interpolating along multiple dimensions, the process attempts to decompose the + interpolation into independent interpolations along one dimension at a time, unless + `reduce=False` is passed. The specific interpolation method and dimensionality + determine which interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, + :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing `makima` to `method`). - Performs univariate or multivariate interpolation of a DataArray onto - new coordinates using scipy's interpolation routines. If interpolating - along an existing dimension, either :py:class:`scipy.interpolate.interp1d` - or a 1-dimensional scipy interpolator (e.g. :py:class:`scipy.interpolate.KroghInterpolator`) - is called. When interpolating along multiple existing dimensions, an - attempt is made to decompose the interpolation into multiple - 1-dimensional interpolations. If this is possible, the 1-dimensional interpolator is called. - Otherwise, :py:func:`scipy.interpolate.interpn` is called. + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. + + Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. Parameters ---------- @@ -2245,20 +2270,16 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "polynomial"}, default: "linear" - The method used to interpolate. The method should be supported by - the scipy interpolator: - - - ``interp1d``: {"linear", "nearest", "zero", "slinear", - "quadratic", "cubic", "polynomial"} - - ``interpn``: {"linear", "nearest"} - - If ``"polynomial"`` is passed, the ``order`` keyword argument must - also be provided. + method : str + Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of x can be in any order and they are sorted first. If True, x has to be an array of monotonically increasing values. + reduce : bool, default: True + If True, the interpolation is decomposed into independent interpolations along one dimension at a time, + where the interpolation coordinates are independent. Setting this to be True alters the behavior of certain + multi-dimensional interpolants compared to the default SciPy output. kwargs : dict-like or None, default: None Additional keyword arguments passed to scipy's interpolator. Valid options and their behavior depend whether ``interp1d`` or @@ -2274,12 +2295,14 @@ def interp( Notes ----- - scipy is required. + - SciPy is required for certain interpolation methods. + - Allowing `reduce=True` (the default) may alter the behavior of interpolation along multiple dimensions + compared to the default behavior in SciPy. See Also -------- - scipy.interpolate.interp1d - scipy.interpolate.interpn + Dataset.interp + Dataset.reindex_like :doc:`xarray-tutorial:fundamentals/02.2_manipulating_dimensions` Tutorial material on manipulating data resolution using :py:func:`~xarray.DataArray.interp` @@ -2361,6 +2384,7 @@ def interp( method=method, kwargs=kwargs, assume_sorted=assume_sorted, + reduce=reduce, **coords_kwargs, ) return self._from_temp_dataset(ds) @@ -2370,17 +2394,37 @@ def interp_like( other: T_Xarray, method: InterpOptions = "linear", assume_sorted: bool = False, + reduce: bool = True, kwargs: Mapping[str, Any] | None = None, ) -> Self: """Interpolate this object onto the coordinates of another object, filling out of range values with NaN. - If interpolating along a single existing dimension, - :py:class:`scipy.interpolate.interp1d` is called. When interpolating - along multiple existing dimensions, an attempt is made to decompose the - interpolation into multiple 1-dimensional interpolations. If this is - possible, :py:class:`scipy.interpolate.interp1d` is called. Otherwise, - :py:func:`scipy.interpolate.interpn` is called. + When interpolating along multiple dimensions, the process attempts to decompose the + interpolation into independent interpolations along one dimension at a time, unless + `reduce=False` is passed. The specific interpolation method and dimensionality + determine which interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing the `makima` flag). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. Parameters ---------- @@ -2388,23 +2432,19 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "polynomial"}, default: "linear" - The method used to interpolate. The method should be supported by - the scipy interpolator: - - - {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "polynomial"} when ``interp1d`` is called. - - {"linear", "nearest"} when ``interpn`` is called. - - If ``"polynomial"`` is passed, the ``order`` keyword argument must - also be provided. + method : str + Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. + reduce : bool, default: True + If True, the interpolation is decomposed into independent interpolations along one dimension at a time, + where the interpolation coordinates are independent. Setting this to be True alters the behavior of certain + multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional - Additional keyword passed to scipy's interpolator. + Additional keyword arguments passed to the interpolant. Returns ------- @@ -2412,6 +2452,17 @@ def interp_like( Another dataarray by interpolating this dataarray's data along the coordinates of the other object. + Notes + ----- + scipy is required. + If the dataarray has object-type coordinates, reindex is used for these + coordinates instead of the interpolation. + + See Also + -------- + DataArray.interp + DataArray.reindex_like + Examples -------- >>> data = np.arange(12).reshape(4, 3) @@ -2467,24 +2518,18 @@ def interp_like( Coordinates: * x (x) int64 32B 10 20 30 40 * y (y) int64 24B 70 80 90 - - Notes - ----- - scipy is required. - If the dataarray has object-type coordinates, reindex is used for these - coordinates instead of the interpolation. - - See Also - -------- - DataArray.interp - DataArray.reindex_like """ + if self.dtype.kind not in "uifc": raise TypeError( f"interp only works for a numeric type array. Given {self.dtype}." ) ds = self._to_temp_dataset().interp_like( - other, method=method, kwargs=kwargs, assume_sorted=assume_sorted + other, + method=method, + kwargs=kwargs, + assume_sorted=assume_sorted, + reduce=reduce, ) return self._from_temp_dataset(ds) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index bc9360a809d..c99cb9c2b64 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3904,20 +3904,45 @@ def interp( coords: Mapping[Any, Any] | None = None, method: InterpOptions = "linear", assume_sorted: bool = False, + reduce: bool = True, kwargs: Mapping[str, Any] | None = None, method_non_numeric: str = "nearest", **coords_kwargs: Any, ) -> Self: - """Interpolate a Dataset onto new coordinates + """ + Interpolate a Dataset onto new coordinates. + + Performs univariate or multivariate interpolation of a Dataset onto new coordinates, + utilizing either NumPy or SciPy interpolation routines. + + When interpolating along multiple dimensions, the process attempts to decompose the + interpolation into independent interpolations along one dimension at a time, unless + `reduce=False` is passed. The specific interpolation method and dimensionality + determine which interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, + :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing `makima` to `method`). - Performs univariate or multivariate interpolation of a Dataset onto - new coordinates using scipy's interpolation routines. If interpolating - along an existing dimension, either :py:class:`scipy.interpolate.interp1d` - or a 1-dimensional scipy interpolator (e.g. :py:class:`scipy.interpolate.KroghInterpolator`) - is called. When interpolating along multiple existing dimensions, an - attempt is made to decompose the interpolation into multiple - 1-dimensional interpolations. If this is possible, the 1-dimensional interpolator - is called. Otherwise, :py:func:`scipy.interpolate.interpn` is called. + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. + + Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. Parameters ---------- @@ -3926,28 +3951,20 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "polynomial", \ - "barycentric", "krogh", "pchip", "spline", "akima"}, default: "linear" - String indicating which method to use for interpolation: - - - 'linear': linear interpolation. Additional keyword - arguments are passed to :py:func:`numpy.interp` - - 'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'polynomial': - are passed to :py:func:`scipy.interpolate.interp1d`. If - ``method='polynomial'``, the ``order`` keyword argument must also be - provided. - - 'barycentric', 'krogh', 'pchip', 'spline', 'akima': use their - respective :py:class:`scipy.interpolate` classes. - + method : str + Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. + reduce : bool, default: True + If True, the interpolation is decomposed into independent interpolations of minimal dimensionality such that + the interpolation coordinates are independent. Setting this to be True alters the behavior of certain + multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional - Additional keyword arguments passed to scipy's interpolator. Valid - options and their behavior depend whether ``interp1d`` or - ``interpn`` is used. + Additional keyword arguments passed to the interpolator. Valid + options and their behavior depend which interpolant is used. method_non_numeric : {"nearest", "pad", "ffill", "backfill", "bfill"}, optional Method for non-numeric types. Passed on to :py:meth:`Dataset.reindex`. ``"nearest"`` is used by default. @@ -3955,6 +3972,7 @@ def interp( The keyword arguments form of ``coords``. One of coords or coords_kwargs must be provided. + Returns ------- interpolated : Dataset @@ -3962,7 +3980,9 @@ def interp( Notes ----- - scipy is required. + - SciPy is required for certain interpolation methods. + - Allowing `reduce=True` (the default) may alter the behavior of interpolation along multiple dimensions + compared to the default behavior in SciPy. See Also -------- @@ -4125,7 +4145,9 @@ def _validate_interp_indexer(x, new_x): if dtype_kind in "uifc": # For normal number types do the interpolation: var_indexers = {k: v for k, v in use_indexers.items() if k in var.dims} - variables[name] = missing.interp(var, var_indexers, method, **kwargs) + variables[name] = missing.interp( + var, var_indexers, method, reduce=reduce, **kwargs + ) elif dtype_kind in "ObU" and (use_indexers.keys() & var.dims): # For types that we do not understand do stepwise # interpolation to avoid modifying the elements. @@ -4186,18 +4208,43 @@ def interp_like( other: T_Xarray, method: InterpOptions = "linear", assume_sorted: bool = False, + reduce: bool = True, kwargs: Mapping[str, Any] | None = None, method_non_numeric: str = "nearest", ) -> Self: - """Interpolate this object onto the coordinates of another object, - filling the out of range values with NaN. + """Interpolate this object onto the coordinates of another object. + + Performs univariate or multivariate interpolation of a Dataset onto new coordinates, + utilizing either NumPy or SciPy interpolation routines. + + When interpolating along multiple dimensions, the process attempts to decompose the + interpolation into independent interpolations along one dimension at a time, unless + `reduce=False` is passed. The specific interpolation method and dimensionality + determine which interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, + :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. - If interpolating along a single existing dimension, - :py:class:`scipy.interpolate.interp1d` is called. When interpolating - along multiple existing dimensions, an attempt is made to decompose the - interpolation into multiple 1-dimensional interpolations. If this is - possible, :py:class:`scipy.interpolate.interp1d` is called. Otherwise, - :py:func:`scipy.interpolate.interpn` is called. + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing `makima` to `method`). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. + + Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. Parameters ---------- @@ -4205,26 +4252,20 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "polynomial", \ - "barycentric", "krogh", "pchip", "spline", "akima"}, default: "linear" - String indicating which method to use for interpolation: - - - 'linear': linear interpolation. Additional keyword - arguments are passed to :py:func:`numpy.interp` - - 'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'polynomial': - are passed to :py:func:`scipy.interpolate.interp1d`. If - ``method='polynomial'``, the ``order`` keyword argument must also be - provided. - - 'barycentric', 'krogh', 'pchip', 'spline', 'akima': use their - respective :py:class:`scipy.interpolate` classes. - + method : str + Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. + reduce : bool, default: True + If True, the interpolation is decomposed into independent 1-dimensional interpolations such that + the interpolation coordinates are independent. Setting this to be True alters the behavior of certain + multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional - Additional keyword passed to scipy's interpolator. + Additional keyword arguments passed to the interpolator. Valid + options and their behavior depend which interpolant is use method_non_numeric : {"nearest", "pad", "ffill", "backfill", "bfill"}, optional Method for non-numeric types. Passed on to :py:meth:`Dataset.reindex`. ``"nearest"`` is used by default. diff --git a/xarray/core/missing.py b/xarray/core/missing.py index 4523e4f8232..83fcee47a3b 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -20,7 +20,7 @@ timedelta_to_numeric, ) from xarray.core.options import _get_keep_attrs -from xarray.core.types import Interp1dOptions, InterpOptions +from xarray.core.types import Interp1dOptions, InterpnOptions, InterpOptions from xarray.core.utils import OrderedSet, is_scalar from xarray.core.variable import Variable, broadcast_variables from xarray.namedarray.parallelcompat import get_chunked_array_type @@ -154,6 +154,9 @@ def __init__( raise ValueError("order is required when method=polynomial") method = order + if method == "quintic": + method = 5 + self.method = method self.cons_kwargs = kwargs @@ -503,6 +506,7 @@ def _get_interpolator( interp_class = _import_interpolant("KroghInterpolator", method) elif method == "pchip": kwargs.update(axis=-1) + kwargs.setdefault("extrapolate", False) interp_class = _import_interpolant("PchipInterpolator", method) elif method == "spline": utils.emit_user_level_warning( @@ -520,9 +524,6 @@ def _get_interpolator( elif method == "makima": kwargs.update(method="makima", axis=-1) interp_class = _import_interpolant("Akima1DInterpolator", method) - elif method == "makima": - kwargs.update(method="makima", axis=-1) - interp_class = _import_interpolant("Akima1DInterpolator", method) else: raise ValueError(f"{method} is not a valid scipy interpolator") else: @@ -536,8 +537,7 @@ def _get_interpolator_nd(method, **kwargs): returns interpolator class and keyword arguments for the class """ - valid_methods = ["linear", "nearest"] - + valid_methods = tuple(get_args(InterpnOptions)) if method in valid_methods: kwargs.update(method=method) kwargs.setdefault("bounds_error", False) @@ -601,7 +601,7 @@ def _floatize_x(x, new_x): return x, new_x -def interp(var, indexes_coords, method: InterpOptions, **kwargs): +def interp(var, indexes_coords, method: InterpOptions, reduce: bool = True, **kwargs): """Make an interpolation of Variable Parameters @@ -615,6 +615,10 @@ def interp(var, indexes_coords, method: InterpOptions, **kwargs): One of {'linear', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic'}. For multidimensional interpolation, only {'linear', 'nearest'} can be used. + reduce: + Decompose the interpolation along independent interpolation dimensions when + possible. This will likely improve performance over true multi-dimensional + interpolation but will alter the result for certain interpolators. **kwargs keyword arguments to be passed to scipy.interpolate @@ -631,8 +635,15 @@ def interp(var, indexes_coords, method: InterpOptions, **kwargs): return var.copy() result = var - # decompose the interpolation into a succession of independent interpolation - for indep_indexes_coords in decompose_interp(indexes_coords): + + if reduce: + # decompose the interpolation into a succession of independent interpolation. This may + # affect the mathematical behavior of certain nd interpolants. + indexes_coords = decompose_interp(indexes_coords) + else: + indexes_coords = [indexes_coords] + + for indep_indexes_coords in indexes_coords: var = result # target dimensions diff --git a/xarray/core/types.py b/xarray/core/types.py index 2e7572a3858..3937e4d3631 100644 --- a/xarray/core/types.py +++ b/xarray/core/types.py @@ -229,12 +229,20 @@ def copy( JoinOptions = Literal["outer", "inner", "left", "right", "exact", "override"] Interp1dOptions = Literal[ - "linear", "nearest", "zero", "slinear", "quadratic", "cubic", "polynomial" + "linear", + "nearest", + "zero", + "slinear", + "quadratic", + "cubic", + "quintic", + "polynomial", ] InterpolantOptions = Literal[ "barycentric", "krogh", "pchip", "spline", "akima", "makima" ] -InterpOptions = Union[Interp1dOptions, InterpolantOptions] +InterpnOptions = Literal["linear", "nearest", "slinear", "cubic", "quintic", "pchip"] +InterpOptions = Union[Interp1dOptions, InterpolantOptions, InterpnOptions] DatetimeUnitOptions = Literal[ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "μs", "ns", "ps", "fs", "as", None diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 6722e8d9404..416d2320a29 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -1,6 +1,6 @@ from __future__ import annotations -from itertools import combinations, permutations +from itertools import combinations, permutations, product from typing import cast import numpy as np @@ -9,7 +9,7 @@ import xarray as xr from xarray.coding.cftimeindex import _parse_array_of_cftime_strings -from xarray.core.types import InterpOptions +from xarray.core.types import InterpnOptions, InterpOptions from xarray.tests import ( assert_allclose, assert_equal, @@ -62,6 +62,43 @@ def get_example_data(case: int) -> xr.DataArray: raise ValueError("case must be 1-4") +@pytest.fixture +def nd_interp_coords(): + # interpolation indices for nd interpolation of da from case 3 of get_example_data + + da = get_example_data(case=3) + + coords = {} + # grid -> grid + coords["xdestnp"] = np.linspace(0.1, 1.0, 11) + coords["ydestnp"] = np.linspace(0.0, 0.2, 10) + coords["zdestnp"] = da.get_index("z") # type: ignore[assignment] + # list of the points defined by the above mesh in C order + mesh_x, mesh_y, mesh_z = np.meshgrid( + coords["xdestnp"], coords["ydestnp"], coords["zdestnp"], indexing="ij" + ) + coords["grid_grid_points"] = np.column_stack( + [mesh_x.ravel(), mesh_y.ravel(), mesh_z.ravel()] + ) + + # grid -> oned + coords["xdest"] = xr.DataArray(np.linspace(0.1, 1.0, 11), dims="y") # type: ignore[assignment] + coords["ydest"] = xr.DataArray(np.linspace(0.0, 0.2, 11), dims="y") # type: ignore[assignment] + coords["zdest"] = da.z + # grid of the points defined by the oned gridded with zdest in C order + coords["grid_oned_points"] = np.array( + [ + (a, b, c) + for (a, b), c in product( + zip(coords["xdest"].data, coords["ydest"].data, strict=False), + coords["zdest"].data, + ) + ] + ) + + return coords + + def test_keywargs(): if not has_scipy: pytest.skip("scipy is not installed.") @@ -245,38 +282,39 @@ def func(obj, dim, new_x, method): assert_allclose(actual, expected.transpose("z", "w", "y", transpose_coords=True)) +@requires_scipy +@pytest.mark.parametrize("method", ("linear", "nearest", "slinear", "pchip")) @pytest.mark.parametrize( "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] ) -def test_interpolate_nd(case: int) -> None: - if not has_scipy: - pytest.skip("scipy is not installed.") - +def test_interpolate_nd_separable( + case: int, method: InterpnOptions, nd_interp_coords +) -> None: if not has_dask and case == 4: pytest.skip("dask is not installed in the environment.") da = get_example_data(case) # grid -> grid - xdestnp = np.linspace(0.1, 1.0, 11) - ydestnp = np.linspace(0.0, 0.2, 10) - actual = da.interp(x=xdestnp, y=ydestnp, method="linear") + xdestnp = nd_interp_coords["xdestnp"] + ydestnp = nd_interp_coords["ydestnp"] + actual = da.interp(x=xdestnp, y=ydestnp, method=method) - # linear interpolation is separateable - expected = da.interp(x=xdestnp, method="linear") - expected = expected.interp(y=ydestnp, method="linear") + # `method` is separable + expected = da.interp(x=xdestnp, method=method) + expected = expected.interp(y=ydestnp, method=method) assert_allclose(actual.transpose("x", "y", "z"), expected.transpose("x", "y", "z")) # grid -> 1d-sample - xdest = xr.DataArray(np.linspace(0.1, 1.0, 11), dims="y") - ydest = xr.DataArray(np.linspace(0.0, 0.2, 11), dims="y") - actual = da.interp(x=xdest, y=ydest, method="linear") + xdest = nd_interp_coords["xdest"] + ydest = nd_interp_coords["ydest"] + actual = da.interp(x=xdest, y=ydest, method=method) - # linear interpolation is separateable + # `method` is separable expected_data = scipy.interpolate.RegularGridInterpolator( (da["x"], da["y"]), da.transpose("x", "y", "z").values, - method="linear", + method=method, bounds_error=False, fill_value=np.nan, )(np.stack([xdest, ydest], axis=-1)) @@ -287,18 +325,90 @@ def test_interpolate_nd(case: int) -> None: "z": da["z"], "y": ydest, "x": ("y", xdest.values), - "x2": da["x2"].interp(x=xdest), + "x2": da["x2"].interp(x=xdest, method=method), }, ) assert_allclose(actual.transpose("y", "z"), expected) # reversed order - actual = da.interp(y=ydest, x=xdest, method="linear") + actual = da.interp(y=ydest, x=xdest, method=method) assert_allclose(actual.transpose("y", "z"), expected) @requires_scipy -def test_interpolate_nd_nd() -> None: +@pytest.mark.parametrize("method", ("cubic", "quintic")) +@pytest.mark.parametrize( + "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] +) +def test_interpolate_nd_inseparable( + case: int, method: InterpnOptions, nd_interp_coords +) -> None: + if not has_dask and case == 4: + pytest.skip("dask is not installed in the environment.") + + da = get_example_data(case) + + # grid -> grid + xdestnp = nd_interp_coords["xdestnp"] + ydestnp = nd_interp_coords["ydestnp"] + zdestnp = nd_interp_coords["zdestnp"] + grid_grid_points = nd_interp_coords["grid_grid_points"] + # the presence/absence of z cordinate may affect nd interpolants, even when the + # coordinate is unchanged + actual = da.interp(x=xdestnp, y=ydestnp, z=zdestnp, method=method, reduce=False) + expected_data = scipy.interpolate.interpn( + points=(da.x, da.y, da.z), + values=da.data, + xi=grid_grid_points, + method=method, + bounds_error=False, + ).reshape((len(xdestnp), len(ydestnp), len(zdestnp))) + expected = xr.DataArray( + expected_data, + dims=["x", "y", "z"], + coords={ + "x": xdestnp, + "y": ydestnp, + "z": zdestnp, + "x2": da["x2"].interp(x=xdestnp, method=method), + }, + ) + assert_allclose(actual.transpose("x", "y", "z"), expected.transpose("x", "y", "z")) + + # grid -> 1d-sample + xdest = nd_interp_coords["xdest"] + ydest = nd_interp_coords["ydest"] + zdest = nd_interp_coords["zdest"] + grid_oned_points = nd_interp_coords["grid_oned_points"] + actual = da.interp(x=xdest, y=ydest, z=zdest, method=method, reduce=False) + expected_data = scipy.interpolate.interpn( + points=(da.x, da.y, da.z), + values=da.data, + xi=grid_oned_points, + method=method, + bounds_error=False, + ).reshape([len(xdest), len(zdest)]) + expected = xr.DataArray( + expected_data, + dims=["y", "z"], + coords={ + "y": ydest, + "z": zdest, + "x": ("y", xdest.values), + "x2": da["x2"].interp(x=xdest, method=method), + }, + ) + + assert_allclose(actual.transpose("y", "z"), expected) + + # reversed order + actual = da.interp(y=ydest, x=xdest, z=zdest, method=method, reduce=False) + assert_allclose(actual.transpose("y", "z"), expected) + + +@requires_scipy +@pytest.mark.parametrize("method", ("linear", "nearest", "quintic")) +def test_interpolate_nd_nd(method: InterpnOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" # Create original array a = [0, 2] @@ -353,14 +463,12 @@ def test_interpolate_nd_with_nan() -> None: xr.testing.assert_allclose(out2.drop_vars("a"), expected_ds) +@requires_scipy @pytest.mark.parametrize("method", ["linear"]) @pytest.mark.parametrize( "case", [pytest.param(0, id="no_chunk"), pytest.param(1, id="chunk_y")] ) def test_interpolate_scalar(method: InterpOptions, case: int) -> None: - if not has_scipy: - pytest.skip("scipy is not installed.") - if not has_dask and case in [1]: pytest.skip("dask is not installed in the environment.") @@ -384,33 +492,37 @@ def func(obj, new_x): assert_allclose(actual, expected) -@pytest.mark.parametrize("method", ["linear"]) +@requires_scipy +@pytest.mark.parametrize("method", ["linear", "quintic"]) @pytest.mark.parametrize( "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] ) def test_interpolate_nd_scalar(method: InterpOptions, case: int) -> None: - if not has_scipy: - pytest.skip("scipy is not installed.") - if not has_dask and case in [4]: pytest.skip("dask is not installed in the environment.") da = get_example_data(case) xdest = 0.4 ydest = 0.05 + zdest = da.get_index("z") - actual = da.interp(x=xdest, y=ydest, method=method) + actual = da.interp(x=xdest, y=ydest, z=zdest, method=method) # scipy interpolation for the reference expected_data = scipy.interpolate.RegularGridInterpolator( - (da["x"], da["y"]), + (da["x"], da["y"], da["z"]), da.transpose("x", "y", "z").values, - method="linear", + method=method, bounds_error=False, fill_value=np.nan, - )(np.stack([xdest, ydest], axis=-1)) - - coords = {"x": xdest, "y": ydest, "x2": da["x2"].interp(x=xdest), "z": da["z"]} - expected = xr.DataArray(expected_data[0], dims=["z"], coords=coords) + )(np.asarray([(xdest, ydest, z_val) for z_val in zdest])) + + coords = { + "x": xdest, + "y": ydest, + "x2": da["x2"].interp(x=xdest, method=method), + "z": da["z"], + } + expected = xr.DataArray(expected_data, dims=["z"], coords=coords) assert_allclose(actual, expected) @@ -840,6 +952,7 @@ def test_interpolate_chunk_1d( * np.exp(z), coords=[("x", x), ("y", y), ("z", z)], ) + kwargs = {"fill_value": "extrapolate"} # choose the data dimensions @@ -888,7 +1001,7 @@ def test_interpolate_chunk_1d( @requires_scipy @requires_dask -@pytest.mark.parametrize("method", ["linear", "nearest"]) +@pytest.mark.parametrize("method", ["linear", "nearest", "cubic"]) @pytest.mark.filterwarnings("ignore:Increasing number of chunks") def test_interpolate_chunk_advanced(method: InterpOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" From 01599592cef9a06c057f05279e695ad83899a6c1 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 11:05:52 -0700 Subject: [PATCH 02/23] remove reduce arg --- xarray/core/dataarray.py | 45 ++++++++++++--------------------------- xarray/core/dataset.py | 46 ++++++++++++---------------------------- xarray/core/missing.py | 12 ++++------- 3 files changed, 32 insertions(+), 71 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 494a0fe1508..852e677b645 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2224,7 +2224,6 @@ def interp( coords: Mapping[Any, Any] | None = None, method: InterpOptions = "linear", assume_sorted: bool = False, - reduce: bool = True, kwargs: Mapping[str, Any] | None = None, **coords_kwargs: Any, ) -> Self: @@ -2232,12 +2231,8 @@ def interp( Interpolate a DataArray onto new coordinates. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. - - When interpolating along multiple dimensions, the process attempts to decompose the - interpolation into independent interpolations along one dimension at a time, unless - `reduce=False` is passed. The specific interpolation method and dimensionality - determine which interpolant is used: + utilizing either NumPy or SciPy interpolation routines. The specific interpolation + method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. @@ -2276,10 +2271,6 @@ def interp( If False, values of x can be in any order and they are sorted first. If True, x has to be an array of monotonically increasing values. - reduce : bool, default: True - If True, the interpolation is decomposed into independent interpolations along one dimension at a time, - where the interpolation coordinates are independent. Setting this to be True alters the behavior of certain - multi-dimensional interpolants compared to the default SciPy output. kwargs : dict-like or None, default: None Additional keyword arguments passed to scipy's interpolator. Valid options and their behavior depend whether ``interp1d`` or @@ -2296,8 +2287,9 @@ def interp( Notes ----- - SciPy is required for certain interpolation methods. - - Allowing `reduce=True` (the default) may alter the behavior of interpolation along multiple dimensions - compared to the default behavior in SciPy. + - When interpolating along multiple dimensions with methods `linear` and `nearest`, + the process attempts to decompose the interpolation into independent interpolations + along one dimension at a time. See Also -------- @@ -2384,7 +2376,6 @@ def interp( method=method, kwargs=kwargs, assume_sorted=assume_sorted, - reduce=reduce, **coords_kwargs, ) return self._from_temp_dataset(ds) @@ -2394,16 +2385,13 @@ def interp_like( other: T_Xarray, method: InterpOptions = "linear", assume_sorted: bool = False, - reduce: bool = True, kwargs: Mapping[str, Any] | None = None, ) -> Self: """Interpolate this object onto the coordinates of another object, filling out of range values with NaN. - When interpolating along multiple dimensions, the process attempts to decompose the - interpolation into independent interpolations along one dimension at a time, unless - `reduce=False` is passed. The specific interpolation method and dimensionality - determine which interpolant is used: + The specific interpolation method and dimensionality determine which + interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. @@ -2439,10 +2427,6 @@ def interp_like( in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. - reduce : bool, default: True - If True, the interpolation is decomposed into independent interpolations along one dimension at a time, - where the interpolation coordinates are independent. Setting this to be True alters the behavior of certain - multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional Additional keyword arguments passed to the interpolant. @@ -2454,9 +2438,12 @@ def interp_like( Notes ----- - scipy is required. - If the dataarray has object-type coordinates, reindex is used for these - coordinates instead of the interpolation. + - scipy is required. + - If the dataarray has object-type coordinates, reindex is used for these + coordinates instead of the interpolation. + - When interpolating along multiple dimensions with methods `linear` and `nearest`, + the process attempts to decompose the interpolation into independent interpolations + along one dimension at a time. See Also -------- @@ -2525,11 +2512,7 @@ def interp_like( f"interp only works for a numeric type array. Given {self.dtype}." ) ds = self._to_temp_dataset().interp_like( - other, - method=method, - kwargs=kwargs, - assume_sorted=assume_sorted, - reduce=reduce, + other, method=method, kwargs=kwargs, assume_sorted=assume_sorted ) return self._from_temp_dataset(ds) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index c99cb9c2b64..65efa7e46a6 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3904,7 +3904,6 @@ def interp( coords: Mapping[Any, Any] | None = None, method: InterpOptions = "linear", assume_sorted: bool = False, - reduce: bool = True, kwargs: Mapping[str, Any] | None = None, method_non_numeric: str = "nearest", **coords_kwargs: Any, @@ -3913,12 +3912,8 @@ def interp( Interpolate a Dataset onto new coordinates. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. - - When interpolating along multiple dimensions, the process attempts to decompose the - interpolation into independent interpolations along one dimension at a time, unless - `reduce=False` is passed. The specific interpolation method and dimensionality - determine which interpolant is used: + utilizing either NumPy or SciPy interpolation routines. The specific interpolation + method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. @@ -3958,10 +3953,6 @@ def interp( in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. - reduce : bool, default: True - If True, the interpolation is decomposed into independent interpolations of minimal dimensionality such that - the interpolation coordinates are independent. Setting this to be True alters the behavior of certain - multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional Additional keyword arguments passed to the interpolator. Valid options and their behavior depend which interpolant is used. @@ -3981,10 +3972,9 @@ def interp( Notes ----- - SciPy is required for certain interpolation methods. - - Allowing `reduce=True` (the default) may alter the behavior of interpolation along multiple dimensions - compared to the default behavior in SciPy. - - See Also + - When interpolating along multiple dimensions with methods `linear` and `nearest`, + the process attempts to decompose the interpolation into independent interpolations + along one dimension at a time. -------- scipy.interpolate.interp1d scipy.interpolate.interpn @@ -4145,9 +4135,7 @@ def _validate_interp_indexer(x, new_x): if dtype_kind in "uifc": # For normal number types do the interpolation: var_indexers = {k: v for k, v in use_indexers.items() if k in var.dims} - variables[name] = missing.interp( - var, var_indexers, method, reduce=reduce, **kwargs - ) + variables[name] = missing.interp(var, var_indexers, method, **kwargs) elif dtype_kind in "ObU" and (use_indexers.keys() & var.dims): # For types that we do not understand do stepwise # interpolation to avoid modifying the elements. @@ -4208,19 +4196,14 @@ def interp_like( other: T_Xarray, method: InterpOptions = "linear", assume_sorted: bool = False, - reduce: bool = True, kwargs: Mapping[str, Any] | None = None, method_non_numeric: str = "nearest", ) -> Self: """Interpolate this object onto the coordinates of another object. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. - - When interpolating along multiple dimensions, the process attempts to decompose the - interpolation into independent interpolations along one dimension at a time, unless - `reduce=False` is passed. The specific interpolation method and dimensionality - determine which interpolant is used: + utilizing either NumPy or SciPy interpolation routines. The specific interpolation + method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. @@ -4259,10 +4242,6 @@ def interp_like( in any order and they are sorted first. If True, interpolated coordinates are assumed to be an array of monotonically increasing values. - reduce : bool, default: True - If True, the interpolation is decomposed into independent 1-dimensional interpolations such that - the interpolation coordinates are independent. Setting this to be True alters the behavior of certain - multi-dimensional interpolants compared to the default SciPy output. kwargs : dict, optional Additional keyword arguments passed to the interpolator. Valid options and their behavior depend which interpolant is use @@ -4278,9 +4257,12 @@ def interp_like( Notes ----- - scipy is required. - If the dataset has object-type coordinates, reindex is used for these - coordinates instead of the interpolation. + - scipy is required. + - If the dataset has object-type coordinates, reindex is used for these + coordinates instead of the interpolation. + - When interpolating along multiple dimensions with methods `linear` and `nearest`, + the process attempts to decompose the interpolation into independent interpolations + along one dimension at a time. See Also -------- diff --git a/xarray/core/missing.py b/xarray/core/missing.py index 83fcee47a3b..593720352f6 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -506,6 +506,7 @@ def _get_interpolator( interp_class = _import_interpolant("KroghInterpolator", method) elif method == "pchip": kwargs.update(axis=-1) + # pchip default behavior is to extrapolate kwargs.setdefault("extrapolate", False) interp_class = _import_interpolant("PchipInterpolator", method) elif method == "spline": @@ -601,7 +602,7 @@ def _floatize_x(x, new_x): return x, new_x -def interp(var, indexes_coords, method: InterpOptions, reduce: bool = True, **kwargs): +def interp(var, indexes_coords, method: InterpOptions, **kwargs): """Make an interpolation of Variable Parameters @@ -615,10 +616,6 @@ def interp(var, indexes_coords, method: InterpOptions, reduce: bool = True, **kw One of {'linear', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic'}. For multidimensional interpolation, only {'linear', 'nearest'} can be used. - reduce: - Decompose the interpolation along independent interpolation dimensions when - possible. This will likely improve performance over true multi-dimensional - interpolation but will alter the result for certain interpolators. **kwargs keyword arguments to be passed to scipy.interpolate @@ -636,9 +633,8 @@ def interp(var, indexes_coords, method: InterpOptions, reduce: bool = True, **kw result = var - if reduce: - # decompose the interpolation into a succession of independent interpolation. This may - # affect the mathematical behavior of certain nd interpolants. + if method in ["linear", "nearest"]: + # decompose the interpolation into a succession of independent interpolation. indexes_coords = decompose_interp(indexes_coords) else: indexes_coords = [indexes_coords] From 68e4b66e2226d0d4fe102d6ed091eb8420cc7bb2 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 11:10:43 -0700 Subject: [PATCH 03/23] docstring formatting --- xarray/core/dataarray.py | 10 +++++----- xarray/core/dataset.py | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 852e677b645..4440a773b4d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2235,14 +2235,14 @@ def interp( method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` (as in the case of `method='linear'` for 1D data). - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. + :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: @@ -2394,11 +2394,11 @@ def interp_like( interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` (as in the case of `method='linear'` for 1D data). - If `method='polynomial'`, the `order` keyword argument must also be provided. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 65efa7e46a6..c0a07488c73 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3916,14 +3916,14 @@ def interp( method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` (as in the case of `method='linear'` for 1D data). - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. + :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: @@ -4206,14 +4206,14 @@ def interp_like( method and dimensionality determine which interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:class:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:class:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:class:`numpy.interp` + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` (as in the case of `method='linear'` for 1D data). - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:class:`scipy.interpolate.interp1d` is called with `kind=order`. + :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: From 48454e6f7980729471e6da0f41287d84c0e16ce6 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 11:32:41 -0700 Subject: [PATCH 04/23] list of string args for method --- xarray/core/dataarray.py | 6 ++++-- xarray/core/dataset.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 4440a773b4d..042df707701 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2265,7 +2265,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : str + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of x can be in any order and they are sorted @@ -2420,7 +2421,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : str + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index c0a07488c73..a169f9be792 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3946,7 +3946,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : str + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be @@ -4235,7 +4236,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : str + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be From 0cfab3a5f4a7019dad109a9c6d0ffee402ad4dbe Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 11:44:01 -0700 Subject: [PATCH 05/23] moving list of interpolants --- xarray/core/dataarray.py | 95 +++++++++++++++++++-------------------- xarray/core/dataset.py | 97 ++++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 042df707701..be20e324db6 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2231,30 +2231,7 @@ def interp( Interpolate a DataArray onto new coordinates. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. The specific interpolation - method and dimensionality determine which interpolant is used: - - 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. - - 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` - (as in the case of `method='linear'` for 1D data). - - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. - - 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: - - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` - - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` - - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` - - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` - (`makima` is handled by passing `makima` to `method`). - - 4. **Interpolation along multiple dimensions of multi-dimensional data** - - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", - "cubic", "quintic", "pchip"}. + utilizing either NumPy or SciPy interpolation routines. Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. @@ -2291,6 +2268,29 @@ def interp( - When interpolating along multiple dimensions with methods `linear` and `nearest`, the process attempts to decompose the interpolation into independent interpolations along one dimension at a time. + - The specific interpolation method and dimensionality determine which + interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing the `makima` flag). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. See Also -------- @@ -2391,30 +2391,6 @@ def interp_like( """Interpolate this object onto the coordinates of another object, filling out of range values with NaN. - The specific interpolation method and dimensionality determine which - interpolant is used: - - 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. - - 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` - (as in the case of `method='linear'` for 1D data). - - If `method='polynomial'`, the `order` keyword argument must also be provided. - - 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: - - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` - - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` - - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` - - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` - (`makima` is handled by passing the `makima` flag). - - 4. **Interpolation along multiple dimensions of multi-dimensional data** - - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", - "cubic", "quintic", "pchip"}. - Parameters ---------- other : Dataset or DataArray @@ -2446,6 +2422,29 @@ def interp_like( - When interpolating along multiple dimensions with methods `linear` and `nearest`, the process attempts to decompose the interpolation into independent interpolations along one dimension at a time. + - The specific interpolation method and dimensionality determine which + interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing the `makima` flag). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. See Also -------- diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index a169f9be792..f35d9aac1b7 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3912,30 +3912,7 @@ def interp( Interpolate a Dataset onto new coordinates. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. The specific interpolation - method and dimensionality determine which interpolant is used: - - 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. - - 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` - (as in the case of `method='linear'` for 1D data). - - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. - - 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: - - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` - - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` - - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` - - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` - (`makima` is handled by passing `makima` to `method`). - - 4. **Interpolation along multiple dimensions of multi-dimensional data** - - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", - "cubic", "quintic", "pchip"}. + utilizing either NumPy or SciPy interpolation routines. Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. @@ -3976,6 +3953,30 @@ def interp( - When interpolating along multiple dimensions with methods `linear` and `nearest`, the process attempts to decompose the interpolation into independent interpolations along one dimension at a time. + - The specific interpolation method and dimensionality determine which + interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing the `makima` flag). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. + -------- scipy.interpolate.interp1d scipy.interpolate.interpn @@ -4203,30 +4204,7 @@ def interp_like( """Interpolate this object onto the coordinates of another object. Performs univariate or multivariate interpolation of a Dataset onto new coordinates, - utilizing either NumPy or SciPy interpolation routines. The specific interpolation - method and dimensionality determine which interpolant is used: - - 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. - - 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} - use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` - (as in the case of `method='linear'` for 1D data). - - If `method='polynomial'`, the `order` keyword argument must also be provided. In this case, - :py:func:`scipy.interpolate.interp1d` is called with `kind=order`. - - 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** - - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: - - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` - - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` - - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` - - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` - (`makima` is handled by passing `makima` to `method`). - - 4. **Interpolation along multiple dimensions of multi-dimensional data** - - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", - "cubic", "quintic", "pchip"}. + utilizing either NumPy or SciPy interpolation routines. Out-of-range values are filled with NaN, unless specified otherwise via `kwargs` to the numpy/scipy interpolant. @@ -4265,6 +4243,29 @@ def interp_like( - When interpolating along multiple dimensions with methods `linear` and `nearest`, the process attempts to decompose the interpolation into independent interpolations along one dimension at a time. + - The specific interpolation method and dimensionality determine which + interpolant is used: + + 1. **Interpolation along one dimension of 1D data (`method='linear'`)** + - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. + + 2. **Interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Methods {"linear", "nearest", "zero", "slinear", "quadratic", "cubic", "quintic", "polynomial"} + use :py:func:`scipy.interpolate.interp1d`, unless conditions permit the use of :py:func:`numpy.interp` + (as in the case of `method='linear'` for 1D data). + - If `method='polynomial'`, the `order` keyword argument must also be provided. + + 3. **Special interpolants for interpolation along one dimension of N-dimensional data (N ≥ 1)** + - Depending on the `method`, the following interpolants from :py:class:`scipy.interpolate` are used: + - `"pchip"`: :py:class:`scipy.interpolate.PchipInterpolator` + - `"barycentric"`: :py:class:`scipy.interpolate.BarycentricInterpolator` + - `"krogh"`: :py:class:`scipy.interpolate.KroghInterpolator` + - `"akima"` or `"makima"`: :py:class:`scipy.interpolate.Akima1dInterpolator` + (`makima` is handled by passing the `makima` flag). + + 4. **Interpolation along multiple dimensions of multi-dimensional data** + - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", + "cubic", "quintic", "pchip"}. See Also -------- From 1dd173a82de34d8f239a6d2adca157c5d6a8cf17 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 11:58:05 -0700 Subject: [PATCH 06/23] formatting --- xarray/core/dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index f35d9aac1b7..20cf01218e5 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3977,6 +3977,7 @@ def interp( - Uses :py:func:`scipy.interpolate.interpn` for methods {"linear", "nearest", "slinear", "cubic", "quintic", "pchip"}. + See Also -------- scipy.interpolate.interp1d scipy.interpolate.interpn From 8f35e61536dc89424d41cc71cea8d614c3b7186c Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 12:16:31 -0700 Subject: [PATCH 07/23] doc build issues --- xarray/core/dataarray.py | 10 +++++----- xarray/core/dataset.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index be20e324db6..9d62320b7db 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2242,8 +2242,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } + method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of x can be in any order and they are sorted @@ -2397,8 +2397,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } + method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be @@ -2423,7 +2423,7 @@ def interp_like( the process attempts to decompose the interpolation into independent interpolations along one dimension at a time. - The specific interpolation method and dimensionality determine which - interpolant is used: + interpolant is used: 1. **Interpolation along one dimension of 1D data (`method='linear'`)** - Uses :py:func:`numpy.interp`, unless `fill_value='extrapolate'` is provided via `kwargs`. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 20cf01218e5..b277d4efac2 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3923,8 +3923,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } + method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be @@ -4215,8 +4215,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } + method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be From b958bccc189204c51cc5ae76da61bec625890196 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 12:27:34 -0700 Subject: [PATCH 08/23] remove reduce from tests --- xarray/tests/test_interp.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 416d2320a29..aac7f2aa071 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -355,7 +355,7 @@ def test_interpolate_nd_inseparable( grid_grid_points = nd_interp_coords["grid_grid_points"] # the presence/absence of z cordinate may affect nd interpolants, even when the # coordinate is unchanged - actual = da.interp(x=xdestnp, y=ydestnp, z=zdestnp, method=method, reduce=False) + actual = da.interp(x=xdestnp, y=ydestnp, z=zdestnp, method=method) expected_data = scipy.interpolate.interpn( points=(da.x, da.y, da.z), values=da.data, @@ -380,7 +380,7 @@ def test_interpolate_nd_inseparable( ydest = nd_interp_coords["ydest"] zdest = nd_interp_coords["zdest"] grid_oned_points = nd_interp_coords["grid_oned_points"] - actual = da.interp(x=xdest, y=ydest, z=zdest, method=method, reduce=False) + actual = da.interp(x=xdest, y=ydest, z=zdest, method=method) expected_data = scipy.interpolate.interpn( points=(da.x, da.y, da.z), values=da.data, @@ -402,7 +402,7 @@ def test_interpolate_nd_inseparable( assert_allclose(actual.transpose("y", "z"), expected) # reversed order - actual = da.interp(y=ydest, x=xdest, z=zdest, method=method, reduce=False) + actual = da.interp(y=ydest, x=xdest, z=zdest, method=method) assert_allclose(actual.transpose("y", "z"), expected) From f1d3c889e84e7a3c466ca614352ca5ca37dadea7 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Tue, 22 Oct 2024 12:37:50 -0700 Subject: [PATCH 09/23] removing other interpolants from test_interpolate_chunk_1d since cannot be 1d decomposed --- xarray/tests/test_interp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index aac7f2aa071..0c914ef6de4 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -921,9 +921,7 @@ def test_decompose(method: InterpOptions) -> None: @requires_scipy @requires_dask -@pytest.mark.parametrize( - "method", ["linear", "nearest", "zero", "slinear", "quadratic", "cubic"] -) +@pytest.mark.parametrize("method", ["linear", "nearest"]) @pytest.mark.parametrize("chunked", [True, False]) @pytest.mark.parametrize( "data_ndim,interp_ndim,nscalar", From af5e3ff413fc0a45c454874aa1e10f9c71fc4fd3 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Fri, 25 Oct 2024 13:48:47 -0400 Subject: [PATCH 10/23] nd_nd test update --- xarray/tests/test_interp.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 0c914ef6de4..ef636e224d0 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -407,22 +407,27 @@ def test_interpolate_nd_inseparable( @requires_scipy -@pytest.mark.parametrize("method", ("linear", "nearest", "quintic")) +@pytest.mark.parametrize("method", ("linear", "nearest")) def test_interpolate_nd_nd(method: InterpnOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" # Create original array a = [0, 2] x = [0, 1, 2] - da = xr.DataArray( - np.arange(6).reshape(2, 3), dims=("a", "x"), coords={"a": a, "x": x} - ) + values = np.arange(6).reshape(2, 3) + da = xr.DataArray(values, dims=("a", "x"), coords={"a": a, "x": x}) # Create indexer into `a` with dimensions (y, x) y = [10] + a_targets = [1, 2, 2] c = {"x": x, "y": y} - ia = xr.DataArray([[1, 2, 2]], dims=("y", "x"), coords=c) - out = da.interp(a=ia) - expected = xr.DataArray([[1.5, 4, 5]], dims=("y", "x"), coords=c) + ia = xr.DataArray([a_targets], dims=("y", "x"), coords=c) + out = da.interp(a=ia, method=method) + + expected_xi = list(zip(a_targets, x, strict=False)) + expected_vec = scipy.interpolate.interpn( + points=(a, x), values=values, xi=expected_xi, method=method + ) + expected = xr.DataArray([expected_vec], dims=("y", "x"), coords=c) xr.testing.assert_allclose(out.drop_vars("a"), expected) # If the *shared* indexing coordinates do not match, interp should fail. From f91ba721cbbf71bfdfb55a314836e23ae6c79488 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Fri, 25 Oct 2024 13:51:28 -0400 Subject: [PATCH 11/23] formatting --- xarray/core/dataarray.py | 8 ++++---- xarray/core/dataset.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 9d62320b7db..7bf4ed68270 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2242,8 +2242,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", \ + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of x can be in any order and they are sorted @@ -2397,8 +2397,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", \ + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index b277d4efac2..842736ff7d8 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3923,8 +3923,8 @@ def interp( New coordinate can be a scalar, array-like or DataArray. If DataArrays are passed as new coordinates, their dimensions are used for the broadcasting. Missing values are skipped. - method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", \ + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be @@ -4215,8 +4215,8 @@ def interp_like( Object with an 'indexes' attribute giving a mapping from dimension names to an 1d array-like, which provides coordinates upon which to index the variables in this dataset. Missing values are skipped. - method : "linear", "nearest", "zero", "slinear", "quadratic", "cubic", - "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" + method : { "linear", "nearest", "zero", "slinear", "quadratic", "cubic", \ + "quintic", "polynomial", "pchip", "barycentric", "krogh", "akima", "makima" } Interpolation method to use (see descriptions above). assume_sorted : bool, default: False If False, values of coordinates that are interpolated over can be From e67e3124471d38b7cb7c15e6268021255b822a51 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Fri, 25 Oct 2024 14:06:42 -0400 Subject: [PATCH 12/23] scipy ref --- xarray/core/dataarray.py | 2 ++ xarray/core/dataset.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 7bf4ed68270..50fd1964ff2 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2296,6 +2296,7 @@ def interp( -------- Dataset.interp Dataset.reindex_like + scipy.interpolate :doc:`xarray-tutorial:fundamentals/02.2_manipulating_dimensions` Tutorial material on manipulating data resolution using :py:func:`~xarray.DataArray.interp` @@ -2450,6 +2451,7 @@ def interp_like( -------- DataArray.interp DataArray.reindex_like + scipy.interpolate Examples -------- diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 842736ff7d8..213144809de 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3979,8 +3979,7 @@ def interp( See Also -------- - scipy.interpolate.interp1d - scipy.interpolate.interpn + scipy.interpolate :doc:`xarray-tutorial:fundamentals/02.2_manipulating_dimensions` Tutorial material on manipulating data resolution using :py:func:`~xarray.Dataset.interp` @@ -4272,6 +4271,7 @@ def interp_like( -------- Dataset.interp Dataset.reindex_like + scipy.interpolate """ if kwargs is None: kwargs = {} From b98fe1fbe56e60d07d023eaedb1cfdb2ff218f85 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Fri, 25 Oct 2024 14:16:22 -0400 Subject: [PATCH 13/23] docstrings --- xarray/core/dataarray.py | 10 ++++------ xarray/core/dataset.py | 8 ++++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 50fd1964ff2..dcc78154648 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2294,9 +2294,7 @@ def interp( See Also -------- - Dataset.interp - Dataset.reindex_like - scipy.interpolate + :mod:`scipy.interpolate` :doc:`xarray-tutorial:fundamentals/02.2_manipulating_dimensions` Tutorial material on manipulating data resolution using :py:func:`~xarray.DataArray.interp` @@ -2449,9 +2447,9 @@ def interp_like( See Also -------- - DataArray.interp - DataArray.reindex_like - scipy.interpolate + :func:`DataArray.interp` + :func:`DataArray.reindex_like` + :mod:`scipy.interpolate` Examples -------- diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 213144809de..16597c1ff3a 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3979,7 +3979,7 @@ def interp( See Also -------- - scipy.interpolate + :mod:`scipy.interpolate` :doc:`xarray-tutorial:fundamentals/02.2_manipulating_dimensions` Tutorial material on manipulating data resolution using :py:func:`~xarray.Dataset.interp` @@ -4269,9 +4269,9 @@ def interp_like( See Also -------- - Dataset.interp - Dataset.reindex_like - scipy.interpolate + :func:`Dataset.interp` + :func:`Dataset.reindex_like` + :mod:`scipy.interpolate` """ if kwargs is None: kwargs = {} From 5d8b905cad2ae4a4c2350858050872e32e21698d Mon Sep 17 00:00:00 2001 From: Holly Mandel Date: Mon, 28 Oct 2024 09:15:27 -0400 Subject: [PATCH 14/23] Update xarray/tests/test_interp.py Co-authored-by: Deepak Cherian --- xarray/tests/test_interp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index ef636e224d0..3205ec89c11 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -72,7 +72,7 @@ def nd_interp_coords(): # grid -> grid coords["xdestnp"] = np.linspace(0.1, 1.0, 11) coords["ydestnp"] = np.linspace(0.0, 0.2, 10) - coords["zdestnp"] = da.get_index("z") # type: ignore[assignment] + coords["zdestnp"] = da.z.data # list of the points defined by the above mesh in C order mesh_x, mesh_y, mesh_z = np.meshgrid( coords["xdestnp"], coords["ydestnp"], coords["zdestnp"], indexing="ij" From da695ee2664326bac67f00b7ec5f0e41523e1061 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Mon, 28 Oct 2024 09:16:02 -0400 Subject: [PATCH 15/23] test method changes --- xarray/core/missing.py | 2 +- xarray/tests/test_interp.py | 84 +++++++++---------------------------- 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/xarray/core/missing.py b/xarray/core/missing.py index 593720352f6..ece93cf66ef 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -633,7 +633,7 @@ def interp(var, indexes_coords, method: InterpOptions, **kwargs): result = var - if method in ["linear", "nearest"]: + if method in ["linear", "nearest", "slinear"]: # decompose the interpolation into a succession of independent interpolation. indexes_coords = decompose_interp(indexes_coords) else: diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 3205ec89c11..6f21ee47483 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -1,7 +1,7 @@ from __future__ import annotations from itertools import combinations, permutations, product -from typing import cast +from typing import cast, get_args import numpy as np import pandas as pd @@ -9,7 +9,7 @@ import xarray as xr from xarray.coding.cftimeindex import _parse_array_of_cftime_strings -from xarray.core.types import InterpnOptions, InterpOptions +from xarray.core.types import InterpnOptions, InterpOptions, Interp1dOptions, InterpolantOptions from xarray.tests import ( assert_allclose, assert_equal, @@ -28,6 +28,7 @@ except ImportError: pass +ALL_1D = get_args(Interp1dOptions) + get_args(InterpolantOptions) def get_example_data(case: int) -> xr.DataArray: if case == 0: @@ -281,66 +282,14 @@ def func(obj, dim, new_x, method): ) assert_allclose(actual, expected.transpose("z", "w", "y", transpose_coords=True)) - @requires_scipy -@pytest.mark.parametrize("method", ("linear", "nearest", "slinear", "pchip")) @pytest.mark.parametrize( - "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] + "method", get_args(InterpnOptions) ) -def test_interpolate_nd_separable( - case: int, method: InterpnOptions, nd_interp_coords -) -> None: - if not has_dask and case == 4: - pytest.skip("dask is not installed in the environment.") - - da = get_example_data(case) - - # grid -> grid - xdestnp = nd_interp_coords["xdestnp"] - ydestnp = nd_interp_coords["ydestnp"] - actual = da.interp(x=xdestnp, y=ydestnp, method=method) - - # `method` is separable - expected = da.interp(x=xdestnp, method=method) - expected = expected.interp(y=ydestnp, method=method) - assert_allclose(actual.transpose("x", "y", "z"), expected.transpose("x", "y", "z")) - - # grid -> 1d-sample - xdest = nd_interp_coords["xdest"] - ydest = nd_interp_coords["ydest"] - actual = da.interp(x=xdest, y=ydest, method=method) - - # `method` is separable - expected_data = scipy.interpolate.RegularGridInterpolator( - (da["x"], da["y"]), - da.transpose("x", "y", "z").values, - method=method, - bounds_error=False, - fill_value=np.nan, - )(np.stack([xdest, ydest], axis=-1)) - expected = xr.DataArray( - expected_data, - dims=["y", "z"], - coords={ - "z": da["z"], - "y": ydest, - "x": ("y", xdest.values), - "x2": da["x2"].interp(x=xdest, method=method), - }, - ) - assert_allclose(actual.transpose("y", "z"), expected) - - # reversed order - actual = da.interp(y=ydest, x=xdest, method=method) - assert_allclose(actual.transpose("y", "z"), expected) - - -@requires_scipy -@pytest.mark.parametrize("method", ("cubic", "quintic")) @pytest.mark.parametrize( "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] ) -def test_interpolate_nd_inseparable( +def test_interpolate_nd( case: int, method: InterpnOptions, nd_interp_coords ) -> None: if not has_dask and case == 4: @@ -358,7 +307,7 @@ def test_interpolate_nd_inseparable( actual = da.interp(x=xdestnp, y=ydestnp, z=zdestnp, method=method) expected_data = scipy.interpolate.interpn( points=(da.x, da.y, da.z), - values=da.data, + values=da.load().data, xi=grid_grid_points, method=method, bounds_error=False, @@ -407,7 +356,8 @@ def test_interpolate_nd_inseparable( @requires_scipy -@pytest.mark.parametrize("method", ("linear", "nearest")) +# omit cubic, pchip, quintic becausenot enough points +@pytest.mark.parametrize("method", ("linear", "nearest", "slinear")) def test_interpolate_nd_nd(method: InterpnOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" # Create original array @@ -469,7 +419,7 @@ def test_interpolate_nd_with_nan() -> None: @requires_scipy -@pytest.mark.parametrize("method", ["linear"]) +@pytest.mark.parametrize("method", ("linear",)) @pytest.mark.parametrize( "case", [pytest.param(0, id="no_chunk"), pytest.param(1, id="chunk_y")] ) @@ -490,6 +440,7 @@ def func(obj, new_x): axis=obj.get_axis_num("x"), bounds_error=False, fill_value=np.nan, + kind=method )(new_x) coords = {"x": xdest, "y": da["y"], "x2": func(da["x2"], xdest)} @@ -498,7 +449,7 @@ def func(obj, new_x): @requires_scipy -@pytest.mark.parametrize("method", ["linear", "quintic"]) +@pytest.mark.parametrize("method", ("linear",)) @pytest.mark.parametrize( "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] ) @@ -905,7 +856,8 @@ def test_3641() -> None: @requires_scipy -@pytest.mark.parametrize("method", ["nearest", "linear"]) +# cubic, quintic, pchip omitted because not enough points +@pytest.mark.parametrize("method", ("linear","nearest","slinear")) def test_decompose(method: InterpOptions) -> None: da = xr.DataArray( np.arange(6).reshape(3, 2), @@ -926,7 +878,9 @@ def test_decompose(method: InterpOptions) -> None: @requires_scipy @requires_dask -@pytest.mark.parametrize("method", ["linear", "nearest"]) +# omit quintic because not enough points +# unknown timeout using pchip +@pytest.mark.parametrize("method", ("cubic","linear","slinear")) @pytest.mark.parametrize("chunked", [True, False]) @pytest.mark.parametrize( "data_ndim,interp_ndim,nscalar", @@ -955,8 +909,7 @@ def test_interpolate_chunk_1d( * np.exp(z), coords=[("x", x), ("y", y), ("z", z)], ) - - kwargs = {"fill_value": "extrapolate"} + kwargs = {} # choose the data dimensions for data_dims in permutations(da.dims, data_ndim): @@ -1004,7 +957,8 @@ def test_interpolate_chunk_1d( @requires_scipy @requires_dask -@pytest.mark.parametrize("method", ["linear", "nearest", "cubic"]) +# quintic omitted because not enough points +@pytest.mark.parametrize("method", ("linear","nearest","slinear","cubic","pchip")) @pytest.mark.filterwarnings("ignore:Increasing number of chunks") def test_interpolate_chunk_advanced(method: InterpOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" From e6725deb693f21f43e745e67f888b998bdbdd43d Mon Sep 17 00:00:00 2001 From: hollymandel Date: Mon, 28 Oct 2024 09:16:49 -0400 Subject: [PATCH 16/23] formatting --- xarray/tests/test_interp.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 6f21ee47483..fe4b4ceb7c6 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -9,7 +9,12 @@ import xarray as xr from xarray.coding.cftimeindex import _parse_array_of_cftime_strings -from xarray.core.types import InterpnOptions, InterpOptions, Interp1dOptions, InterpolantOptions +from xarray.core.types import ( + Interp1dOptions, + InterpnOptions, + InterpolantOptions, + InterpOptions, +) from xarray.tests import ( assert_allclose, assert_equal, @@ -30,6 +35,7 @@ ALL_1D = get_args(Interp1dOptions) + get_args(InterpolantOptions) + def get_example_data(case: int) -> xr.DataArray: if case == 0: # 2D @@ -282,16 +288,13 @@ def func(obj, dim, new_x, method): ) assert_allclose(actual, expected.transpose("z", "w", "y", transpose_coords=True)) + @requires_scipy -@pytest.mark.parametrize( - "method", get_args(InterpnOptions) -) +@pytest.mark.parametrize("method", get_args(InterpnOptions)) @pytest.mark.parametrize( "case", [pytest.param(3, id="no_chunk"), pytest.param(4, id="chunked")] ) -def test_interpolate_nd( - case: int, method: InterpnOptions, nd_interp_coords -) -> None: +def test_interpolate_nd(case: int, method: InterpnOptions, nd_interp_coords) -> None: if not has_dask and case == 4: pytest.skip("dask is not installed in the environment.") @@ -440,7 +443,7 @@ def func(obj, new_x): axis=obj.get_axis_num("x"), bounds_error=False, fill_value=np.nan, - kind=method + kind=method, )(new_x) coords = {"x": xdest, "y": da["y"], "x2": func(da["x2"], xdest)} @@ -857,7 +860,7 @@ def test_3641() -> None: @requires_scipy # cubic, quintic, pchip omitted because not enough points -@pytest.mark.parametrize("method", ("linear","nearest","slinear")) +@pytest.mark.parametrize("method", ("linear", "nearest", "slinear")) def test_decompose(method: InterpOptions) -> None: da = xr.DataArray( np.arange(6).reshape(3, 2), @@ -880,7 +883,7 @@ def test_decompose(method: InterpOptions) -> None: @requires_dask # omit quintic because not enough points # unknown timeout using pchip -@pytest.mark.parametrize("method", ("cubic","linear","slinear")) +@pytest.mark.parametrize("method", ("cubic", "linear", "slinear")) @pytest.mark.parametrize("chunked", [True, False]) @pytest.mark.parametrize( "data_ndim,interp_ndim,nscalar", @@ -958,7 +961,7 @@ def test_interpolate_chunk_1d( @requires_scipy @requires_dask # quintic omitted because not enough points -@pytest.mark.parametrize("method", ("linear","nearest","slinear","cubic","pchip")) +@pytest.mark.parametrize("method", ("linear", "nearest", "slinear", "cubic", "pchip")) @pytest.mark.filterwarnings("ignore:Increasing number of chunks") def test_interpolate_chunk_advanced(method: InterpOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" From 30d42c60302e730c8b890e8d6729a1862b60d133 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 29 Oct 2024 15:56:05 -0700 Subject: [PATCH 17/23] fix typing --- xarray/tests/test_interp.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index fe4b4ceb7c6..75dc1577477 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -912,7 +912,6 @@ def test_interpolate_chunk_1d( * np.exp(z), coords=[("x", x), ("y", y), ("z", z)], ) - kwargs = {} # choose the data dimensions for data_dims in permutations(da.dims, data_ndim): @@ -947,8 +946,8 @@ def test_interpolate_chunk_1d( if chunked: dest[dim] = xr.DataArray(data=dest[dim], dims=[dim]) dest[dim] = dest[dim].chunk(2) - actual = da.interp(method=method, **dest, kwargs=kwargs) - expected = da.compute().interp(method=method, **dest, kwargs=kwargs) + actual = da.interp(method=method, **dest) + expected = da.compute().interp(method=method, **dest) assert_identical(actual, expected) From 18bf91c43e9b23afabd840048162a77a272f0fee Mon Sep 17 00:00:00 2001 From: hollymandel Date: Sun, 3 Nov 2024 11:46:18 -0800 Subject: [PATCH 18/23] removing cubic from chunk 1d test because of timeout --- xarray/tests/test_interp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 75dc1577477..7af37e21e0e 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -882,8 +882,8 @@ def test_decompose(method: InterpOptions) -> None: @requires_scipy @requires_dask # omit quintic because not enough points -# unknown timeout using pchip -@pytest.mark.parametrize("method", ("cubic", "linear", "slinear")) +# cubic and pchip omitted because they take too long +@pytest.mark.parametrize("method", ("linear", "slinear")) @pytest.mark.parametrize("chunked", [True, False]) @pytest.mark.parametrize( "data_ndim,interp_ndim,nscalar", From 9bbe3a02eb1a27fe01f1063e63de184a5e00ea13 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Sun, 3 Nov 2024 11:53:14 -0800 Subject: [PATCH 19/23] rerun tests From 7dc18c1bfd2078c8c4afd9a634f0335db6a2ea35 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Sun, 3 Nov 2024 13:05:33 -0800 Subject: [PATCH 20/23] try again From f6d9ad0fef1ee262a002f6481a762063d3eba766 Mon Sep 17 00:00:00 2001 From: hollymandel Date: Sun, 3 Nov 2024 19:46:58 -0800 Subject: [PATCH 21/23] formatting --- xarray/tests/test_interp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 7af37e21e0e..3005ecad91d 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -359,7 +359,7 @@ def test_interpolate_nd(case: int, method: InterpnOptions, nd_interp_coords) -> @requires_scipy -# omit cubic, pchip, quintic becausenot enough points +# omit cubic, pchip, quintic because not enough points @pytest.mark.parametrize("method", ("linear", "nearest", "slinear")) def test_interpolate_nd_nd(method: InterpnOptions) -> None: """Interpolate nd array with an nd indexer sharing coordinates.""" @@ -881,7 +881,7 @@ def test_decompose(method: InterpOptions) -> None: @requires_scipy @requires_dask -# omit quintic because not enough points +# quintic omitted because not enough points # cubic and pchip omitted because they take too long @pytest.mark.parametrize("method", ("linear", "slinear")) @pytest.mark.parametrize("chunked", [True, False]) From 928f46db2a2beb10dffefb3b1f23f736675ffba2 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Wed, 6 Nov 2024 11:06:44 -0700 Subject: [PATCH 22/23] Skip fewer tests --- xarray/tests/test_interp.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index 3005ecad91d..de5c4daca02 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -881,9 +881,7 @@ def test_decompose(method: InterpOptions) -> None: @requires_scipy @requires_dask -# quintic omitted because not enough points -# cubic and pchip omitted because they take too long -@pytest.mark.parametrize("method", ("linear", "slinear")) +@pytest.mark.parametrize("method", ("linear", "nearest", "cubic", "pchip", "quintic")) @pytest.mark.parametrize("chunked", [True, False]) @pytest.mark.parametrize( "data_ndim,interp_ndim,nscalar", @@ -902,10 +900,13 @@ def test_interpolate_chunk_1d( It should do a series of 1d interpolation """ + if method in ["cubic", "pchip"] and interp_ndim == 3: + pytest.skip("Too slow.") + # 3d non chunked data - x = np.linspace(0, 1, 5) + x = np.linspace(0, 1, 6) y = np.linspace(2, 4, 7) - z = np.linspace(-0.5, 0.5, 11) + z = np.linspace(-0.5, 0.5, 8) da = xr.DataArray( data=np.sin(x[:, np.newaxis, np.newaxis]) * np.cos(y[:, np.newaxis]) From f4141607bc7d96ee5b11c1fa679bfb6f2aa484c2 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Wed, 6 Nov 2024 11:15:03 -0700 Subject: [PATCH 23/23] skip quintic for high dim interp --- xarray/tests/test_interp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py index de5c4daca02..0e797b73d52 100644 --- a/xarray/tests/test_interp.py +++ b/xarray/tests/test_interp.py @@ -900,7 +900,7 @@ def test_interpolate_chunk_1d( It should do a series of 1d interpolation """ - if method in ["cubic", "pchip"] and interp_ndim == 3: + if method in ["cubic", "pchip", "quintic"] and interp_ndim == 3: pytest.skip("Too slow.") # 3d non chunked data