""" Functions for acting on a axis of an array. """ import numpy as np def axis_slice(a, start=None, stop=None, step=None, axis=-1): """Take a slice along axis 'axis' from 'a'. Parameters ---------- a : numpy.ndarray The array to be sliced. start, stop, step : int or None The slice parameters. axis : int, optional The axis of `a` to be sliced. Examples -------- >>> import numpy as np >>> from scipy.signal._arraytools import axis_slice >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> axis_slice(a, start=0, stop=1, axis=1) array([[1], [4], [7]]) >>> axis_slice(a, start=1, axis=0) array([[4, 5, 6], [7, 8, 9]]) Notes ----- The keyword arguments start, stop and step are used by calling slice(start, stop, step). This implies axis_slice() does not handle its arguments the exactly the same as indexing. To select a single index k, for example, use axis_slice(a, start=k, stop=k+1) In this case, the length of the axis 'axis' in the result will be 1; the trivial dimension is not removed. (Use numpy.squeeze() to remove trivial axes.) """ a_slice = [slice(None)] * a.ndim a_slice[axis] = slice(start, stop, step) b = a[tuple(a_slice)] return b def axis_reverse(a, axis=-1): """Reverse the 1-D slices of `a` along axis `axis`. Returns axis_slice(a, step=-1, axis=axis). """ return axis_slice(a, step=-1, axis=axis) def odd_ext(x, n, axis=-1): """ Odd extension at the boundaries of an array Generate a new ndarray by making an odd extension of `x` along an axis. Parameters ---------- x : ndarray The array to be extended. n : int The number of elements by which to extend `x` at each end of the axis. axis : int, optional The axis along which to extend `x`. Default is -1. Examples -------- >>> import numpy as np >>> from scipy.signal._arraytools import odd_ext >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) >>> odd_ext(a, 2) array([[-1, 0, 1, 2, 3, 4, 5, 6, 7], [-4, -1, 0, 1, 4, 9, 16, 23, 28]]) Odd extension is a "180 degree rotation" at the endpoints of the original array: >>> t = np.linspace(0, 1.5, 100) >>> a = 0.9 * np.sin(2 * np.pi * t**2) >>> b = odd_ext(a, 40) >>> import matplotlib.pyplot as plt >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='odd extension') >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') >>> plt.legend(loc='best') >>> plt.show() """ if n < 1: return x if n > x.shape[axis] - 1: raise ValueError(("The extension length n (%d) is too big. " + "It must not exceed x.shape[axis]-1, which is %d.") % (n, x.shape[axis] - 1)) left_end = axis_slice(x, start=0, stop=1, axis=axis) left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis) right_end = axis_slice(x, start=-1, axis=axis) right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis) ext = np.concatenate((2 * left_end - left_ext, x, 2 * right_end - right_ext), axis=axis) return ext def even_ext(x, n, axis=-1): """ Even extension at the boundaries of an array Generate a new ndarray by making an even extension of `x` along an axis. Parameters ---------- x : ndarray The array to be extended. n : int The number of elements by which to extend `x` at each end of the axis. axis : int, optional The axis along which to extend `x`. Default is -1. Examples -------- >>> import numpy as np >>> from scipy.signal._arraytools import even_ext >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) >>> even_ext(a, 2) array([[ 3, 2, 1, 2, 3, 4, 5, 4, 3], [ 4, 1, 0, 1, 4, 9, 16, 9, 4]]) Even extension is a "mirror image" at the boundaries of the original array: >>> t = np.linspace(0, 1.5, 100) >>> a = 0.9 * np.sin(2 * np.pi * t**2) >>> b = even_ext(a, 40) >>> import matplotlib.pyplot as plt >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='even extension') >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') >>> plt.legend(loc='best') >>> plt.show() """ if n < 1: return x if n > x.shape[axis] - 1: raise ValueError(("The extension length n (%d) is too big. " + "It must not exceed x.shape[axis]-1, which is %d.") % (n, x.shape[axis] - 1)) left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis) right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis) ext = np.concatenate((left_ext, x, right_ext), axis=axis) return ext def const_ext(x, n, axis=-1): """ Constant extension at the boundaries of an array Generate a new ndarray that is a constant extension of `x` along an axis. The extension repeats the values at the first and last element of the axis. Parameters ---------- x : ndarray The array to be extended. n : int The number of elements by which to extend `x` at each end of the axis. axis : int, optional The axis along which to extend `x`. Default is -1. Examples -------- >>> import numpy as np >>> from scipy.signal._arraytools import const_ext >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) >>> const_ext(a, 2) array([[ 1, 1, 1, 2, 3, 4, 5, 5, 5], [ 0, 0, 0, 1, 4, 9, 16, 16, 16]]) Constant extension continues with the same values as the endpoints of the array: >>> t = np.linspace(0, 1.5, 100) >>> a = 0.9 * np.sin(2 * np.pi * t**2) >>> b = const_ext(a, 40) >>> import matplotlib.pyplot as plt >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='constant extension') >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') >>> plt.legend(loc='best') >>> plt.show() """ if n < 1: return x left_end = axis_slice(x, start=0, stop=1, axis=axis) ones_shape = [1] * x.ndim ones_shape[axis] = n ones = np.ones(ones_shape, dtype=x.dtype) left_ext = ones * left_end right_end = axis_slice(x, start=-1, axis=axis) right_ext = ones * right_end ext = np.concatenate((left_ext, x, right_ext), axis=axis) return ext def zero_ext(x, n, axis=-1): """ Zero padding at the boundaries of an array Generate a new ndarray that is a zero-padded extension of `x` along an axis. Parameters ---------- x : ndarray The array to be extended. n : int The number of elements by which to extend `x` at each end of the axis. axis : int, optional The axis along which to extend `x`. Default is -1. Examples -------- >>> import numpy as np >>> from scipy.signal._arraytools import zero_ext >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) >>> zero_ext(a, 2) array([[ 0, 0, 1, 2, 3, 4, 5, 0, 0], [ 0, 0, 0, 1, 4, 9, 16, 0, 0]]) """ if n < 1: return x zeros_shape = list(x.shape) zeros_shape[axis] = n zeros = np.zeros(zeros_shape, dtype=x.dtype) ext = np.concatenate((zeros, x, zeros), axis=axis) return ext def _validate_fs(fs, allow_none=True): """ Check if the given sampling frequency is a scalar and raises an exception otherwise. If allow_none is False, also raises an exception for none sampling rates. Returns the sampling frequency as float or none if the input is none. """ if fs is None: if not allow_none: raise ValueError("Sampling frequency can not be none.") else: # should be float if not np.isscalar(fs): raise ValueError("Sampling frequency fs must be a single scalar.") fs = float(fs) return fs