summaryrefslogtreecommitdiff
path: root/numpy/lib/function_base.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/lib/function_base.py')
-rw-r--r--numpy/lib/function_base.py149
1 files changed, 108 insertions, 41 deletions
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index 0a1d05f77..135053e43 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -263,7 +263,7 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None):
normed : bool, optional
If False, returns the number of samples in each bin. If True,
returns the bin density ``bin_count / sample_count / bin_volume``.
- weights : array_like (N,), optional
+ weights : (N,) array_like, optional
An array of values `w_i` weighing each sample `(x_i, y_i, z_i, ...)`.
Weights are normalized to 1 if normed is True. If normed is False,
the values of the returned histogram are equal to the sum of the
@@ -337,6 +337,11 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None):
smin[i] = smin[i] - .5
smax[i] = smax[i] + .5
+ # avoid rounding issues for comparisons when dealing with inexact types
+ if np.issubdtype(sample.dtype, np.inexact):
+ edge_dt = sample.dtype
+ else:
+ edge_dt = float
# Create edge arrays
for i in arange(D):
if isscalar(bins[i]):
@@ -345,9 +350,9 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None):
"Element at index %s in `bins` should be a positive "
"integer." % i)
nbin[i] = bins[i] + 2 # +2 for outlier bins
- edges[i] = linspace(smin[i], smax[i], nbin[i]-1)
+ edges[i] = linspace(smin[i], smax[i], nbin[i]-1, dtype=edge_dt)
else:
- edges[i] = asarray(bins[i], float)
+ edges[i] = asarray(bins[i], edge_dt)
nbin[i] = len(edges[i]) + 1 # +1 for outlier bins
dedges[i] = diff(edges[i])
if np.any(np.asarray(dedges[i]) <= 0):
@@ -456,7 +461,7 @@ def average(a, axis=None, weights=None, returned=False):
Returns
-------
- average, [sum_of_weights] : {array_type, double}
+ average, [sum_of_weights] : array_type or double
Return the average along the specified axis. When returned is `True`,
return a tuple with the average as the first element and the sum
of the weights as the second element. The return type is `Float`
@@ -510,8 +515,7 @@ def average(a, axis=None, weights=None, returned=False):
scl = avg.dtype.type(a.size/avg.size)
else:
a = a + 0.0
- wgt = np.array(weights, dtype=a.dtype, copy=0)
-
+ wgt = np.asarray(weights)
# Sanity checks
if a.shape != wgt.shape:
if axis is None:
@@ -528,7 +532,7 @@ def average(a, axis=None, weights=None, returned=False):
# setup wgt to broadcast along axis
wgt = np.array(wgt, copy=0, ndmin=a.ndim).swapaxes(-1, axis)
- scl = wgt.sum(axis=axis)
+ scl = wgt.sum(axis=axis, dtype=np.result_type(a.dtype, wgt.dtype))
if (scl == 0.0).any():
raise ZeroDivisionError(
"Weights sum to zero, can't be normalized")
@@ -878,28 +882,33 @@ def copy(a, order='K'):
# Basic operations
-def gradient(f, *varargs):
+def gradient(f, *varargs, **kwargs):
"""
Return the gradient of an N-dimensional array.
The gradient is computed using second order accurate central differences
- in the interior and second order accurate one-sides (forward or backwards)
- differences at the boundaries. The returned gradient hence has the same
- shape as the input array.
+ in the interior and either first differences or second order accurate
+ one-sides (forward or backwards) differences at the boundaries. The
+ returned gradient hence has the same shape as the input array.
Parameters
----------
f : array_like
- An N-dimensional array containing samples of a scalar function.
- `*varargs` : scalars
- 0, 1, or N scalars specifying the sample distances in each direction,
- that is: `dx`, `dy`, `dz`, ... The default distance is 1.
+ An N-dimensional array containing samples of a scalar function.
+ varargs : list of scalar, optional
+ N scalars specifying the sample distances for each dimension,
+ i.e. `dx`, `dy`, `dz`, ... Default distance: 1.
+ edge_order : {1, 2}, optional
+ Gradient is calculated using N\ :sup:`th` order accurate differences
+ at the boundaries. Default: 1.
+
+ .. versionadded:: 1.9.1
Returns
-------
gradient : ndarray
- N arrays of the same shape as `f` giving the derivative of `f` with
- respect to each dimension.
+ N arrays of the same shape as `f` giving the derivative of `f` with
+ respect to each dimension.
Examples
--------
@@ -911,15 +920,14 @@ def gradient(f, *varargs):
>>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float))
[array([[ 2., 2., -1.],
- [ 2., 2., -1.]]),
- array([[ 1. , 2.5, 4. ],
- [ 1. , 1. , 1. ]])]
+ [ 2., 2., -1.]]), array([[ 1. , 2.5, 4. ],
+ [ 1. , 1. , 1. ]])]
- >>> x = np.array([0,1,2,3,4])
- >>> dx = gradient(x)
+ >>> x = np.array([0, 1, 2, 3, 4])
+ >>> dx = np.gradient(x)
>>> y = x**2
- >>> gradient(y,dx)
- array([0., 2., 4., 6., 8.])
+ >>> np.gradient(y, dx, edge_order=2)
+ array([-0., 2., 4., 6., 8.])
"""
f = np.asanyarray(f)
N = len(f.shape) # number of dimensions
@@ -934,6 +942,13 @@ def gradient(f, *varargs):
raise SyntaxError(
"invalid number of arguments")
+ edge_order = kwargs.pop('edge_order', 1)
+ if kwargs:
+ raise TypeError('"{}" are not valid keyword arguments.'.format(
+ '", "'.join(kwargs.keys())))
+ if edge_order > 2:
+ raise ValueError("'edge_order' greater than 2 not supported")
+
# use central differences on interior and one-sided differences on the
# endpoints. This preserves second order-accuracy over the full domain.
@@ -973,7 +988,7 @@ def gradient(f, *varargs):
"at least two elements are required.")
# Numerical differentiation: 1st order edges, 2nd order interior
- if y.shape[axis] == 2:
+ if y.shape[axis] == 2 or edge_order == 1:
# Use first order differences for time data
out = np.empty_like(y, dtype=otype)
@@ -1021,7 +1036,8 @@ def gradient(f, *varargs):
out[slice1] = (3.0*y[slice2] - 4.0*y[slice3] + y[slice4])/2.0
# divide by step size
- outvals.append(out / dx[axis])
+ out /= dx[axis]
+ outvals.append(out)
# reset the slice object in this dimension to ":"
slice1[axis] = slice(None)
@@ -1097,7 +1113,7 @@ def diff(a, n=1, axis=-1):
return a[slice1]-a[slice2]
-def interp(x, xp, fp, left=None, right=None):
+def interp(x, xp, fp, left=None, right=None, period=None):
"""
One-dimensional linear interpolation.
@@ -1110,7 +1126,9 @@ def interp(x, xp, fp, left=None, right=None):
The x-coordinates of the interpolated values.
xp : 1-D sequence of floats
- The x-coordinates of the data points, must be increasing.
+ The x-coordinates of the data points, must be increasing if argument
+ `period` is not specified. Otherwise, `xp` is internally sorted after
+ normalizing the periodic boundaries with ``xp = xp % period``.
fp : 1-D sequence of floats
The y-coordinates of the data points, same length as `xp`.
@@ -1121,15 +1139,23 @@ def interp(x, xp, fp, left=None, right=None):
right : float, optional
Value to return for `x > xp[-1]`, default is `fp[-1]`.
+ period : None or float, optional
+ .. versionadded:: 1.10.0
+ A period for the x-coordinates. This parameter allows the proper
+ interpolation of angular x-coordinates. Parameters `left` and `right`
+ are ignored if `period` is specified.
+
Returns
-------
- y : {float, ndarray}
+ y : float or ndarray
The interpolated values, same shape as `x`.
Raises
------
ValueError
If `xp` and `fp` have different length
+ If `xp` or `fp` are not 1-D sequences
+ If `period == 0`
Notes
-----
@@ -1139,7 +1165,6 @@ def interp(x, xp, fp, left=None, right=None):
np.all(np.diff(xp) > 0)
-
Examples
--------
>>> xp = [1, 2, 3]
@@ -1165,13 +1190,51 @@ def interp(x, xp, fp, left=None, right=None):
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.show()
+ Interpolation with periodic x-coordinates:
+
+ >>> x = [-180, -170, -185, 185, -10, -5, 0, 365]
+ >>> xp = [190, -190, 350, -350]
+ >>> fp = [5, 10, 3, 4]
+ >>> np.interp(x, xp, fp, period=360)
+ array([7.5, 5., 8.75, 6.25, 3., 3.25, 3.5, 3.75])
+
"""
- if isinstance(x, (float, int, number)):
- return compiled_interp([x], xp, fp, left, right).item()
- elif isinstance(x, np.ndarray) and x.ndim == 0:
- return compiled_interp([x], xp, fp, left, right).item()
+ if period is None:
+ if isinstance(x, (float, int, number)):
+ return compiled_interp([x], xp, fp, left, right).item()
+ elif isinstance(x, np.ndarray) and x.ndim == 0:
+ return compiled_interp([x], xp, fp, left, right).item()
+ else:
+ return compiled_interp(x, xp, fp, left, right)
else:
- return compiled_interp(x, xp, fp, left, right)
+ if period == 0:
+ raise ValueError("period must be a non-zero value")
+ period = abs(period)
+ left = None
+ right = None
+ return_array = True
+ if isinstance(x, (float, int, number)):
+ return_array = False
+ x = [x]
+ x = np.asarray(x, dtype=np.float64)
+ xp = np.asarray(xp, dtype=np.float64)
+ fp = np.asarray(fp, dtype=np.float64)
+ if xp.ndim != 1 or fp.ndim != 1:
+ raise ValueError("Data points must be 1-D sequences")
+ if xp.shape[0] != fp.shape[0]:
+ raise ValueError("fp and xp are not of the same length")
+ # normalizing periodic boundaries
+ x = x % period
+ xp = xp % period
+ asort_xp = np.argsort(xp)
+ xp = xp[asort_xp]
+ fp = fp[asort_xp]
+ xp = np.concatenate((xp[-1:]-period, xp, xp[0:1]+period))
+ fp = np.concatenate((fp[-1:], fp, fp[0:1]))
+ if return_array:
+ return compiled_interp(x, xp, fp, left, right)
+ else:
+ return compiled_interp(x, xp, fp, left, right).item()
def angle(z, deg=0):
@@ -1187,7 +1250,7 @@ def angle(z, deg=0):
Returns
-------
- angle : {ndarray, scalar}
+ angle : ndarray or scalar
The counterclockwise angle from the positive real axis on
the complex plane, with dtype as numpy.float64.
@@ -1387,6 +1450,8 @@ def extract(condition, arr):
This is equivalent to ``np.compress(ravel(condition), ravel(arr))``. If
`condition` is boolean ``np.extract`` is equivalent to ``arr[condition]``.
+ Note that `place` does the exact opposite of `extract`.
+
Parameters
----------
condition : array_like
@@ -1402,7 +1467,7 @@ def extract(condition, arr):
See Also
--------
- take, put, copyto, compress
+ take, put, copyto, compress, place
Examples
--------
@@ -1915,7 +1980,7 @@ def corrcoef(x, y=None, rowvar=1, bias=0, ddof=None):
observations (unbiased estimate). If `bias` is 1, then
normalization is by ``N``. These values can be overridden by using
the keyword ``ddof`` in numpy versions >= 1.5.
- ddof : {None, int}, optional
+ ddof : int, optional
.. versionadded:: 1.5
If not ``None`` normalization is by ``(N - ddof)``, where ``N`` is
the number of observations; this overrides the value implied by
@@ -2998,7 +3063,7 @@ def percentile(a, q, axis=None, out=None,
nearest neighbors as well as the `interpolation` parameter will
determine the percentile if the normalized ranking does not match q
exactly. This function is the same as the median if ``q=50``, the same
- as the minimum if ``q=0``and the same as the maximum if ``q=100``.
+ as the minimum if ``q=0`` and the same as the maximum if ``q=100``.
Examples
--------
@@ -3031,7 +3096,7 @@ def percentile(a, q, axis=None, out=None,
array([ 3.5])
"""
- q = asarray(q, dtype=np.float64)
+ q = array(q, dtype=np.float64, copy=True)
r, k = _ureduce(a, func=_percentile, q=q, axis=axis, out=out,
overwrite_input=overwrite_input,
interpolation=interpolation)
@@ -3758,7 +3823,9 @@ def insert(arr, obj, values, axis=None):
if (index < 0):
index += N
- values = array(values, copy=False, ndmin=arr.ndim)
+ # There are some object array corner cases here, but we cannot avoid
+ # that:
+ values = array(values, copy=False, ndmin=arr.ndim, dtype=arr.dtype)
if indices.ndim == 0:
# broadcasting is very different here, since a[:,0,:] = ... behaves
# very different from a[:,[0],:] = ...! This changes values so that