def getcount(arr, hits, shots=10): """Gets a summary of accuracies""" assert arr.shape==(batch_size, (seq_size +1)), \ "array input shape does not match expected shape: (%d,%d)"%(batch_size, (seq_size +1)) current_count = np.zeros((batch_size, seq_size + 1, num_classes)) success_count = np.zeros_like(current_count) shot_count = np.zeros((num_classes, shots)) shot_success_count = np.zeros((num_classes, shots)) for I in ndi.ndindex(arr.shape): current_count[I][arr[I]] = 1 success_count[I][arr[I]] = hits[I] temp = np.cumsum(current_count, axis=1) for batch in range(batch_size): for instance in range(seq_size + 1): for class_label in range(num_classes): num_shot = int(temp[batch, instance, class_label]) if num_shot != 0 and num_shot - 1 < shots: shot_count[class_label, num_shot - 1] += 1 shot_success_count[class_label, num_shot - 1] += hits[batch, instance] total_count = np.sum(current_count, axis=0).T success_count = (np.sum(success_count, axis=0).T) return total_count, success_count, shot_count, shot_success_count
def apply_along_axis(func1d, axis, arr, flux, *args, **kwargs): # handle negative axes arr = asanyarray(arr) flux = asanyarray(flux) nd = arr.ndim axis = normalize_axis_index(axis, nd) # arr, with the iteration axis at the end in_dims = list(range(nd)) inarr_view = transpose(arr, in_dims[:axis] + in_dims[axis + 1:] + [axis]) flux_view = transpose(flux, in_dims[:axis] + in_dims[axis + 1:] + [axis]) # compute indices for the iteration axes, and append a trailing ellipsis to # prevent 0d arrays decaying to scalars, which fixes gh-8642 inds = ndindex(inarr_view.shape[:-1]) inds = (ind + (Ellipsis, ) for ind in inds) # invoke the function on the first item try: ind0 = next(inds) except StopIteration: raise ValueError( 'Cannot apply_along_axis when any iteration dimensions are 0') res = asanyarray(func1d(inarr_view[ind0], flux_view[ind0], *args, **kwargs)) # build a buffer for storing evaluations of func1d. # remove the requested axis, and add the new ones on the end. # laid out so that each write is contiguous. # for a tuple index inds, buff[inds] = func1d(inarr_view[inds]) buff = zeros(inarr_view.shape[:-1] + res.shape, res.dtype) # permutation of axes such that out = buff.transpose(buff_permute) buff_dims = list(range(buff.ndim)) buff_permute = (buff_dims[0:axis] + buff_dims[buff.ndim - res.ndim:buff.ndim] + buff_dims[axis:buff.ndim - res.ndim]) # matrices have a nasty __array_prepare__ and __array_wrap__ if not isinstance(res, matrix): buff = res.__array_prepare__(buff) # save the first result, then compute and save all remaining results buff[ind0] = res for ind in inds: buff[ind] = asanyarray( func1d(inarr_view[ind], flux_view[ind], *args, **kwargs)) if not isinstance(res, matrix): # wrap the array, to preserve subclasses buff = res.__array_wrap__(buff) # finally, rotate the inserted axes back to where they belong return transpose(buff, buff_permute) else: # matrices have to be transposed first, because they collapse dimensions! out_arr = transpose(buff, buff_permute) return res.__array_wrap__(out_arr)
def counter(y_seq, n=num_classes): u, v, w = y_seq.shape class_label = np.argmax(y_seq, axis=2) g = np.cumsum(y_seq, axis=1) return np.array([ g[I][class_label[I]] for I in ndi.ndindex(class_label.shape) ]).reshape(u, v)
def apply_along_axis(func1d, axis, arr, *args, **kwargs): """Apply a function to 1-D slices along the given axis. Args: func1d (function (M,) -> (Nj...)): This function should accept 1-D arrays. It is applied to 1-D slices of ``arr`` along the specified axis. It must return a 1-D ``cupy.ndarray``. axis (integer): Axis along which ``arr`` is sliced. arr (cupy.ndarray (Ni..., M, Nk...)): Input array. args: Additional arguments for ``func1d``. kwargs: Additional keyword arguments for ``func1d``. Returns: cupy.ndarray: The output array. The shape of ``out`` is identical to the shape of ``arr``, except along the ``axis`` dimension. This axis is removed, and replaced with new dimensions equal to the shape of the return value of ``func1d``. So if ``func1d`` returns a scalar ``out`` will have one fewer dimensions than ``arr``. .. seealso:: :func:`numpy.apply_along_axis` """ ndim = arr.ndim axis = internal._normalize_axis_index(axis, ndim) inarr_view = cupy.moveaxis(arr, axis, -1) # compute indices for the iteration axes, and append a trailing ellipsis to # prevent 0d arrays decaying to scalars inds = index_tricks.ndindex(inarr_view.shape[:-1]) inds = (ind + (Ellipsis,) for ind in inds) # invoke the function on the first item try: ind0 = next(inds) except StopIteration: raise ValueError( 'Cannot apply_along_axis when any iteration dimensions are 0' ) res = func1d(inarr_view[ind0], *args, **kwargs) if cupy.isscalar(res): # scalar outputs need to be transfered to a device ndarray res = cupy.asarray(res) # build a buffer for storing evaluations of func1d. # remove the requested axis, and add the new ones on the end. # laid out so that each write is contiguous. # for a tuple index inds, buff[inds] = func1d(inarr_view[inds]) buff = cupy.empty(inarr_view.shape[:-1] + res.shape, res.dtype) # save the first result, then compute and save all remaining results buff[ind0] = res for ind in inds: buff[ind] = func1d(inarr_view[ind], *args, **kwargs) # restore the inserted axes back to where they belong for i in range(res.ndim): buff = cupy.moveaxis(buff, -1, axis) return buff
def test_ndindex(): x = list(ndindex(1, 2, 3)) expected = [ix for ix, e in ndenumerate(np.zeros((1, 2, 3)))] assert_array_equal(x, expected) x = list(ndindex((1, 2, 3))) assert_array_equal(x, expected) # Test use of scalars and tuples x = list(ndindex((3, ))) assert_array_equal(x, list(ndindex(3))) # Make sure size argument is optional x = list(ndindex()) assert_equal(x, [()]) x = list(ndindex(())) assert_equal(x, [()]) # Make sure 0-sized ndindex works correctly x = list(ndindex(*[0])) assert_equal(x, [])
def test_ndindex(): x = list(ndindex(1, 2, 3)) expected = [ix for ix, e in ndenumerate(np.zeros((1, 2, 3)))] assert_array_equal(x, expected) x = list(ndindex((1, 2, 3))) assert_array_equal(x, expected) # Test use of scalars and tuples x = list(ndindex((3,))) assert_array_equal(x, list(ndindex(3))) # Make sure size argument is optional x = list(ndindex()) assert_equal(x, [()]) x = list(ndindex(())) assert_equal(x, [()]) # Make sure 0-sized ndindex works correctly x = list(ndindex(*[0])) assert_equal(x, [])
def apply_along_axis(func1d, axis, arr, *args, **kwargs): """ Apply a function to 1-D slices along the given axis. Execute `func1d(a, *args, **kwargs)` where `func1d` operates on 1-D arrays and `a` is a 1-D slice of `arr` along `axis`. This is equivalent to (but faster than) the following use of `ndindex` and `s_`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of indices:: Ni, Nk = a.shape[:axis], a.shape[axis+1:] for ii in ndindex(Ni): for kk in ndindex(Nk): f = func1d(arr[ii + s_[:,] + kk]) Nj = f.shape for jj in ndindex(Nj): out[ii + jj + kk] = f[jj] Equivalently, eliminating the inner loop, this can be expressed as:: Ni, Nk = a.shape[:axis], a.shape[axis+1:] for ii in ndindex(Ni): for kk in ndindex(Nk): out[ii + s_[...,] + kk] = func1d(arr[ii + s_[:,] + kk]) Parameters ---------- func1d : function (M,) -> (Nj...) This function should accept 1-D arrays. It is applied to 1-D slices of `arr` along the specified axis. axis : integer Axis along which `arr` is sliced. arr : ndarray (Ni..., M, Nk...) Input array. args : any Additional arguments to `func1d`. kwargs : any Additional named arguments to `func1d`. .. versionadded:: 1.9.0 Returns ------- out : ndarray (Ni..., Nj..., Nk...) The output array. The shape of `out` is identical to the shape of `arr`, except along the `axis` dimension. This axis is removed, and replaced with new dimensions equal to the shape of the return value of `func1d`. So if `func1d` returns a scalar `out` will have one fewer dimensions than `arr`. See Also -------- apply_over_axes : Apply a function repeatedly over multiple axes. Examples -------- >>> def my_func(a): ... \"\"\"Average first and last element of a 1-D array\"\"\" ... return (a[0] + a[-1]) * 0.5 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(my_func, 0, b) array([4., 5., 6.]) >>> np.apply_along_axis(my_func, 1, b) array([2., 5., 8.]) For a function that returns a 1D array, the number of dimensions in `outarr` is the same as `arr`. >>> b = np.array([[8,1,7], [4,3,9], [5,2,6]]) >>> np.apply_along_axis(sorted, 1, b) array([[1, 7, 8], [3, 4, 9], [2, 5, 6]]) For a function that returns a higher dimensional array, those dimensions are inserted in place of the `axis` dimension. >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(np.diag, -1, b) array([[[1, 0, 0], [0, 2, 0], [0, 0, 3]], [[4, 0, 0], [0, 5, 0], [0, 0, 6]], [[7, 0, 0], [0, 8, 0], [0, 0, 9]]]) """ # handle negative axes arr = asanyarray(arr) nd = arr.ndim axis = normalize_axis_index(axis, nd) # arr, with the iteration axis at the end in_dims = list(range(nd)) inarr_view = transpose(arr, in_dims[:axis] + in_dims[axis + 1 :] + [axis]) # compute indices for the iteration axes, and append a trailing ellipsis to # prevent 0d arrays decaying to scalars, which fixes gh-8642 inds = ndindex(inarr_view.shape[:-1]) inds = (ind + (Ellipsis,) for ind in inds) # invoke the function on the first item try: ind0 = next(inds) except StopIteration as e: raise ValueError( "Cannot apply_along_axis when any iteration dimensions are 0" ) from None res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs)) # build a buffer for storing evaluations of func1d. # remove the requested axis, and add the new ones on the end. # laid out so that each write is contiguous. # for a tuple index inds, buff[inds] = func1d(inarr_view[inds]) buff = zeros(inarr_view.shape[:-1] + res.shape, res.dtype) # permutation of axes such that out = buff.transpose(buff_permute) buff_dims = list(range(buff.ndim)) buff_permute = ( buff_dims[0:axis] + buff_dims[buff.ndim - res.ndim : buff.ndim] + buff_dims[axis : buff.ndim - res.ndim] ) # matrices have a nasty __array_prepare__ and __array_wrap__ if not isinstance(res, matrix): buff = res.__array_prepare__(buff) # save the first result, then compute and save all remaining results buff[ind0] = res for ind in inds: buff[ind] = asanyarray(func1d(inarr_view[ind], *args, **kwargs)) if not isinstance(res, matrix): # wrap the array, to preserve subclasses buff = res.__array_wrap__(buff) # finally, rotate the inserted axes back to where they belong return transpose(buff, buff_permute) else: # matrices have to be transposed first, because they collapse dimensions! out_arr = transpose(buff, buff_permute) return res.__array_wrap__(out_arr)
def apply_along_axis(func1d, axis, arr, *args, **kwargs): """ Apply a function to 1-D slices along the given axis. Execute `func1d(a, *args)` where `func1d` operates on 1-D arrays and `a` is a 1-D slice of `arr` along `axis`. Parameters ---------- func1d : function This function should accept 1-D arrays. It is applied to 1-D slices of `arr` along the specified axis. axis : integer Axis along which `arr` is sliced. arr : ndarray Input array. args : any Additional arguments to `func1d`. kwargs : any Additional named arguments to `func1d`. .. versionadded:: 1.9.0 Returns ------- apply_along_axis : ndarray The output array. The shape of `outarr` is identical to the shape of `arr`, except along the `axis` dimension. This axis is removed, and replaced with new dimensions equal to the shape of the return value of `func1d`. So if `func1d` returns a scalar `outarr` will have one fewer dimensions than `arr`. See Also -------- apply_over_axes : Apply a function repeatedly over multiple axes. Examples -------- >>> def my_func(a): ... \"\"\"Average first and last element of a 1-D array\"\"\" ... return (a[0] + a[-1]) * 0.5 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(my_func, 0, b) array([ 4., 5., 6.]) >>> np.apply_along_axis(my_func, 1, b) array([ 2., 5., 8.]) For a function that returns a 1D array, the number of dimensions in `outarr` is the same as `arr`. >>> b = np.array([[8,1,7], [4,3,9], [5,2,6]]) >>> np.apply_along_axis(sorted, 1, b) array([[1, 7, 8], [3, 4, 9], [2, 5, 6]]) For a function that returns a higher dimensional array, those dimensions are inserted in place of the `axis` dimension. >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(np.diag, -1, b) array([[[1, 0, 0], [0, 2, 0], [0, 0, 3]], [[4, 0, 0], [0, 5, 0], [0, 0, 6]], [[7, 0, 0], [0, 8, 0], [0, 0, 9]]]) """ # handle negative axes arr = asanyarray(arr) nd = arr.ndim axis = normalize_axis_index(axis, nd) # arr, with the iteration axis at the end in_dims = list(range(nd)) inarr_view = transpose(arr, in_dims[:axis] + in_dims[axis+1:] + [axis]) # compute indices for the iteration axes, and append a trailing ellipsis to # prevent 0d arrays decaying to scalars, which fixes gh-8642 inds = ndindex(inarr_view.shape[:-1]) inds = (ind + (Ellipsis,) for ind in inds) # invoke the function on the first item try: ind0 = next(inds) except StopIteration: raise ValueError('Cannot apply_along_axis when any iteration dimensions are 0') res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs)) # build a buffer for storing evaluations of func1d. # remove the requested axis, and add the new ones on the end. # laid out so that each write is contiguous. # for a tuple index inds, buff[inds] = func1d(inarr_view[inds]) buff = zeros(inarr_view.shape[:-1] + res.shape, res.dtype) # permutation of axes such that out = buff.transpose(buff_permute) buff_dims = list(range(buff.ndim)) buff_permute = ( buff_dims[0 : axis] + buff_dims[buff.ndim-res.ndim : buff.ndim] + buff_dims[axis : buff.ndim-res.ndim] ) # matrices have a nasty __array_prepare__ and __array_wrap__ if not isinstance(res, matrix): buff = res.__array_prepare__(buff) # save the first result, then compute and save all remaining results buff[ind0] = res for ind in inds: buff[ind] = asanyarray(func1d(inarr_view[ind], *args, **kwargs)) if not isinstance(res, matrix): # wrap the array, to preserve subclasses buff = res.__array_wrap__(buff) # finally, rotate the inserted axes back to where they belong return transpose(buff, buff_permute) else: # matrices have to be transposed first, because they collapse dimensions! out_arr = transpose(buff, buff_permute) return res.__array_wrap__(out_arr)
def pad(array, pad_width, mode='constant', **kwargs): """ Pad an array. Parameters ---------- array : array_like of rank N The array to pad. pad_width : {sequence, array_like, int} Number of values padded to the edges of each axis. ((before_1, after_1), ... (before_N, after_N)) unique pad widths for each axis. ((before, after),) yields same before and after pad for each axis. (pad,) or int is a shortcut for before = after = pad width for all axes. mode : str or function, optional One of the following string values or a user supplied function. 'constant' (default) Pads with a constant value. 'edge' Pads with the edge values of array. 'linear_ramp' Pads with the linear ramp between end_value and the array edge value. 'maximum' Pads with the maximum value of all or part of the vector along each axis. 'mean' Pads with the mean value of all or part of the vector along each axis. 'median' Pads with the median value of all or part of the vector along each axis. 'minimum' Pads with the minimum value of all or part of the vector along each axis. 'reflect' Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis. 'symmetric' Pads with the reflection of the vector mirrored along the edge of the array. 'wrap' Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning. 'empty' Pads with undefined values. .. versionadded:: 1.17 <function> Padding function, see Notes. stat_length : sequence or int, optional Used in 'maximum', 'mean', 'median', and 'minimum'. Number of values at edge of each axis used to calculate the statistic value. ((before_1, after_1), ... (before_N, after_N)) unique statistic lengths for each axis. ((before, after),) yields same before and after statistic lengths for each axis. (stat_length,) or int is a shortcut for before = after = statistic length for all axes. Default is ``None``, to use the entire axis. constant_values : sequence or scalar, optional Used in 'constant'. The values to set the padded values for each axis. ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants for each axis. ``((before, after),)`` yields same before and after constants for each axis. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. end_values : sequence or scalar, optional Used in 'linear_ramp'. The values used for the ending value of the linear_ramp and that will form the edge of the padded array. ``((before_1, after_1), ... (before_N, after_N))`` unique end values for each axis. ``((before, after),)`` yields same before and after end values for each axis. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. reflect_type : {'even', 'odd'}, optional Used in 'reflect', and 'symmetric'. The 'even' style is the default with an unaltered reflection around the edge value. For the 'odd' style, the extended part of the array is created by subtracting the reflected values from two times the edge value. Returns ------- pad : ndarray Padded array of rank equal to `array` with shape increased according to `pad_width`. Notes ----- .. versionadded:: 1.7.0 For an array with rank greater than 1, some of the padding of later axes is calculated from padding of previous axes. This is easiest to think about with a rank 2 array where the corners of the padded array are calculated by using padded values from the first axis. The padding function, if used, should modify a rank 1 array in-place. It has the following signature:: padding_func(vector, iaxis_pad_width, iaxis, kwargs) where vector : ndarray A rank 1 array already padded with zeros. Padded values are vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:]. iaxis_pad_width : tuple A 2-tuple of ints, iaxis_pad_width[0] represents the number of values padded at the beginning of vector where iaxis_pad_width[1] represents the number of values padded at the end of vector. iaxis : int The axis currently being calculated. kwargs : dict Any keyword arguments the function requires. Examples -------- >>> a = [1, 2, 3, 4, 5] >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6)) array([4, 4, 1, ..., 6, 6, 6]) >>> np.pad(a, (2, 3), 'edge') array([1, 1, 1, ..., 5, 5, 5]) >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4)) array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) >>> np.pad(a, (2,), 'maximum') array([5, 5, 1, 2, 3, 4, 5, 5, 5]) >>> np.pad(a, (2,), 'mean') array([3, 3, 1, 2, 3, 4, 5, 3, 3]) >>> np.pad(a, (2,), 'median') array([3, 3, 1, 2, 3, 4, 5, 3, 3]) >>> a = [[1, 2], [3, 4]] >>> np.pad(a, ((3, 2), (2, 3)), 'minimum') array([[1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [3, 3, 3, 4, 3, 3, 3], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1]]) >>> a = [1, 2, 3, 4, 5] >>> np.pad(a, (2, 3), 'reflect') array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]) >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd') array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> np.pad(a, (2, 3), 'symmetric') array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]) >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd') array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]) >>> np.pad(a, (2, 3), 'wrap') array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]) >>> def pad_with(vector, pad_width, iaxis, kwargs): ... pad_value = kwargs.get('padder', 10) ... vector[:pad_width[0]] = pad_value ... vector[-pad_width[1]:] = pad_value >>> a = np.arange(6) >>> a = a.reshape((2, 3)) >>> np.pad(a, 2, pad_with) array([[10, 10, 10, 10, 10, 10, 10], [10, 10, 10, 10, 10, 10, 10], [10, 10, 0, 1, 2, 10, 10], [10, 10, 3, 4, 5, 10, 10], [10, 10, 10, 10, 10, 10, 10], [10, 10, 10, 10, 10, 10, 10]]) >>> np.pad(a, 2, pad_with, padder=100) array([[100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100], [100, 100, 0, 1, 2, 100, 100], [100, 100, 3, 4, 5, 100, 100], [100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100]]) """ array = np.asarray(array) pad_width = np.asarray(pad_width) if not pad_width.dtype.kind == 'i': raise TypeError('`pad_width` must be of integral type.') # Broadcast to shape (array.ndim, 2) pad_width = _as_pairs(pad_width, array.ndim, as_index=True) if callable(mode): # Old behavior: Use user-supplied function with np.apply_along_axis function = mode # Create a new zero padded array padded, _ = _pad_simple(array, pad_width, fill_value=0) # And apply along each axis for axis in range(padded.ndim): # Iterate using ndindex as in apply_along_axis, but assuming that # function operates inplace on the padded array. # view with the iteration axis at the end view = np.moveaxis(padded, axis, -1) # compute indices for the iteration axes, and append a trailing # ellipsis to prevent 0d arrays decaying to scalars (gh-8642) inds = ndindex(view.shape[:-1]) inds = (ind + (Ellipsis, ) for ind in inds) for ind in inds: function(view[ind], pad_width[axis], axis, kwargs) return padded # Make sure that no unsupported keywords were passed for the current mode allowed_kwargs = { 'empty': [], 'edge': [], 'wrap': [], 'constant': ['constant_values'], 'linear_ramp': ['end_values'], 'maximum': ['stat_length'], 'mean': ['stat_length'], 'median': ['stat_length'], 'minimum': ['stat_length'], 'reflect': ['reflect_type'], 'symmetric': ['reflect_type'], } try: unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode]) except KeyError: raise ValueError("mode '{}' is not supported".format(mode)) if unsupported_kwargs: raise ValueError( "unsupported keyword arguments for mode '{}': {}".format( mode, unsupported_kwargs)) stat_functions = { "maximum": np.amax, "minimum": np.amin, "mean": np.mean, "median": np.median } # Create array with final shape and original values # (padded area is undefined) padded, original_area_slice = _pad_simple(array, pad_width) # And prepare iteration over all dimensions # (zipping may be more readable than using enumerate) axes = range(padded.ndim) if mode == "constant": values = kwargs.get("constant_values", 0) values = _as_pairs(values, padded.ndim) for axis, width_pair, value_pair in zip(axes, pad_width, values): roi = _view_roi(padded, original_area_slice, axis) _set_pad_area(roi, axis, width_pair, value_pair) elif mode == "empty": pass # Do nothing as _pad_simple already returned the correct result elif array.size == 0: # Only modes "constant" and "empty" can extend empty axes, all other # modes depend on `array` not being empty # -> ensure every empty axis is only "padded with 0" for axis, width_pair in zip(axes, pad_width): if array.shape[axis] == 0 and any(width_pair): raise ValueError( "can't extend empty axis {} using modes other than " "'constant' or 'empty'".format(axis)) # passed, don't need to do anything more as _pad_simple already # returned the correct result elif mode == "edge": for axis, width_pair in zip(axes, pad_width): roi = _view_roi(padded, original_area_slice, axis) edge_pair = _get_edges(roi, axis, width_pair) _set_pad_area(roi, axis, width_pair, edge_pair) elif mode == "linear_ramp": end_values = kwargs.get("end_values", 0) end_values = _as_pairs(end_values, padded.ndim) for axis, width_pair, value_pair in zip(axes, pad_width, end_values): roi = _view_roi(padded, original_area_slice, axis) ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair) _set_pad_area(roi, axis, width_pair, ramp_pair) elif mode in stat_functions: func = stat_functions[mode] length = kwargs.get("stat_length", None) length = _as_pairs(length, padded.ndim, as_index=True) for axis, width_pair, length_pair in zip(axes, pad_width, length): roi = _view_roi(padded, original_area_slice, axis) stat_pair = _get_stats(roi, axis, width_pair, length_pair, func) _set_pad_area(roi, axis, width_pair, stat_pair) elif mode in {"reflect", "symmetric"}: method = kwargs.get("reflect_type", "even") include_edge = True if mode == "symmetric" else False for axis, (left_index, right_index) in zip(axes, pad_width): if array.shape[axis] == 1 and (left_index > 0 or right_index > 0): # Extending singleton dimension for 'reflect' is legacy # behavior; it really should raise an error. edge_pair = _get_edges(padded, axis, (left_index, right_index)) _set_pad_area(padded, axis, (left_index, right_index), edge_pair) continue roi = _view_roi(padded, original_area_slice, axis) while left_index > 0 or right_index > 0: # Iteratively pad until dimension is filled with reflected # values. This is necessary if the pad area is larger than # the length of the original values in the current dimension. left_index, right_index = _set_reflect_both( roi, axis, (left_index, right_index), method, include_edge) elif mode == "wrap": for axis, (left_index, right_index) in zip(axes, pad_width): roi = _view_roi(padded, original_area_slice, axis) while left_index > 0 or right_index > 0: # Iteratively pad until dimension is filled with wrapped # values. This is necessary if the pad area is larger than # the length of the original values in the current dimension. left_index, right_index = _set_wrap_both( roi, axis, (left_index, right_index)) return padded
def apply_along_axis(func1d, axis, arr, *args, **kwargs): """ Apply a function to 1-D slices along the given axis. Execute `func1d(a, *args)` where `func1d` operates on 1-D arrays and `a` is a 1-D slice of `arr` along `axis`. Parameters ---------- func1d : function This function should accept 1-D arrays. It is applied to 1-D slices of `arr` along the specified axis. axis : integer Axis along which `arr` is sliced. arr : ndarray Input array. args : any Additional arguments to `func1d`. kwargs : any Additional named arguments to `func1d`. .. versionadded:: 1.9.0 Returns ------- apply_along_axis : ndarray The output array. The shape of `outarr` is identical to the shape of `arr`, except along the `axis` dimension. This axis is removed, and replaced with new dimensions equal to the shape of the return value of `func1d`. So if `func1d` returns a scalar `outarr` will have one fewer dimensions than `arr`. See Also -------- apply_over_axes : Apply a function repeatedly over multiple axes. Examples -------- >>> def my_func(a): ... \"\"\"Average first and last element of a 1-D array\"\"\" ... return (a[0] + a[-1]) * 0.5 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(my_func, 0, b) array([ 4., 5., 6.]) >>> np.apply_along_axis(my_func, 1, b) array([ 2., 5., 8.]) For a function that returns a 1D array, the number of dimensions in `outarr` is the same as `arr`. >>> b = np.array([[8,1,7], [4,3,9], [5,2,6]]) >>> np.apply_along_axis(sorted, 1, b) array([[1, 7, 8], [3, 4, 9], [2, 5, 6]]) For a function that returns a higher dimensional array, those dimensions are inserted in place of the `axis` dimension. >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(np.diag, -1, b) array([[[1, 0, 0], [0, 2, 0], [0, 0, 3]], [[4, 0, 0], [0, 5, 0], [0, 0, 6]], [[7, 0, 0], [0, 8, 0], [0, 0, 9]]]) """ # handle negative axes arr = asanyarray(arr) nd = arr.ndim if not (-nd <= axis < nd): raise IndexError('axis {0} out of bounds [-{1}, {1})'.format(axis, nd)) if axis < 0: axis += nd # arr, with the iteration axis at the end in_dims = list(range(nd)) inarr_view = transpose(arr, in_dims[:axis] + in_dims[axis+1:] + [axis]) # compute indices for the iteration axes inds = ndindex(inarr_view.shape[:-1]) # invoke the function on the first item try: ind0 = next(inds) except StopIteration: raise ValueError('Cannot apply_along_axis when any iteration dimensions are 0') res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs)) # build a buffer for storing evaluations of func1d. # remove the requested axis, and add the new ones on the end. # laid out so that each write is contiguous. # for a tuple index inds, buff[inds] = func1d(inarr_view[inds]) buff = zeros(inarr_view.shape[:-1] + res.shape, res.dtype) # permutation of axes such that out = buff.transpose(buff_permute) buff_dims = list(range(buff.ndim)) buff_permute = ( buff_dims[0 : axis] + buff_dims[buff.ndim-res.ndim : buff.ndim] + buff_dims[axis : buff.ndim-res.ndim] ) # matrices have a nasty __array_prepare__ and __array_wrap__ if not isinstance(res, matrix): buff = res.__array_prepare__(buff) # save the first result, then compute and save all remaining results buff[ind0] = res for ind in inds: buff[ind] = asanyarray(func1d(inarr_view[ind], *args, **kwargs)) if not isinstance(res, matrix): # wrap the array, to preserve subclasses buff = res.__array_wrap__(buff) # finally, rotate the inserted axes back to where they belong return transpose(buff, buff_permute) else: # matrices have to be transposed first, because they collapse dimensions! out_arr = transpose(buff, buff_permute) return res.__array_wrap__(out_arr)
def pad(array, pad_width, mode='constant', **kwargs): """ Pad an array. Parameters ---------- array : array_like of rank N The array to pad. pad_width : {sequence, array_like, int} Number of values padded to the edges of each axis. ((before_1, after_1), ... (before_N, after_N)) unique pad widths for each axis. ((before, after),) yields same before and after pad for each axis. (pad,) or int is a shortcut for before = after = pad width for all axes. mode : str or function, optional One of the following string values or a user supplied function. 'constant' (default) Pads with a constant value. 'edge' Pads with the edge values of array. 'linear_ramp' Pads with the linear ramp between end_value and the array edge value. 'maximum' Pads with the maximum value of all or part of the vector along each axis. 'mean' Pads with the mean value of all or part of the vector along each axis. 'median' Pads with the median value of all or part of the vector along each axis. 'minimum' Pads with the minimum value of all or part of the vector along each axis. 'reflect' Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis. 'symmetric' Pads with the reflection of the vector mirrored along the edge of the array. 'wrap' Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning. 'empty' Pads with undefined values. .. versionadded:: 1.17 <function> Padding function, see Notes. stat_length : sequence or int, optional Used in 'maximum', 'mean', 'median', and 'minimum'. Number of values at edge of each axis used to calculate the statistic value. ((before_1, after_1), ... (before_N, after_N)) unique statistic lengths for each axis. ((before, after),) yields same before and after statistic lengths for each axis. (stat_length,) or int is a shortcut for before = after = statistic length for all axes. Default is ``None``, to use the entire axis. constant_values : sequence or scalar, optional Used in 'constant'. The values to set the padded values for each axis. ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants for each axis. ``((before, after),)`` yields same before and after constants for each axis. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. end_values : sequence or scalar, optional Used in 'linear_ramp'. The values used for the ending value of the linear_ramp and that will form the edge of the padded array. ``((before_1, after_1), ... (before_N, after_N))`` unique end values for each axis. ``((before, after),)`` yields same before and after end values for each axis. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. reflect_type : {'even', 'odd'}, optional Used in 'reflect', and 'symmetric'. The 'even' style is the default with an unaltered reflection around the edge value. For the 'odd' style, the extended part of the array is created by subtracting the reflected values from two times the edge value. Returns ------- pad : ndarray Padded array of rank equal to `array` with shape increased according to `pad_width`. Notes ----- .. versionadded:: 1.7.0 For an array with rank greater than 1, some of the padding of later axes is calculated from padding of previous axes. This is easiest to think about with a rank 2 array where the corners of the padded array are calculated by using padded values from the first axis. The padding function, if used, should modify a rank 1 array in-place. It has the following signature:: padding_func(vector, iaxis_pad_width, iaxis, kwargs) where vector : ndarray A rank 1 array already padded with zeros. Padded values are vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:]. iaxis_pad_width : tuple A 2-tuple of ints, iaxis_pad_width[0] represents the number of values padded at the beginning of vector where iaxis_pad_width[1] represents the number of values padded at the end of vector. iaxis : int The axis currently being calculated. kwargs : dict Any keyword arguments the function requires. Examples -------- >>> a = [1, 2, 3, 4, 5] >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6)) array([4, 4, 1, ..., 6, 6, 6]) >>> np.pad(a, (2, 3), 'edge') array([1, 1, 1, ..., 5, 5, 5]) >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4)) array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) >>> np.pad(a, (2,), 'maximum') array([5, 5, 1, 2, 3, 4, 5, 5, 5]) >>> np.pad(a, (2,), 'mean') array([3, 3, 1, 2, 3, 4, 5, 3, 3]) >>> np.pad(a, (2,), 'median') array([3, 3, 1, 2, 3, 4, 5, 3, 3]) >>> a = [[1, 2], [3, 4]] >>> np.pad(a, ((3, 2), (2, 3)), 'minimum') array([[1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1], [3, 3, 3, 4, 3, 3, 3], [1, 1, 1, 2, 1, 1, 1], [1, 1, 1, 2, 1, 1, 1]]) >>> a = [1, 2, 3, 4, 5] >>> np.pad(a, (2, 3), 'reflect') array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]) >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd') array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> np.pad(a, (2, 3), 'symmetric') array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]) >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd') array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]) >>> np.pad(a, (2, 3), 'wrap') array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]) >>> def pad_with(vector, pad_width, iaxis, kwargs): ... pad_value = kwargs.get('padder', 10) ... vector[:pad_width[0]] = pad_value ... vector[-pad_width[1]:] = pad_value >>> a = np.arange(6) >>> a = a.reshape((2, 3)) >>> np.pad(a, 2, pad_with) array([[10, 10, 10, 10, 10, 10, 10], [10, 10, 10, 10, 10, 10, 10], [10, 10, 0, 1, 2, 10, 10], [10, 10, 3, 4, 5, 10, 10], [10, 10, 10, 10, 10, 10, 10], [10, 10, 10, 10, 10, 10, 10]]) >>> np.pad(a, 2, pad_with, padder=100) array([[100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100], [100, 100, 0, 1, 2, 100, 100], [100, 100, 3, 4, 5, 100, 100], [100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100]]) """ array = np.asarray(array) pad_width = np.asarray(pad_width) if not pad_width.dtype.kind == 'i': raise TypeError('`pad_width` must be of integral type.') # Broadcast to shape (array.ndim, 2) pad_width = _as_pairs(pad_width, array.ndim, as_index=True) if callable(mode): # Old behavior: Use user-supplied function with np.apply_along_axis function = mode # Create a new zero padded array padded, _ = _pad_simple(array, pad_width, fill_value=0) # And apply along each axis for axis in range(padded.ndim): # Iterate using ndindex as in apply_along_axis, but assuming that # function operates inplace on the padded array. # view with the iteration axis at the end view = np.moveaxis(padded, axis, -1) # compute indices for the iteration axes, and append a trailing # ellipsis to prevent 0d arrays decaying to scalars (gh-8642) inds = ndindex(view.shape[:-1]) inds = (ind + (Ellipsis,) for ind in inds) for ind in inds: function(view[ind], pad_width[axis], axis, kwargs) return padded # Make sure that no unsupported keywords were passed for the current mode allowed_kwargs = { 'empty': [], 'edge': [], 'wrap': [], 'constant': ['constant_values'], 'linear_ramp': ['end_values'], 'maximum': ['stat_length'], 'mean': ['stat_length'], 'median': ['stat_length'], 'minimum': ['stat_length'], 'reflect': ['reflect_type'], 'symmetric': ['reflect_type'], } try: unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode]) except KeyError: raise ValueError("mode '{}' is not supported".format(mode)) if unsupported_kwargs: raise ValueError("unsupported keyword arguments for mode '{}': {}" .format(mode, unsupported_kwargs)) stat_functions = {"maximum": np.max, "minimum": np.min, "mean": np.mean, "median": np.median} # Create array with final shape and original values # (padded area is undefined) padded, original_area_slice = _pad_simple(array, pad_width) # And prepare iteration over all dimensions # (zipping may be more readable than using enumerate) axes = range(padded.ndim) if mode == "constant": values = kwargs.get("constant_values", 0) values = _as_pairs(values, padded.ndim) for axis, width_pair, value_pair in zip(axes, pad_width, values): roi = _view_roi(padded, original_area_slice, axis) _set_pad_area(roi, axis, width_pair, value_pair) elif mode == "empty": pass # Do nothing as _pad_simple already returned the correct result elif array.size == 0: # Only modes "constant" and "empty" can extend empty axes, all other # modes depend on `array` not being empty # -> ensure every empty axis is only "padded with 0" for axis, width_pair in zip(axes, pad_width): if array.shape[axis] == 0 and any(width_pair): raise ValueError( "can't extend empty axis {} using modes other than " "'constant' or 'empty'".format(axis) ) # passed, don't need to do anything more as _pad_simple already # returned the correct result elif mode == "edge": for axis, width_pair in zip(axes, pad_width): roi = _view_roi(padded, original_area_slice, axis) edge_pair = _get_edges(roi, axis, width_pair) _set_pad_area(roi, axis, width_pair, edge_pair) elif mode == "linear_ramp": end_values = kwargs.get("end_values", 0) end_values = _as_pairs(end_values, padded.ndim) for axis, width_pair, value_pair in zip(axes, pad_width, end_values): roi = _view_roi(padded, original_area_slice, axis) ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair) _set_pad_area(roi, axis, width_pair, ramp_pair) elif mode in stat_functions: func = stat_functions[mode] length = kwargs.get("stat_length", None) length = _as_pairs(length, padded.ndim, as_index=True) for axis, width_pair, length_pair in zip(axes, pad_width, length): roi = _view_roi(padded, original_area_slice, axis) stat_pair = _get_stats(roi, axis, width_pair, length_pair, func) _set_pad_area(roi, axis, width_pair, stat_pair) elif mode in {"reflect", "symmetric"}: method = kwargs.get("reflect_type", "even") include_edge = True if mode == "symmetric" else False for axis, (left_index, right_index) in zip(axes, pad_width): if array.shape[axis] == 1 and (left_index > 0 or right_index > 0): # Extending singleton dimension for 'reflect' is legacy # behavior; it really should raise an error. edge_pair = _get_edges(padded, axis, (left_index, right_index)) _set_pad_area( padded, axis, (left_index, right_index), edge_pair) continue roi = _view_roi(padded, original_area_slice, axis) while left_index > 0 or right_index > 0: # Iteratively pad until dimension is filled with reflected # values. This is necessary if the pad area is larger than # the length of the original values in the current dimension. left_index, right_index = _set_reflect_both( roi, axis, (left_index, right_index), method, include_edge ) elif mode == "wrap": for axis, (left_index, right_index) in zip(axes, pad_width): roi = _view_roi(padded, original_area_slice, axis) while left_index > 0 or right_index > 0: # Iteratively pad until dimension is filled with wrapped # values. This is necessary if the pad area is larger than # the length of the original values in the current dimension. left_index, right_index = _set_wrap_both( roi, axis, (left_index, right_index)) return padded