Spaces:
Paused
Paused
| """ | |
| Utilities that manipulate strides to achieve desirable effects. | |
| An explanation of strides can be found in the "ndarray.rst" file in the | |
| NumPy reference guide. | |
| """ | |
| import numpy as np | |
| from numpy.core.numeric import normalize_axis_tuple | |
| from numpy.core.overrides import array_function_dispatch, set_module | |
| __all__ = ['broadcast_to', 'broadcast_arrays', 'broadcast_shapes'] | |
| class DummyArray: | |
| """Dummy object that just exists to hang __array_interface__ dictionaries | |
| and possibly keep alive a reference to a base array. | |
| """ | |
| def __init__(self, interface, base=None): | |
| self.__array_interface__ = interface | |
| self.base = base | |
| def _maybe_view_as_subclass(original_array, new_array): | |
| if type(original_array) is not type(new_array): | |
| # if input was an ndarray subclass and subclasses were OK, | |
| # then view the result as that subclass. | |
| new_array = new_array.view(type=type(original_array)) | |
| # Since we have done something akin to a view from original_array, we | |
| # should let the subclass finalize (if it has it implemented, i.e., is | |
| # not None). | |
| if new_array.__array_finalize__: | |
| new_array.__array_finalize__(original_array) | |
| return new_array | |
| def as_strided(x, shape=None, strides=None, subok=False, writeable=True): | |
| """ | |
| Create a view into the array with the given shape and strides. | |
| .. warning:: This function has to be used with extreme care, see notes. | |
| Parameters | |
| ---------- | |
| x : ndarray | |
| Array to create a new. | |
| shape : sequence of int, optional | |
| The shape of the new array. Defaults to ``x.shape``. | |
| strides : sequence of int, optional | |
| The strides of the new array. Defaults to ``x.strides``. | |
| subok : bool, optional | |
| .. versionadded:: 1.10 | |
| If True, subclasses are preserved. | |
| writeable : bool, optional | |
| .. versionadded:: 1.12 | |
| If set to False, the returned array will always be readonly. | |
| Otherwise it will be writable if the original array was. It | |
| is advisable to set this to False if possible (see Notes). | |
| Returns | |
| ------- | |
| view : ndarray | |
| See also | |
| -------- | |
| broadcast_to : broadcast an array to a given shape. | |
| reshape : reshape an array. | |
| lib.stride_tricks.sliding_window_view : | |
| userfriendly and safe function for the creation of sliding window views. | |
| Notes | |
| ----- | |
| ``as_strided`` creates a view into the array given the exact strides | |
| and shape. This means it manipulates the internal data structure of | |
| ndarray and, if done incorrectly, the array elements can point to | |
| invalid memory and can corrupt results or crash your program. | |
| It is advisable to always use the original ``x.strides`` when | |
| calculating new strides to avoid reliance on a contiguous memory | |
| layout. | |
| Furthermore, arrays created with this function often contain self | |
| overlapping memory, so that two elements are identical. | |
| Vectorized write operations on such arrays will typically be | |
| unpredictable. They may even give different results for small, large, | |
| or transposed arrays. | |
| Since writing to these arrays has to be tested and done with great | |
| care, you may want to use ``writeable=False`` to avoid accidental write | |
| operations. | |
| For these reasons it is advisable to avoid ``as_strided`` when | |
| possible. | |
| """ | |
| # first convert input to array, possibly keeping subclass | |
| x = np.array(x, copy=False, subok=subok) | |
| interface = dict(x.__array_interface__) | |
| if shape is not None: | |
| interface['shape'] = tuple(shape) | |
| if strides is not None: | |
| interface['strides'] = tuple(strides) | |
| array = np.asarray(DummyArray(interface, base=x)) | |
| # The route via `__interface__` does not preserve structured | |
| # dtypes. Since dtype should remain unchanged, we set it explicitly. | |
| array.dtype = x.dtype | |
| view = _maybe_view_as_subclass(x, array) | |
| if view.flags.writeable and not writeable: | |
| view.flags.writeable = False | |
| return view | |
| def _sliding_window_view_dispatcher(x, window_shape, axis=None, *, | |
| subok=None, writeable=None): | |
| return (x,) | |
| def sliding_window_view(x, window_shape, axis=None, *, | |
| subok=False, writeable=False): | |
| """ | |
| Create a sliding window view into the array with the given window shape. | |
| Also known as rolling or moving window, the window slides across all | |
| dimensions of the array and extracts subsets of the array at all window | |
| positions. | |
| .. versionadded:: 1.20.0 | |
| Parameters | |
| ---------- | |
| x : array_like | |
| Array to create the sliding window view from. | |
| window_shape : int or tuple of int | |
| Size of window over each axis that takes part in the sliding window. | |
| If `axis` is not present, must have same length as the number of input | |
| array dimensions. Single integers `i` are treated as if they were the | |
| tuple `(i,)`. | |
| axis : int or tuple of int, optional | |
| Axis or axes along which the sliding window is applied. | |
| By default, the sliding window is applied to all axes and | |
| `window_shape[i]` will refer to axis `i` of `x`. | |
| If `axis` is given as a `tuple of int`, `window_shape[i]` will refer to | |
| the axis `axis[i]` of `x`. | |
| Single integers `i` are treated as if they were the tuple `(i,)`. | |
| subok : bool, optional | |
| If True, sub-classes will be passed-through, otherwise the returned | |
| array will be forced to be a base-class array (default). | |
| writeable : bool, optional | |
| When true, allow writing to the returned view. The default is false, | |
| as this should be used with caution: the returned view contains the | |
| same memory location multiple times, so writing to one location will | |
| cause others to change. | |
| Returns | |
| ------- | |
| view : ndarray | |
| Sliding window view of the array. The sliding window dimensions are | |
| inserted at the end, and the original dimensions are trimmed as | |
| required by the size of the sliding window. | |
| That is, ``view.shape = x_shape_trimmed + window_shape``, where | |
| ``x_shape_trimmed`` is ``x.shape`` with every entry reduced by one less | |
| than the corresponding window size. | |
| See Also | |
| -------- | |
| lib.stride_tricks.as_strided: A lower-level and less safe routine for | |
| creating arbitrary views from custom shape and strides. | |
| broadcast_to: broadcast an array to a given shape. | |
| Notes | |
| ----- | |
| For many applications using a sliding window view can be convenient, but | |
| potentially very slow. Often specialized solutions exist, for example: | |
| - `scipy.signal.fftconvolve` | |
| - filtering functions in `scipy.ndimage` | |
| - moving window functions provided by | |
| `bottleneck <https://github.com/pydata/bottleneck>`_. | |
| As a rough estimate, a sliding window approach with an input size of `N` | |
| and a window size of `W` will scale as `O(N*W)` where frequently a special | |
| algorithm can achieve `O(N)`. That means that the sliding window variant | |
| for a window size of 100 can be a 100 times slower than a more specialized | |
| version. | |
| Nevertheless, for small window sizes, when no custom algorithm exists, or | |
| as a prototyping and developing tool, this function can be a good solution. | |
| Examples | |
| -------- | |
| >>> x = np.arange(6) | |
| >>> x.shape | |
| (6,) | |
| >>> v = sliding_window_view(x, 3) | |
| >>> v.shape | |
| (4, 3) | |
| >>> v | |
| array([[0, 1, 2], | |
| [1, 2, 3], | |
| [2, 3, 4], | |
| [3, 4, 5]]) | |
| This also works in more dimensions, e.g. | |
| >>> i, j = np.ogrid[:3, :4] | |
| >>> x = 10*i + j | |
| >>> x.shape | |
| (3, 4) | |
| >>> x | |
| array([[ 0, 1, 2, 3], | |
| [10, 11, 12, 13], | |
| [20, 21, 22, 23]]) | |
| >>> shape = (2,2) | |
| >>> v = sliding_window_view(x, shape) | |
| >>> v.shape | |
| (2, 3, 2, 2) | |
| >>> v | |
| array([[[[ 0, 1], | |
| [10, 11]], | |
| [[ 1, 2], | |
| [11, 12]], | |
| [[ 2, 3], | |
| [12, 13]]], | |
| [[[10, 11], | |
| [20, 21]], | |
| [[11, 12], | |
| [21, 22]], | |
| [[12, 13], | |
| [22, 23]]]]) | |
| The axis can be specified explicitly: | |
| >>> v = sliding_window_view(x, 3, 0) | |
| >>> v.shape | |
| (1, 4, 3) | |
| >>> v | |
| array([[[ 0, 10, 20], | |
| [ 1, 11, 21], | |
| [ 2, 12, 22], | |
| [ 3, 13, 23]]]) | |
| The same axis can be used several times. In that case, every use reduces | |
| the corresponding original dimension: | |
| >>> v = sliding_window_view(x, (2, 3), (1, 1)) | |
| >>> v.shape | |
| (3, 1, 2, 3) | |
| >>> v | |
| array([[[[ 0, 1, 2], | |
| [ 1, 2, 3]]], | |
| [[[10, 11, 12], | |
| [11, 12, 13]]], | |
| [[[20, 21, 22], | |
| [21, 22, 23]]]]) | |
| Combining with stepped slicing (`::step`), this can be used to take sliding | |
| views which skip elements: | |
| >>> x = np.arange(7) | |
| >>> sliding_window_view(x, 5)[:, ::2] | |
| array([[0, 2, 4], | |
| [1, 3, 5], | |
| [2, 4, 6]]) | |
| or views which move by multiple elements | |
| >>> x = np.arange(7) | |
| >>> sliding_window_view(x, 3)[::2, :] | |
| array([[0, 1, 2], | |
| [2, 3, 4], | |
| [4, 5, 6]]) | |
| A common application of `sliding_window_view` is the calculation of running | |
| statistics. The simplest example is the | |
| `moving average <https://en.wikipedia.org/wiki/Moving_average>`_: | |
| >>> x = np.arange(6) | |
| >>> x.shape | |
| (6,) | |
| >>> v = sliding_window_view(x, 3) | |
| >>> v.shape | |
| (4, 3) | |
| >>> v | |
| array([[0, 1, 2], | |
| [1, 2, 3], | |
| [2, 3, 4], | |
| [3, 4, 5]]) | |
| >>> moving_average = v.mean(axis=-1) | |
| >>> moving_average | |
| array([1., 2., 3., 4.]) | |
| Note that a sliding window approach is often **not** optimal (see Notes). | |
| """ | |
| window_shape = (tuple(window_shape) | |
| if np.iterable(window_shape) | |
| else (window_shape,)) | |
| # first convert input to array, possibly keeping subclass | |
| x = np.array(x, copy=False, subok=subok) | |
| window_shape_array = np.array(window_shape) | |
| if np.any(window_shape_array < 0): | |
| raise ValueError('`window_shape` cannot contain negative values') | |
| if axis is None: | |
| axis = tuple(range(x.ndim)) | |
| if len(window_shape) != len(axis): | |
| raise ValueError(f'Since axis is `None`, must provide ' | |
| f'window_shape for all dimensions of `x`; ' | |
| f'got {len(window_shape)} window_shape elements ' | |
| f'and `x.ndim` is {x.ndim}.') | |
| else: | |
| axis = normalize_axis_tuple(axis, x.ndim, allow_duplicate=True) | |
| if len(window_shape) != len(axis): | |
| raise ValueError(f'Must provide matching length window_shape and ' | |
| f'axis; got {len(window_shape)} window_shape ' | |
| f'elements and {len(axis)} axes elements.') | |
| out_strides = x.strides + tuple(x.strides[ax] for ax in axis) | |
| # note: same axis can be windowed repeatedly | |
| x_shape_trimmed = list(x.shape) | |
| for ax, dim in zip(axis, window_shape): | |
| if x_shape_trimmed[ax] < dim: | |
| raise ValueError( | |
| 'window shape cannot be larger than input array shape') | |
| x_shape_trimmed[ax] -= dim - 1 | |
| out_shape = tuple(x_shape_trimmed) + window_shape | |
| return as_strided(x, strides=out_strides, shape=out_shape, | |
| subok=subok, writeable=writeable) | |
| def _broadcast_to(array, shape, subok, readonly): | |
| shape = tuple(shape) if np.iterable(shape) else (shape,) | |
| array = np.array(array, copy=False, subok=subok) | |
| if not shape and array.shape: | |
| raise ValueError('cannot broadcast a non-scalar to a scalar array') | |
| if any(size < 0 for size in shape): | |
| raise ValueError('all elements of broadcast shape must be non-' | |
| 'negative') | |
| extras = [] | |
| it = np.nditer( | |
| (array,), flags=['multi_index', 'refs_ok', 'zerosize_ok'] + extras, | |
| op_flags=['readonly'], itershape=shape, order='C') | |
| with it: | |
| # never really has writebackifcopy semantics | |
| broadcast = it.itviews[0] | |
| result = _maybe_view_as_subclass(array, broadcast) | |
| # In a future version this will go away | |
| if not readonly and array.flags._writeable_no_warn: | |
| result.flags.writeable = True | |
| result.flags._warn_on_write = True | |
| return result | |
| def _broadcast_to_dispatcher(array, shape, subok=None): | |
| return (array,) | |
| def broadcast_to(array, shape, subok=False): | |
| """Broadcast an array to a new shape. | |
| Parameters | |
| ---------- | |
| array : array_like | |
| The array to broadcast. | |
| shape : tuple | |
| The shape of the desired array. | |
| subok : bool, optional | |
| If True, then sub-classes will be passed-through, otherwise | |
| the returned array will be forced to be a base-class array (default). | |
| Returns | |
| ------- | |
| broadcast : array | |
| A readonly view on the original array with the given shape. It is | |
| typically not contiguous. Furthermore, more than one element of a | |
| broadcasted array may refer to a single memory location. | |
| Raises | |
| ------ | |
| ValueError | |
| If the array is not compatible with the new shape according to NumPy's | |
| broadcasting rules. | |
| See Also | |
| -------- | |
| broadcast | |
| broadcast_arrays | |
| broadcast_shapes | |
| Notes | |
| ----- | |
| .. versionadded:: 1.10.0 | |
| Examples | |
| -------- | |
| >>> x = np.array([1, 2, 3]) | |
| >>> np.broadcast_to(x, (3, 3)) | |
| array([[1, 2, 3], | |
| [1, 2, 3], | |
| [1, 2, 3]]) | |
| """ | |
| return _broadcast_to(array, shape, subok=subok, readonly=True) | |
| def _broadcast_shape(*args): | |
| """Returns the shape of the arrays that would result from broadcasting the | |
| supplied arrays against each other. | |
| """ | |
| # use the old-iterator because np.nditer does not handle size 0 arrays | |
| # consistently | |
| b = np.broadcast(*args[:32]) | |
| # unfortunately, it cannot handle 32 or more arguments directly | |
| for pos in range(32, len(args), 31): | |
| # ironically, np.broadcast does not properly handle np.broadcast | |
| # objects (it treats them as scalars) | |
| # use broadcasting to avoid allocating the full array | |
| b = broadcast_to(0, b.shape) | |
| b = np.broadcast(b, *args[pos:(pos + 31)]) | |
| return b.shape | |
| def broadcast_shapes(*args): | |
| """ | |
| Broadcast the input shapes into a single shape. | |
| :ref:`Learn more about broadcasting here <basics.broadcasting>`. | |
| .. versionadded:: 1.20.0 | |
| Parameters | |
| ---------- | |
| `*args` : tuples of ints, or ints | |
| The shapes to be broadcast against each other. | |
| Returns | |
| ------- | |
| tuple | |
| Broadcasted shape. | |
| Raises | |
| ------ | |
| ValueError | |
| If the shapes are not compatible and cannot be broadcast according | |
| to NumPy's broadcasting rules. | |
| See Also | |
| -------- | |
| broadcast | |
| broadcast_arrays | |
| broadcast_to | |
| Examples | |
| -------- | |
| >>> np.broadcast_shapes((1, 2), (3, 1), (3, 2)) | |
| (3, 2) | |
| >>> np.broadcast_shapes((6, 7), (5, 6, 1), (7,), (5, 1, 7)) | |
| (5, 6, 7) | |
| """ | |
| arrays = [np.empty(x, dtype=[]) for x in args] | |
| return _broadcast_shape(*arrays) | |
| def _broadcast_arrays_dispatcher(*args, subok=None): | |
| return args | |
| def broadcast_arrays(*args, subok=False): | |
| """ | |
| Broadcast any number of arrays against each other. | |
| Parameters | |
| ---------- | |
| `*args` : array_likes | |
| The arrays to broadcast. | |
| subok : bool, optional | |
| If True, then sub-classes will be passed-through, otherwise | |
| the returned arrays will be forced to be a base-class array (default). | |
| Returns | |
| ------- | |
| broadcasted : list of arrays | |
| These arrays are views on the original arrays. They are typically | |
| not contiguous. Furthermore, more than one element of a | |
| broadcasted array may refer to a single memory location. If you need | |
| to write to the arrays, make copies first. While you can set the | |
| ``writable`` flag True, writing to a single output value may end up | |
| changing more than one location in the output array. | |
| .. deprecated:: 1.17 | |
| The output is currently marked so that if written to, a deprecation | |
| warning will be emitted. A future version will set the | |
| ``writable`` flag False so writing to it will raise an error. | |
| See Also | |
| -------- | |
| broadcast | |
| broadcast_to | |
| broadcast_shapes | |
| Examples | |
| -------- | |
| >>> x = np.array([[1,2,3]]) | |
| >>> y = np.array([[4],[5]]) | |
| >>> np.broadcast_arrays(x, y) | |
| [array([[1, 2, 3], | |
| [1, 2, 3]]), array([[4, 4, 4], | |
| [5, 5, 5]])] | |
| Here is a useful idiom for getting contiguous copies instead of | |
| non-contiguous views. | |
| >>> [np.array(a) for a in np.broadcast_arrays(x, y)] | |
| [array([[1, 2, 3], | |
| [1, 2, 3]]), array([[4, 4, 4], | |
| [5, 5, 5]])] | |
| """ | |
| # nditer is not used here to avoid the limit of 32 arrays. | |
| # Otherwise, something like the following one-liner would suffice: | |
| # return np.nditer(args, flags=['multi_index', 'zerosize_ok'], | |
| # order='C').itviews | |
| args = [np.array(_m, copy=False, subok=subok) for _m in args] | |
| shape = _broadcast_shape(*args) | |
| if all(array.shape == shape for array in args): | |
| # Common case where nothing needs to be broadcasted. | |
| return args | |
| return [_broadcast_to(array, shape, subok=subok, readonly=False) | |
| for array in args] | |