Repository: numpy/numpy

Issue:
numpy.gradient inconsistent returns
``` python
x = np.arange(20)
np.gradient(x)
```

returns an array. While,

``` python
x = np.arange(20)
x = x.resape(10,2)
np.gradient(x)
```

returns a list of arrays. For consistency the returns should be the same or force a default axis=0 argument so that an array is always returned.

As a side note, it would be nice if gradient could handle the following...

``` python
x = np.arange(20)
x = x.resape(10,2,1)
np.gradient(x)
```

> ValueError: Shape of array too small to calculate a numerical gradient, at least two elements are required.

and only fail if the user requests the gradient along the signal element axis.

docstring on `gradient`, confusing
i may be misunderstanding the docstring on this function. the gradient function, describes an argument `varargs` to be the sample distance in each direction, but then in the docstring  example it shows

```
>>> x = np.array([0, 1, 2, 3, 4])
>>> dx = np.gradient(x)
>>> y = x**2
>>> np.gradient(y, dx, edge_order=2)
array([-0.,  2.,  4.,  6.,  8.])
```

which seems like `dx` is the sample distance between each sample. so this example seems to indicate that `varargs` is more accurately described by `dx1, dx2,...dxN`, not `dx,dy,dz,...`.

the ealier interpretation appears to align more closely with some test case  results as well. 



Discussion:
Thanks for the report.

Happily, your second issue is solved in numpy 1.11, which added an `axis` parameter to `np.gradient`. See #6129. So you can do `np.gradient(np.arange(20).reshape(10,2,1), axis=(0,1))`.

As to the inconsistency in return values: I believe this is the indended behavior but the docstring is misleading. It is true that the docstring says `np.gradient` returns a "list of ndarray", but if you look lower in the examples section there is a description of how it returns an ndarray in the 1d case and a list of ndarrays otherwise.

I suggests that we should just update the docstring to reflect reality. The docstring has other problems too, see #6847.

Oh, the docstring was already changed in #5757, but not well enough :)

I agree that this example works but does not correspond to the  possibilities described earlier in the docstring.

That the example works at all is anyways a coincidence, as it only works properly for evenly spaced arrays x, where dx consists of a the same number repeated over and over again.

I would propose to remove this example from the docstring as it is really misleading of what gradient is capable to do.


Resolving patch:
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -1410,9 +1410,10 @@ def gradient(f, *varargs, **kwargs):
 
     Returns
     -------
-    gradient : list of ndarray
-        Each element of `list` has the same shape as `f` giving the derivative
-        of `f` with respect to each dimension.
+    gradient : ndarray or list of ndarray
+        A set of ndarrays (or a single ndarray if there is only one dimension)
+        correposnding to the derivatives of f with respect to each dimension.
+        Each derivative has the same shape as f.
 
     Examples
     --------
@@ -1432,9 +1433,8 @@ def gradient(f, *varargs, **kwargs):
             [ 1. ,  1. ,  1. ]])]
 
     >>> x = np.array([0, 1, 2, 3, 4])
-    >>> dx = np.gradient(x)
     >>> y = x**2
-    >>> np.gradient(y, dx, edge_order=2)
+    >>> np.gradient(y, edge_order=2)
     array([-0.,  2.,  4.,  6.,  8.])
 
     The axis keyword can be used to specify a subset of axes of which the gradient is calculated
@@ -1472,6 +1472,8 @@ def gradient(f, *varargs, **kwargs):
     else:
         raise SyntaxError(
             "invalid number of arguments")
+    if any([not np.isscalar(dxi) for dxi in dx]):
+        raise ValueError("distances must be scalars")
 
     edge_order = kwargs.pop('edge_order', 1)
     if kwargs:


Test patch:
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -666,6 +666,9 @@ def test_badargs(self):
         assert_raises(SyntaxError, gradient, x, np.array([1., 1.]),
                       np.array([1., 1.]), np.array([1., 1.]))
 
+        # disallow arrays as distances, see gh-6847
+        assert_raises(ValueError, gradient, np.arange(5), np.ones(5))
+
     def test_masked(self):
         # Make sure that gradient supports subclasses like masked arrays
         x = np.ma.array([[1, 1], [3, 4]],