Module thunderhopper.arraytools
Functions
def is_valid_numpy_index(ind)
-
Expand source code
def is_valid_numpy_index(ind): """ Checks if the given variable is valid for indexing numpy arrays. Valid array indices can be integers, bools, arrays of ints or bools, slice objects, ellipsis, None, or tuples of a combination of those. Parameters ---------- ind : any type (any shape) The variable to validate as numpy array index. Returns ------- valid : bool Returns True if ind can be used to index numpy arrays, False otherwise. """ # Force dimension specificity: if not isinstance(ind, tuple): ind = (ind,) for dim in ind: # Validate indices along each array dimension separately: data_type = type(dim) if not isinstance(dim, np.ndarray) else dim.dtype is_int = np.issubdtype(data_type, np.integer) is_bool = np.issubdtype(data_type, np.bool_) is_slice = isinstance(dim, slice) # Type violation early exit: if not any([is_int, is_bool, is_slice, dim is Ellipsis, dim is None]): return False return True
Checks if the given variable is valid for indexing numpy arrays. Valid array indices can be integers, bools, arrays of ints or bools, slice objects, ellipsis, None, or tuples of a combination of those.
Parameters
ind
:any type (any shape)
- The variable to validate as numpy array index.
Returns
valid
:bool
- Returns True if ind can be used to index numpy arrays, False otherwise.
def broadcastable(*shapes, vars=None, crash=False, output=True)
-
Expand source code
def broadcastable(*shapes, vars=None, crash=False, output=True): """ Checks if the given array shapes are broadcastable to a common shape. Upon failure, can either return normally or immediately raise an error. Can return the common shape expected from operations with these arrays. Parameters ---------- shapes : array-like (m,) of array-likes of ints, optional Array shapes to be validated. The default is None. vars : array-like (m,) of arrays, floats or ints, optional Variables whose shapes are to be validated. Overrides shapes if given. Only accepts ndarrays and scalars. The default is None. crash : bool, optional If True, failed broadcasting raises a ValueError, while successful broadcasting returns shape without valid. If False, always returns valid, together with shape if output is True. The default is False. output : bool, optional If True, returns the common shape of the arrays in addition to valid. Ignored if crash is True. The default is True. Returns ------- valid : bool Returns True if all array shapes are broadcastable to a common shape. Only returned if crash is False. shape : tuple of ints, optional Array shape resulting from broadcasting. Bullshit if valid is False. Only returned if output is True. """ # Input interpretation: if vars is not None: shapes = [(1,) if np.isscalar(var) else var.shape for var in vars] # Check array dimensionality: dims = np.array([len(shape) for shape in shapes]) reference = np.array(shapes[np.argmax(dims)]) # Align shapes by trailing dimensions: aligned = np.ones((dims.size, dims.max()), dtype=int) for i, shape in enumerate(shapes): aligned[i, -dims[i]:] = shape # Check if sizes of each dimension are compatible: valid = ((aligned == reference) | (aligned == 1) | (reference == 1)).all() common_shape = tuple(aligned.max(axis=0)) # Return options: if crash and not valid: msg = f'Array shapes {string_series(shapes, delimit=" ")} '\ 'are not broadcastable to a common shape.' raise ValueError(msg) elif crash: return common_shape return (valid, common_shape) if output else valid
Checks if the given array shapes are broadcastable to a common shape. Upon failure, can either return normally or immediately raise an error. Can return the common shape expected from operations with these arrays.
Parameters
shapes
:array-like (m,)
ofarray-likes
ofints
, optional- Array shapes to be validated. The default is None.
vars
:array-like (m,)
ofarrays, floats
orints
, optional- Variables whose shapes are to be validated. Overrides shapes if given. Only accepts ndarrays and scalars. The default is None.
crash
:bool
, optional- If True, failed broadcasting raises a ValueError, while successful broadcasting returns shape without valid. If False, always returns valid, together with shape if output is True. The default is False.
output
:bool
, optional- If True, returns the common shape of the arrays in addition to valid. Ignored if crash is True. The default is True.
Returns
valid
:bool
- Returns True if all array shapes are broadcastable to a common shape. Only returned if crash is False.
shape
:tuple
ofints
, optional- Array shape resulting from broadcasting. Bullshit if valid is False. Only returned if output is True.
def reduce_array(array, squeeze=False, unpack=False)
-
Expand source code
def reduce_array(array, squeeze=False, unpack=False): """ Wrapper to quickly squeeze and itemize numpy arrays, if possible. Allows to remove singleton dimensions (squeeze, maintains at least 1D) and to convert single-entry arrays into scalar output (unpack). Parameters ---------- array : ND-array of arbitrary type and shape Array to be shape-checked and reduced to simpler formats, if possible. Must be at least 1D. Array is not copied or modified by this function. squeeze : bool, optional If True, calls np.squeeze() on array to remove dimensions of size 1. If array is size 1, retains a single dimension to prevent 0D output. The default is False. unpack : bool, optional If True and output would be an array of size 1, converts the array into a single scalar. Applied after squeezing. The default is False. Returns ------- array : 1D...ND-array or scalar of array.dtype View of input array after dimensional pruning and scalar conversion. If squeeze is True, may have fewer dimensions (down to 1D). If unpack is True, may be converted to a scalar. If input array has no singleton dimensions and is not size 1, the output is the same as the input. If squeeze and unpack are both False, immediately returns the input array. """ # Optional dimensional pruning: if squeeze and array.ndim > 1: # Remove all singleton dimensions, retaining at least 1D: axes = (ind for ind, size in enumerate(array.shape)[1:] if size == 1) array = np.squeeze(array, axis=axes) # Optional scalar conversion of single-entry 1D arrays: return array.item() if (unpack and array.size == 1) else array
Wrapper to quickly squeeze and itemize numpy arrays, if possible. Allows to remove singleton dimensions (squeeze, maintains at least 1D) and to convert single-entry arrays into scalar output (unpack).
Parameters
array
:ND-array
ofarbitrary type and shape
- Array to be shape-checked and reduced to simpler formats, if possible. Must be at least 1D. Array is not copied or modified by this function.
squeeze
:bool
, optional- If True, calls np.squeeze() on array to remove dimensions of size 1. If array is size 1, retains a single dimension to prevent 0D output. The default is False.
unpack
:bool
, optional- If True and output would be an array of size 1, converts the array into a single scalar. Applied after squeezing. The default is False.
Returns
array
:1D...ND-array
orscalar
ofarray.dtype
- View of input array after dimensional pruning and scalar conversion. If squeeze is True, may have fewer dimensions (down to 1D). If unpack is True, may be converted to a scalar. If input array has no singleton dimensions and is not size 1, the output is the same as the input. If squeeze and unpack are both False, immediately returns the input array.
def slice_index(dims, axis=0, start=None, stop=None, step=1, include=False)
-
Expand source code
def slice_index(dims, axis=0, start=None, stop=None, step=1, include=False): """ Indices to slice an N-dimensional array along one or multiple axes. Shamelessly stolen and adapted from scipy.signal._arraytools. Parameters ---------- dims : int Number of dimensions of the target array. axis : int or tuple/list/1D array of ints (m,), optional Target axis or axes along which to slice the array. If several, enables multi-dimensional slicing, also accepting sequence-like inputs for each of start, stop, and step. Single elements apply to all target axes. The default is 0. start : int or tuple/list/1D array of ints (m,), optional Inclusive start index of the slice. The default is None. stop : int or tuple/list/1D array of ints (m,), optional Stop index of the slice. Inclusive if include is True, otherwise exclusive as by standard python doctrine. The default is None. step : int or tuple/list/1D array of ints (m,), optional Step size between indices in the slice. The default is 1. include : bool, optional If True, makes the specified stop the last index of the slice. The default is False. Returns ------- slice_inds : tuple of slice objects (dims,) Indices for slicing along the given target axes. """ # Ensure 1D sequence-likes of equal length: axis, start, stop, step = equal_sequences(axis, start, stop, step, skip_None=False) # Full slice along each dimension: slice_inds = [slice(None)] * dims for ax, first, last, interval in zip(axis, start, stop, step): if include and last is not None: # Ensure inclusive stop: step_sign = np.sign(interval) # Sanitize edge cases (specific combinations of stop and step): if any(np.isin([-1, 0], last) & np.isin([1, -1], step_sign)): last = None else: # One more or less: last += step_sign # Adjust slice along each target axis: slice_inds[ax] = slice(first, last, interval) return tuple(slice_inds)
Indices to slice an N-dimensional array along one or multiple axes. Shamelessly stolen and adapted from scipy.signal._arraytools.
Parameters
dims
:int
- Number of dimensions of the target array.
axis
:int
ortuple/list/1D array
ofints (m,)
, optional- Target axis or axes along which to slice the array. If several, enables multi-dimensional slicing, also accepting sequence-like inputs for each of start, stop, and step. Single elements apply to all target axes. The default is 0.
start
:int
ortuple/list/1D array
ofints (m,)
, optional- Inclusive start index of the slice. The default is None.
stop
:int
ortuple/list/1D array
ofints (m,)
, optional- Stop index of the slice. Inclusive if include is True, otherwise exclusive as by standard python doctrine. The default is None.
step
:int
ortuple/list/1D array
ofints (m,)
, optional- Step size between indices in the slice. The default is 1.
include
:bool
, optional- If True, makes the specified stop the last index of the slice. The default is False.
Returns
slice_inds
:tuple
ofslice objects (dims,)
- Indices for slicing along the given target axes.
def array_slice(array, axis=0, start=None, stop=None, step=1, include=False, squeeze=False, unpack=False)
-
Expand source code
def array_slice(array, axis=0, start=None, stop=None, step=1, include=False, squeeze=False, unpack=False): """ Slices an N-dimensional array along one or multiple target axes. Returns a view of the sliced input array, preserving dimensionality. Sliced arrays can optionally be squeezed to remove excess singleton dimensions. Single-entry arrays can further be converted to scalars. Shamelessly stolen and adapted from scipy.signal._arraytools. Parameters ---------- array : ND-array of arbitrary type and shape Input array to be sliced. axis : int or list of ints (m,), optional Target axis or axes along which to slice the array. If several, enables multi-dimensional slicing, also accepting list inputs for start, stop, and step (integer inputs apply to all target axes). The default is 0. start : int or list of ints (m,), optional Inclusive start index of the slice. The default is None. stop : int or list of ints (m,), optional Stop index of the slice. Inclusive if include is True, otherwise exclusive as by standard python doctrine. The default is None. step : int or list of ints (m,), optional Step size between indices in the slice. The default is 1. include : bool, optional If True, makes the specified stop the last index of the slice. The default is False. squeeze : bool, optional If True, calls np.squeeze() on the sliced array to remove dimensions of size 1, retaining at least a single dimension. The default is False. unpack : bool, optional If True and output would be an array of size 1, converts the array into a single scalar. Applied after squeezing. The default is False. Returns ------- sliced : array of array.dtype or scalar of array.dtype View of the sliced array with as many dimensions as the input array. If squeeze is True, may have less dimensions (but will be at least 1D). If unpack is True, may be a scalar instead of an array. """ slice_inds = slice_index(array.ndim, axis, start, stop, step, include) return reduce_array(array[slice_inds], squeeze, unpack)
Slices an N-dimensional array along one or multiple target axes. Returns a view of the sliced input array, preserving dimensionality. Sliced arrays can optionally be squeezed to remove excess singleton dimensions. Single-entry arrays can further be converted to scalars. Shamelessly stolen and adapted from scipy.signal._arraytools.
Parameters
array
:ND-array
ofarbitrary type and shape
- Input array to be sliced.
axis
:int
orlist
ofints (m,)
, optional- Target axis or axes along which to slice the array. If several, enables multi-dimensional slicing, also accepting list inputs for start, stop, and step (integer inputs apply to all target axes). The default is 0.
start
:int
orlist
ofints (m,)
, optional- Inclusive start index of the slice. The default is None.
stop
:int
orlist
ofints (m,)
, optional- Stop index of the slice. Inclusive if include is True, otherwise exclusive as by standard python doctrine. The default is None.
step
:int
orlist
ofints (m,)
, optional- Step size between indices in the slice. The default is 1.
include
:bool
, optional- If True, makes the specified stop the last index of the slice. The default is False.
squeeze
:bool
, optional- If True, calls np.squeeze() on the sliced array to remove dimensions of size 1, retaining at least a single dimension. The default is False.
unpack
:bool
, optional- If True and output would be an array of size 1, converts the array into a single scalar. Applied after squeezing. The default is False.
Returns
sliced
:array
ofarray.dtype
orscalar
ofarray.dtype
- View of the sliced array with as many dimensions as the input array. If squeeze is True, may have less dimensions (but will be at least 1D). If unpack is True, may be a scalar instead of an array.
def remap_array(array, axes_map, dims=None, shape=None, invert=False, pass_scalars=False, validate=True)
-
Expand source code
def remap_array(array, axes_map, dims=None, shape=None, invert=False, pass_scalars=False, validate=True): """ Freely adds and moves array dimensions without changing the data. Consists of a call to np.expand_dims() to match the required target dimensionality, followed by np.moveaxis() to position the specified input dimensions at the requested output dimensions. Allows to undo a previous reshape operation based on the used axes_map input and the original dimensionality using np.moveaxis() and np.squeeze(). Parameters ---------- array : ND array of any type (any shape) Input array to be reshaped. axes_map : dict {ints: ints} or 1D array-like (>=array.ndim,) of ints Index mapping linking input dimensions to their respective output dimension. If dict, moves only the input dimensions specified by the keys while maintaining order of the remaining dimensions. Otherwise, expects at least as many output dimensions as array.ndim, one for each array dimension in original order, or more to add singleton dimensions. Input dimensions must be unique and in [-array.ndim, array.ndim - 1]. Output dimensions must be unique and in [-dims, dims - 1]. Provided indices may be negative, regardless of input format. dims : int, optional Dimensionality of the output array. If specified while invert is False, appends singleton dimensions until array matches this dimensionality. If specified while invert is True, applies inverse index mapping, then prunes singleton dimensions until array matches this dimensionality. Replaced by shape. If neither is provided, falls back to the highest provided output dimension, the dimensionality of array (invert=False), or the number of non-singleton dimensions in array (invert=True), whatever is larger. The default is None. shape : 1D array-like (>=array.ndim,) of ints, optional Replaces dims by the number of dimensions in shape and executes as usual. If specified while invert is False, validates that the remapped array is broadcast-compatible with shape in every dimension. Functional but not recomended for use while invert is True. The default is None. invert : bool, optional If True, switches to inverse index mapping by swapping the provided input dimensions and output dimensions in order to undo a previous reshape operation by this function that produced the array at hand. Requires axes_map to be the same argument as used previously, and dims to be the number of dimensions in the original array. If False, simply applies the specified index mapping. The default is False. pass_scalars : bool, optional If True, immediately returns scalar array inputs such as ints, floats, bools, None, or 0D arrays. If False, treats scalar inputs as 1D arrays of size 1. The default is False. validate : bool, optional If True, extensively validates the input parameters. If False, skips validation entirely and attempts direct execution. Has no effect on the broadcasting validation triggered by shape. The default is True. Returns ------- array : ND array of any type View on the reshaped array with the required target dimensionality. Raises ------ ValueError Breaks if axes_map specifies duplicate input or output dimensions, if any specified dimension is out of bounds with dims or array.ndim, or if an array-like axes_map is not at least of length array.ndim. Also breaks if the remapped array is not broadcast-compatible (equal axis sizes or one singleton) with the provided shape in all dimensions. """ # Input interpretation: if np.ndim(array) == 0: if pass_scalars: return array # Promote scalar to 1D array: array = np.array(array, ndmin=1) elif not isinstance(array, np.ndarray): # Ensure ND array format: array = np.array(array) if isinstance(axes_map, dict): # Selective mapping of individual dimensions: input_axes, output_axes = zip(*axes_map.items()) elif np.iterable(axes_map): # Full mapping, at least one for each array dimension: input_axes, output_axes = (*range(len(axes_map)),), axes_map if invert: # Swap indices to undo a previous reshape: input_axes, output_axes = output_axes, input_axes if shape is not None: # Replace/override: dims = len(shape) # Input validation: n_in, n_out, n = len(input_axes), len(output_axes), array.ndim out_of_bounds = lambda axes, d: any(ax >= d or ax < -d for ax in axes) validate_dims = validate and (dims is not None) dict_input = isinstance(axes_map, dict) s = f' (invert={invert})' if validate and invert and n_in != len(set(input_axes)): msg = f'Cannot remap from duplicate input dimensions{s}: {input_axes}' raise ValueError(msg) elif validate and not invert and n_out != len(set(output_axes)): msg = f'Cannot remap to duplicate output dimensions{s}: {output_axes}' raise ValueError(msg) elif validate_dims and invert and dims > n: msg = f'Cannot remap array from {n} to {dims} dimensions. '\ f'Output cannot have more dimensions than input{s}.' raise ValueError(msg) elif validate_dims and not invert and dims < n: msg = f'Cannot remap array from {n} to {dims} dimensions. '\ f'Output cannot have fewer dimensions than input{s}.' raise ValueError(msg) elif validate_dims and not invert and out_of_bounds(output_axes, dims): msg = f'With {dims} output dimensions, cannot remap to dimensions '\ f'greater than {dims - 1} or less than {-dims}: {output_axes}' raise ValueError(msg) elif validate and (dict_input or invert) and out_of_bounds(input_axes, n): msg = f'With {n} input dimensions, cannot remap from dimensions '\ f'greater than {n - 1} or less than {-n}{s}: {input_axes}' raise ValueError(msg) elif validate and not (dict_input or invert) and n_out < array.ndim: msg = 'Without input dimensions to remap from, requires at least as '\ f'many output dimensions as array dimensions ({n}, got {n_out}).' raise ValueError(msg) # Backward: if invert: if dims is None: # Count non-singleton dimensions: n_full = sum(np.array(array.shape) > 1) # Maximize with the highest requested output dimension: dims = max(max(output_axes) + 1, abs(min(output_axes)), n_full) # Move array dimensions back into original positions: array = np.moveaxis(array, input_axes, output_axes) # Prune any trailing singleton dimensions: return np.squeeze(array, tuple(range(dims, n))) # Forward: if dims is None: # Maximize array dimensionality with highest output dimension: dims = max((max(output_axes) + 1, abs(min(output_axes)), n)) # Append singleton axes to match target dimensionality: array = np.expand_dims(array, tuple(range(n, dims))) # Move array dimensions to their target positions: array = np.moveaxis(array, input_axes, output_axes) if shape is not None: # Validate broadcasting compatibility with requested shape: shape1, shape2 = np.array(array.shape), np.array(shape) # Assert compatible axis sizes (equal or one) in each dimension: if not all((shape1 == shape2) | (shape1 == 1) | (shape2 == 1)): raise ValueError(f'Remapped array shape {array.shape} is not '\ f'compatible with the requested shape {shape}.') return array
Freely adds and moves array dimensions without changing the data. Consists of a call to np.expand_dims() to match the required target dimensionality, followed by np.moveaxis() to position the specified input dimensions at the requested output dimensions. Allows to undo a previous reshape operation based on the used axes_map input and the original dimensionality using np.moveaxis() and np.squeeze().
Parameters
array
:ND array
ofany type (any shape)
- Input array to be reshaped.
axes_map
:dict {ints: ints}
or1D array-like (>=array.ndim,)
ofints
- Index mapping linking input dimensions to their respective output dimension. If dict, moves only the input dimensions specified by the keys while maintaining order of the remaining dimensions. Otherwise, expects at least as many output dimensions as array.ndim, one for each array dimension in original order, or more to add singleton dimensions. Input dimensions must be unique and in [-array.ndim, array.ndim - 1]. Output dimensions must be unique and in [-dims, dims - 1]. Provided indices may be negative, regardless of input format.
dims
:int
, optional- Dimensionality of the output array. If specified while invert is False, appends singleton dimensions until array matches this dimensionality. If specified while invert is True, applies inverse index mapping, then prunes singleton dimensions until array matches this dimensionality. Replaced by shape. If neither is provided, falls back to the highest provided output dimension, the dimensionality of array (invert=False), or the number of non-singleton dimensions in array (invert=True), whatever is larger. The default is None.
shape
:1D array-like (>=array.ndim,)
ofints
, optional- Replaces dims by the number of dimensions in shape and executes as usual. If specified while invert is False, validates that the remapped array is broadcast-compatible with shape in every dimension. Functional but not recomended for use while invert is True. The default is None.
invert
:bool
, optional- If True, switches to inverse index mapping by swapping the provided input dimensions and output dimensions in order to undo a previous reshape operation by this function that produced the array at hand. Requires axes_map to be the same argument as used previously, and dims to be the number of dimensions in the original array. If False, simply applies the specified index mapping. The default is False.
pass_scalars
:bool
, optional- If True, immediately returns scalar array inputs such as ints, floats, bools, None, or 0D arrays. If False, treats scalar inputs as 1D arrays of size 1. The default is False.
validate
:bool
, optional- If True, extensively validates the input parameters. If False, skips validation entirely and attempts direct execution. Has no effect on the broadcasting validation triggered by shape. The default is True.
Returns
array
:ND array
ofany type
- View on the reshaped array with the required target dimensionality.
Raises
ValueError
- Breaks if axes_map specifies duplicate input or output dimensions, if any specified dimension is out of bounds with dims or array.ndim, or if an array-like axes_map is not at least of length array.ndim. Also breaks if the remapped array is not broadcast-compatible (equal axis sizes or one singleton) with the provided shape in all dimensions.
def align_arrays(arrays, new_axis=0, align='center', pad=0, dtype=builtins.float)
-
Expand source code
def align_arrays(arrays, new_axis=0, align='center', pad=0, dtype=float): """ Appends arrays as aligned slices along a new dimension of the output. Parameters ---------- arrays : tuple or list (m,) of ND arrays Arrays to be aligned in a new array. Must all have the same data type and number of dimensions. Arrays are appended in the provided order. Single arrays are If a single array is given, adds a singleton dimension along new_axis. new_axis : int, optional Additional axis of the output array along which the input arrays are appended. The order of other axes is preserved. The default is 0. align : str, optional Defines how input arrays are inserted in each output axis. Options are # 'center': Aligns to the axis mid, padding both sides. # 'start': Aligns to the axis beginning, padding afterwards. # 'end': Aligns to the axis ending, padding before. If 'center', padding at the ending may be one sample larger if the number of padded samples is odd. The default is 'center'. pad : float or int or bool or np.nan, optional Fill value to initialize the output array with. The default is 0. dtype : dtype class, optional Data type to initialize the output array with. Must be compatible with the fill value specified by pad. The default is float. Returns ------- output : N+1D array (..., m, ...) of dtype Output array with the input arrays inserted along new_axis. Raises ------ ValueError Breaks if any input arrays have different dimensionality. """ if isinstance(arrays, np.ndarray): # Simple axis insertion early exit: return np.expand_dims(arrays, axis=new_axis) # Determine extent of each array: dims, shapes = zip(*[(array.ndim, array.shape) for array in arrays]) dims, max_dims = np.array(dims), max(dims) # Validate equal dimensionality: invalid = np.nonzero(dims < max_dims)[0] if invalid.size: txt = string_series(invalid, prefix=['Array', 'Arrays'], suffix=['is ', 'are '], conj='and') raise ValueError(f'{txt} incompatible with dimensionality {max_dims}.') # Get maximum extent per dimension: shapes = np.array(shapes, dtype=int) max_shape = np.max(shapes, axis=0) # Determine unfilled spaces: paddings = max_shape - shapes if align == 'center': starts = paddings // 2 elif align == 'start': starts = np.zeros_like(paddings) elif align == 'end': starts = paddings stops = starts + shapes # Initialize output array with additional axis to merge arrays along: full_shape = (*max_shape[:new_axis], dims.size, *max_shape[new_axis:]) output = np.full(full_shape, fill_value=pad, dtype=dtype) # Prepare indices for array insertion: slice_axes = list(range(output.ndim)) starts, stops = starts.tolist(), stops.tolist() # Insert each aligned array slice into the output array: for i, (array, start, stop) in enumerate(zip(arrays, starts, stops)): start.insert(new_axis, i), stop.insert(new_axis, i + 1) inds = slice_index(output.ndim, slice_axes, start, stop) output[inds] = np.expand_dims(array, axis=new_axis) return output
Appends arrays as aligned slices along a new dimension of the output.
Parameters
arrays
:tuple
orlist (m,)
ofND arrays
- Arrays to be aligned in a new array. Must all have the same data type and number of dimensions. Arrays are appended in the provided order. Single arrays are If a single array is given, adds a singleton dimension along new_axis.
new_axis
:int
, optional- Additional axis of the output array along which the input arrays are appended. The order of other axes is preserved. The default is 0.
align
:str
, optional- Defines how input arrays are inserted in each output axis. Options are
'center': Aligns to the axis mid, padding both sides.
'start': Aligns to the axis beginning, padding afterwards.
'end': Aligns to the axis ending, padding before.
If 'center', padding at the ending may be one sample larger if the number of padded samples is odd. The default is 'center'. pad
:float
orint
orbool
ornp.nan
, optional- Fill value to initialize the output array with. The default is 0.
dtype
:dtype class
, optional- Data type to initialize the output array with. Must be compatible with the fill value specified by pad. The default is float.
Returns
output
:N+1D array (..., m, ...)
ofdtype
- Output array with the input arrays inserted along new_axis.
Raises
ValueError
- Breaks if any input arrays have different dimensionality.
def edge_along_axis(array, axis=0, which=-1, validate=True, reduce=False)
-
Expand source code
def edge_along_axis(array, axis=0, which=-1, validate=True, reduce=False): """ In a 2D array, finds first or last non-zero entry per column or row. Designed for multiple distributions or multi-channel time series data, but can handle 1D arrays by a separate processing branch. By default, identified non-zero entries are returned as a tuple of 1D arrays that can be used to index the original array (np.nonzero format). Output can be reduced to a single 1D array of indices along the given axis. Parameters ---------- array : 2D array (m, n) or 1D array (m,) of arbitrary type Array in which to identify the requested edge indices. axis : int, optional Array dimension along which to find the requested edge indices. Options are 0 or 1. If axis is 0, searches along each array column. If axis is 1, searches along each array row. Must be 0 if array is 1D. The default is 0. which : int, optional Specifies whether to find the first or last non-zero entry along the given axis. Options are 0 (first) and -1 (last). The default is -1. validate : bool, optional If True and array contains all-zero columns/rows, omits corresponding indices from the output. If False, returns indices for all slices (non- sensical integers in case of all-zero slices). The default is True. reduce : bool, optional If False, returns a tuple of two 1D arrays (one if input array is 1D), which provide the full index pair for each column/row. This format is valid for indexing both 1D and 2D input arrays, producing 1D output. If True, returns a single 1D array of indices along the given array axis, with one entry per slice. This format is good to access the indices themselves but may be unsuitable for indexing. The default is False. Returns ------- inds : 1D array (p,) or tuple (2, or 1,) of 1D arrays (p,) Indices of the first or last non-zero entry per column or row in array. If reduce is True, returns a single 1D array of indices along axis, else a tuple of one or two 1D index arrays (depending on array.ndim). Can at most hold as many entries as specified slices in array (less if validate is True and and array contains all-zero slices). Raises ------ ValueError Breaks if array is neither 1D nor 2D. Breaks immediately if array contains no non-zero entries. If validate is True, breaks if no entries remain after omitting all-zero slices. """ # All-zero early exit: if not np.any(array): raise ValueError('No non-zero entries found in the given array.') # Input interpretation: if which == 0: # Find first indices: mask_value = np.inf func = np.argmin elif which == -1: # Find last indices: mask_value = -np.inf func = np.argmax # Univariate early exit: if array.ndim == 1 or (array.ndim == 2 and 1 in array.shape): # Get index coordinates of entry: inds = np.argwhere(array)[which] if reduce: # Slice coordinates to single dimension (1D array): return array_slice(inds, start=axis, stop=axis, include=True) # Ensure index use (tuple of 1D arrays): return tuple(i for i in inds[:, None]) # Shape validation: elif array.ndim != 2: raise ValueError('Input array must be 1D or 2D.') # Draw parallel index gridlines along axis: grid_inds = np.arange(array.shape[axis]) grid_inds = np.expand_dims(grid_inds, axis=1 - axis) # Set non-zero entries to indices, hide others: masked_array = np.where(array, grid_inds, mask_value) # Find smallest/largest index per column/row: inds = func(masked_array, axis=axis).astype(int) # Return options: if validate: # Omit all-zero columns/rows: is_valid = np.any(array, axis=axis) inds = inds[is_valid] if inds.size == 0: # Break instead of returning empty index array: raise ValueError('No non-zero entries found in the given array.') if reduce: # 1D array: return inds # Complete index pairs by each row/column: other_inds = np.arange(array.shape[1 - axis]) if validate: # Match to valid counterparts: other_inds = other_inds[is_valid] # Return sorted to enable index use (tuple of 1D arrays): return (other_inds, inds) if axis else (inds, other_inds)
In a 2D array, finds first or last non-zero entry per column or row. Designed for multiple distributions or multi-channel time series data, but can handle 1D arrays by a separate processing branch. By default, identified non-zero entries are returned as a tuple of 1D arrays that can be used to index the original array (np.nonzero format). Output can be reduced to a single 1D array of indices along the given axis.
Parameters
array
:2D array (m, n)
or1D array (m,)
ofarbitrary type
- Array in which to identify the requested edge indices.
axis
:int
, optional- Array dimension along which to find the requested edge indices. Options are 0 or 1. If axis is 0, searches along each array column. If axis is 1, searches along each array row. Must be 0 if array is 1D. The default is 0.
which
:int
, optional- Specifies whether to find the first or last non-zero entry along the given axis. Options are 0 (first) and -1 (last). The default is -1.
validate
:bool
, optional- If True and array contains all-zero columns/rows, omits corresponding indices from the output. If False, returns indices for all slices (non- sensical integers in case of all-zero slices). The default is True.
reduce
:bool
, optional- If False, returns a tuple of two 1D arrays (one if input array is 1D), which provide the full index pair for each column/row. This format is valid for indexing both 1D and 2D input arrays, producing 1D output. If True, returns a single 1D array of indices along the given array axis, with one entry per slice. This format is good to access the indices themselves but may be unsuitable for indexing. The default is False.
Returns
inds
:1D array (p,)
ortuple (2,
or1,)
of1D arrays (p,)
- Indices of the first or last non-zero entry per column or row in array. If reduce is True, returns a single 1D array of indices along axis, else a tuple of one or two 1D index arrays (depending on array.ndim). Can at most hold as many entries as specified slices in array (less if validate is True and and array contains all-zero slices).
Raises
ValueError
- Breaks if array is neither 1D nor 2D. Breaks immediately if array contains no non-zero entries. If validate is True, breaks if no entries remain after omitting all-zero slices.
def safe_fraction(numerator, denominator, shape=None, value=nan, sign=None)
-
Expand source code
def safe_fraction(numerator, denominator, shape=None, value=np.nan, sign=None): """ Divides two arrays element-wise, avoiding divisions by zero. Wrapper to np.divide(). Skips invalid operations and sets the output to the given replace value. Can filter further by the sign of the output. Parameters ---------- numerator : ND-array of floats or ints Numerator of element-wise division. denominator : ND-array of floats or ints Denominator of element-wise division, broadcastable to numerator. shape : tuple of ints, optional Shape of the output array. If not specified, inferred from the shapes of numerator and denominator, if possible. The default is None. value : scalar, optional Value to replace the output of invalid or filtered operations. The default is np.nan. sign : int or array-like of ints If specified, also skips operations that produce output with a sign other than the specified ones. Options are 1 for positive, -1 for negative, and 0. The default is None. Returns ------- output : ND-array of floats (shape) Result of element-wise division of numerator by denominator. Raises ------ ValueError Breaks if numerator and denominator are not broadcastable. """ # Input validation: if shape is None: valid, shape = broadcastable([numerator.shape, denominator.shape]) if not valid: msg = 'Numerator and denominator must be broadcastable.' raise ValueError(msg) # Initialize storage array and find zero divisions: out, where = np.full(shape, value), denominator != 0 if sign is not None: # Optional filtering of positive, negative, or zero output: where &= np.isin(np.sign(numerator) * np.sign(denominator), sign) return np.divide(numerator, denominator, out=out, where=where)
Divides two arrays element-wise, avoiding divisions by zero. Wrapper to np.divide(). Skips invalid operations and sets the output to the given replace value. Can filter further by the sign of the output.
Parameters
numerator
:ND-array
offloats
orints
- Numerator of element-wise division.
denominator
:ND-array
offloats
orints
- Denominator of element-wise division, broadcastable to numerator.
shape
:tuple
ofints
, optional- Shape of the output array. If not specified, inferred from the shapes of numerator and denominator, if possible. The default is None.
value
:scalar
, optional- Value to replace the output of invalid or filtered operations. The default is np.nan.
sign
:int
orarray-like
ofints
- If specified, also skips operations that produce output with a sign other than the specified ones. Options are 1 for positive, -1 for negative, and 0. The default is None.
Returns
output
:ND-array
offloats (shape)
- Result of element-wise division of numerator by denominator.
Raises
ValueError
- Breaks if numerator and denominator are not broadcastable.