-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
implement Gradient #2398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement Gradient #2398
Changes from 18 commits
59ff688
0adfc68
d665b3c
e0fa5fd
218e62d
888b924
a0ab4c2
c581513
d6be041
a083460
267694d
bf2f35e
2a71b62
b504da8
fb356c5
1694d3c
7a0b57f
e93b926
4c656e0
74acc81
8817306
4112cd9
1c2c88c
cbfecb4
2e8db19
c31539e
528bcab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1137,3 +1137,81 @@ def where(cond, x, y): | |
| join='exact', | ||
| dataset_join='exact', | ||
| dask='allowed') | ||
|
|
||
|
|
||
| def _differentiate_variable(variable, coord, edge_order): | ||
| """ Compute the gradient along 1 dimension for Variable. | ||
| variable, coord: Variable | ||
| """ | ||
| from .variable import Variable | ||
|
|
||
| result_array = duck_array_ops.gradient( | ||
| variable.data, coord.data, edge_order=edge_order, | ||
| axis=variable.get_axis_num(coord.dims[0])) | ||
| return Variable(variable.dims, result_array) | ||
|
|
||
|
|
||
| def differentiate(dataarray, coord, edge_order=1): | ||
| """ Compute the gradient of the dataarray with the second order accurate | ||
| central differences. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| dataarray: xr.DataArray | ||
| Target array | ||
| coord: str | ||
| The coordinate to be used to differentiate the array. | ||
| edge_order: 1 or 2. Default 1 | ||
| N-th order accurate differences at the boundaries. | ||
|
|
||
| Returns | ||
| ------- | ||
| differentiate: DataArray or a sequence of DataArrays | ||
|
|
||
| See also | ||
| -------- | ||
| numpy.gradient: corresponding numpy function | ||
|
|
||
| Examples | ||
| -------- | ||
|
|
||
| >>> da = xr.DataArray(np.arange(12).reshape(4, 3), dims=['x', 'y'], | ||
| ... coords={'x': [0, 0.1, 1.1, 1.2]}) | ||
| >>> da | ||
| <xarray.DataArray (x: 4, y: 3)> | ||
| array([[ 0, 1, 2], | ||
| [ 3, 4, 5], | ||
| [ 6, 7, 8], | ||
| [ 9, 10, 11]]) | ||
| Coordinates: | ||
| * x (x) float64 0.0 0.1 1.1 1.2 | ||
| Dimensions without coordinates: y | ||
| >>> | ||
| >>> xr.differentiate(da, 'x') | ||
| <xarray.DataArray (x: 4, y: 3)> | ||
| array([[30. , 30. , 30. ], | ||
| [27.545455, 27.545455, 27.545455], | ||
| [27.545455, 27.545455, 27.545455], | ||
| [30. , 30. , 30. ]]) | ||
| Coordinates: | ||
| * x (x) float64 0.0 0.1 1.1 1.2 | ||
| Dimensions without coordinates: y | ||
| """ | ||
| from .dataarray import DataArray | ||
|
|
||
| if not isinstance(dataarray, DataArray): | ||
| raise TypeError( | ||
| 'Only DataArray is supported. Given {}.'.format(type(dataarray))) | ||
|
||
|
|
||
| if coord not in dataarray.coords and coord not in dataarray.dims: | ||
| raise ValueError('Coordiante {} does not exist.'.format(coord)) | ||
|
|
||
| coord_var = dataarray[coord].variable | ||
| if coord_var.ndim != 1: | ||
| raise ValueError( | ||
| 'Only 1d-coordinate is supported. {} is {} ' | ||
| 'dimensional.'.format(coord, dataarray[coord].ndim)) | ||
|
|
||
| return DataArray( | ||
| _differentiate_variable(dataarray.variable, coord_var, edge_order), | ||
| coords=dataarray.coords) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| from __future__ import absolute_import, division, print_function | ||
|
|
||
| from distutils.version import LooseVersion | ||
|
|
||
| import numpy as np | ||
| from dask import __version__ as dask_version | ||
| import dask.array as da | ||
|
|
||
| try: | ||
|
|
@@ -30,3 +33,124 @@ def isin(element, test_elements, assume_unique=False, invert=False): | |
| if invert: | ||
| result = ~result | ||
| return result | ||
|
|
||
|
|
||
| if LooseVersion(dask_version) > LooseVersion('1.19.2'): | ||
| gradient = da.gradient | ||
|
|
||
| else: # pragma: no cover | ||
| # Copied from dask v0.19.2 | ||
| # Used under the terms of Dask's license, see licenses/DASK_LICENSE. | ||
| import math | ||
| from numbers import Integral, Real | ||
|
|
||
| AxisError = np.AxisError | ||
|
|
||
| def validate_axis(axis, ndim): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. E303 too many blank lines (2) |
||
| """ Validate an input to axis= keywords """ | ||
| if isinstance(axis, (tuple, list)): | ||
| return tuple(validate_axis(ax, ndim) for ax in axis) | ||
| if not isinstance(axis, Integral): | ||
| raise TypeError("Axis value must be an integer, got %s" % axis) | ||
| if axis < -ndim or axis >= ndim: | ||
| raise AxisError("Axis %d is out of bounds for array of dimension " | ||
| "%d" % (axis, ndim)) | ||
| if axis < 0: | ||
| axis += ndim | ||
| return axis | ||
|
|
||
| def _gradient_kernel(x, block_id, coord, axis, array_locs, grad_kwargs): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. E303 too many blank lines (2) |
||
| """ | ||
| x: nd-array | ||
| array of one block | ||
| coord: 1d-array or scalar | ||
| coordinate along which the gradient is computed. | ||
| axis: int | ||
| axis along which the gradient is computed | ||
| array_locs: | ||
| actual location along axis. None if coordinate is scalar | ||
| grad_kwargs: | ||
| keyword to be passed to np.gradient | ||
| """ | ||
| block_loc = block_id[axis] | ||
| if array_locs is not None: | ||
| coord = coord[array_locs[0][block_loc]:array_locs[1][block_loc]] | ||
| grad = np.gradient(x, coord, axis=axis, **grad_kwargs) | ||
| return grad | ||
|
|
||
| def gradient(f, *varargs, **kwargs): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. E303 too many blank lines (2) |
||
| f = da.asarray(f) | ||
|
|
||
| kwargs["edge_order"] = math.ceil(kwargs.get("edge_order", 1)) | ||
| if kwargs["edge_order"] > 2: | ||
| raise ValueError("edge_order must be less than or equal to 2.") | ||
|
|
||
| drop_result_list = False | ||
| axis = kwargs.pop("axis", None) | ||
| if axis is None: | ||
| axis = tuple(range(f.ndim)) | ||
| elif isinstance(axis, Integral): | ||
| drop_result_list = True | ||
| axis = (axis,) | ||
|
|
||
| axis = validate_axis(axis, f.ndim) | ||
|
|
||
| if len(axis) != len(set(axis)): | ||
| raise ValueError("duplicate axes not allowed") | ||
|
|
||
| axis = tuple(ax % f.ndim for ax in axis) | ||
|
|
||
| if varargs == (): | ||
| varargs = (1,) | ||
| if len(varargs) == 1: | ||
| varargs = len(axis) * varargs | ||
| if len(varargs) != len(axis): | ||
| raise TypeError( | ||
| "Spacing must either be a single scalar, or a scalar / " | ||
| "1d-array per axis" | ||
| ) | ||
|
|
||
| if issubclass(f.dtype.type, (np.bool8, Integral)): | ||
| f = f.astype(float) | ||
| elif issubclass(f.dtype.type, Real) and f.dtype.itemsize < 4: | ||
| f = f.astype(float) | ||
|
|
||
| results = [] | ||
| for i, ax in enumerate(axis): | ||
| for c in f.chunks[ax]: | ||
| if np.min(c) < kwargs["edge_order"] + 1: | ||
| raise ValueError( | ||
| 'Chunk size must be larger than edge_order + 1. ' | ||
| 'Minimum chunk for aixs {} is {}. Rechunk to ' | ||
| 'proceed.'.format(np.min(c), ax)) | ||
|
|
||
| if np.isscalar(varargs[i]): | ||
| array_locs = None | ||
| else: | ||
| if isinstance(varargs[i], da.Array): | ||
| raise NotImplementedError( | ||
| 'dask array coordinated is not supported.') | ||
| # coordinate position for each block taking overlap into | ||
| # account | ||
| chunk = np.array(f.chunks[ax]) | ||
| array_loc_stop = np.cumsum(chunk) + 1 | ||
| array_loc_start = array_loc_stop - chunk - 2 | ||
| array_loc_stop[-1] -= 1 | ||
| array_loc_start[0] = 0 | ||
| array_locs = (array_loc_start, array_loc_stop) | ||
|
|
||
| results.append(f.map_overlap( | ||
| _gradient_kernel, | ||
| dtype=f.dtype, | ||
| depth={j: 1 if j == ax else 0 for j in range(f.ndim)}, | ||
| boundary="none", | ||
| coord=varargs[i], | ||
| axis=ax, | ||
| array_locs=array_locs, | ||
| grad_kwargs=kwargs, | ||
| )) | ||
|
|
||
| if drop_result_list: | ||
| results = results[0] | ||
|
|
||
| return results | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2010,6 +2010,9 @@ def diff(self, dim, n=1, label='upper'): | |
| Coordinates: | ||
| * x (x) int64 3 4 | ||
|
|
||
| See Also | ||
| -------- | ||
| DataArray.differentiate | ||
| """ | ||
| ds = self._to_temp_dataset().diff(n=n, dim=dim, label=label) | ||
| return self._from_temp_dataset(ds) | ||
|
|
@@ -2289,6 +2292,54 @@ def rank(self, dim, pct=False, keep_attrs=False): | |
| ds = self._to_temp_dataset().rank(dim, pct=pct, keep_attrs=keep_attrs) | ||
| return self._from_temp_dataset(ds) | ||
|
|
||
| def differentiate(self, coord, edge_order=1): | ||
|
||
| """ Differentiate the array with the second order accurate central | ||
| differences. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and here |
||
|
|
||
| Parameters | ||
| ---------- | ||
| coord: str | ||
| The coordinate to be used to compute the gradient. | ||
| edge_order: 1 or 2. Default 1 | ||
| N-th order accurate differences at the boundaries. | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs |
||
| Returns | ||
| ------- | ||
| differentiated: DataArray | ||
|
|
||
| See also | ||
| -------- | ||
| numpy.gradient: corresponding numpy function | ||
| xr.differentiate: more numpy-like function for xarray object. | ||
|
|
||
| Examples | ||
| -------- | ||
|
|
||
| >>> da = xr.DataArray(np.arange(12).reshape(4, 3), dims=['x', 'y'], | ||
| ... coords={'x': [0, 0.1, 1.1, 1.2]}) | ||
| >>> da | ||
| <xarray.DataArray (x: 4, y: 3)> | ||
| array([[ 0, 1, 2], | ||
| [ 3, 4, 5], | ||
| [ 6, 7, 8], | ||
| [ 9, 10, 11]]) | ||
| Coordinates: | ||
| * x (x) float64 0.0 0.1 1.1 1.2 | ||
| Dimensions without coordinates: y | ||
| >>> | ||
| >>> da.differentiate('x') | ||
| <xarray.DataArray (x: 4, y: 3)> | ||
| array([[30. , 30. , 30. ], | ||
| [27.545455, 27.545455, 27.545455], | ||
| [27.545455, 27.545455, 27.545455], | ||
| [30. , 30. , 30. ]]) | ||
| Coordinates: | ||
| * x (x) float64 0.0 0.1 1.1 1.2 | ||
| Dimensions without coordinates: y | ||
| """ | ||
| ds = self._to_temp_dataset().differentiate(coord, edge_order) | ||
| return self._from_temp_dataset(ds) | ||
|
|
||
|
|
||
| # priority most be higher than Variable to properly work with binary ufuncs | ||
| ops.inject_all_ops_and_reduce_methods(DataArray, priority=60) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
xarray.differentiateis no longer added.