高效的numpy零阶保持

4

有没有一种高效的方法来使用零阶保持重新采样numpy数组?理想情况下,其签名类似于numpy.interp

我知道scipy.interpolate.interp1d,但我相信会有一种矢量化的替代方法来处理这种情况。

4个回答

4

Numpy < 1.12

如果您不需要插值新值,最有效的方法是保留原始数组并使用浮点数进行索引。这实际上是一种零阶保持。

>>> import numpy as np
>>> A = np.array(range(10))
>>> [A[i] for i in np.linspace(0, 9, num=25)]
[0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9]

Numpy >= 1.12

在numpy v1.11中,使用浮点数进行索引已被弃用,并且在v1.12(2017年1月)中被移除。当前numpy版本中,上述代码会引发IndexError异常。

您可以通过使用包装器来访问数组,并即时将浮点索引转换为整数,以复制旧版本numpy的浮点索引行为。当内存效率成为问题时,这将避免需要预先插值中间值使用numpy.interpscipy.interpolate.interp1d


2

Numpy数组不再允许非整数索引,因此原先的解决方案已经不再有效。 scipy.interpolate.interp1d很稳健,但在许多情况下并不是最快的。这里提供了一个稳健的代码解决方案,它在可以使用np.searchsorted时使用它,并在无法使用时返回到scipy版本。

import numpy as np
from scipy.interpolate import interp1d

def zero_order_hold(x, xp, yp, left=np.nan, assume_sorted=False):
    r"""
    Interpolates a function by holding at the most recent value.

    Parameters
    ----------
    x : array_like
        The x-coordinates at which to evaluate the interpolated values.
    xp: 1-D sequence of floats
        The x-coordinates of the data points, must be increasing if argument period is not specified. Otherwise, xp is internally sorted after normalizing the periodic boundaries with xp = xp % period.
    yp: 1-D sequence of float or complex
        The y-coordinates of the data points, same length as xp.
    left: int or float, optional, default is np.nan
        Value to use for any value less that all points in xp
    assume_sorted : bool, optional, default is False
        Whether you can assume the data is sorted and do simpler (i.e. faster) calculations

    Returns
    -------
    y : float or complex (corresponding to fp) or ndarray
        The interpolated values, same shape as x.

    Notes
    -----
    #.  Written by DStauffman in July 2020.

    Examples
    --------
    >>> import numpy as np
    >>> xp = np.array([0., 111., 2000., 5000.])
    >>> yp = np.array([0, 1, -2, 3])
    >>> x = np.arange(0, 6001, dtype=float)
    >>> y = zero_order_hold(x, xp, yp)

    """
    # force arrays
    x  = np.asanyarray(x)
    xp = np.asanyarray(xp)
    yp = np.asanyarray(yp)
    # find the minimum value, as anything left of this is considered extrapolated
    xmin = xp[0] if assume_sorted else np.min(xp)
    # check that xp data is sorted, if not, use slower scipy version
    if assume_sorted or np.all(xp[:-1] <= xp[1:]):
        ix = np.searchsorted(xp, x, side='right') - 1
        return np.where(np.asanyarray(x) < xmin, left, yp[ix])
    func = interp1d(xp, yp, kind='zero', fill_value='extrapolate', assume_sorted=False)
    return np.where(np.asanyarray(x) < xmin, left, func(x))

这通过了一堆测试用例:

import unittest

import numpy as np
from scipy.interpolate import interp1d

class Test_zero_order_hold(unittest.TestCase):
    r"""
    Tests the zero_order_hold function with the following cases:
        Subsample high rate
        Supersample low rate
        xp Not sorted
        x not sorted
        Left extrapolation
        Lists instead of arrays

    Notes
    -----
    #.  Uses scipy.interpolate.interp1d as the gold standard (but it's slower)
    """
    def test_subsample(self):
        xp = np.linspace(0., 100*np.pi, 500000)
        yp = np.sin(2 * np.pi * xp)
        x  = np.arange(0., 350., 0.1)
        func = interp1d(xp, yp, kind='zero', fill_value='extrapolate', assume_sorted=True)
        y_exp = func(x)
        y = zero_order_hold(x, xp, yp)
        np.testing.assert_array_equal(y, y_exp)
        y = zero_order_hold(x, xp, yp, assume_sorted=True)
        np.testing.assert_array_equal(y, y_exp)

    def test_supersample(self):
        xp = np.array([0., 5000., 10000., 86400.])
        yp = np.array([0, 1, -2, 0])
        x  = np.arange(0., 86400.,)
        func = interp1d(xp, yp, kind='zero', fill_value='extrapolate', assume_sorted=True)
        y_exp = func(x)
        y = zero_order_hold(x, xp, yp)
        np.testing.assert_array_equal(y, y_exp)
        y = zero_order_hold(x, xp, yp, assume_sorted=True)
        np.testing.assert_array_equal(y, y_exp)

    def test_xp_not_sorted(self):
        xp    = np.array([0, 10, 5, 15])
        yp    = np.array([0, 1, -2, 3])
        x     = np.array([10, 2, 14,  6,  8, 10, 4, 14, 0, 16])
        y_exp = np.array([ 1, 0,  1, -2, -2,  1, 0,  1, 0,  3])
        y     = zero_order_hold(x, xp, yp)
        np.testing.assert_array_equal(y, y_exp)

    def test_x_not_sorted(self):
        xp    = np.array([0, 5, 10, 15])
        yp    = np.array([0, -2, 1, 3])
        x     = np.array([10, 2, 14,  6,  8, 10, 4, 14, 0, 16])
        y_exp = np.array([ 1, 0,  1, -2, -2,  1, 0,  1, 0,  3])
        y     = zero_order_hold(x, xp, yp)
        np.testing.assert_array_equal(y, y_exp)

    def test_left_end(self):
        xp    = np.array([0, 5, 10, 15, 4])
        yp    = np.array([0, 1, -2, 3, 0])
        x     = np.array([-4, -2, 0, 2, 4, 6])
        y_exp = np.array([-5, -5, 0, 0, 0, 1])
        y     = zero_order_hold(x, xp, yp, left=-5)
        np.testing.assert_array_equal(y, y_exp)

    def test_lists(self):
        xp    = [0, 5, 10, 15]
        yp    = [0, 1, 2, 3]
        x     = [-4, -2, 0, 2, 4, 6, 20]
        y_exp = [-1, -1, 0, 0, 0, 1, 3]
        y     = zero_order_hold(x, xp, yp, left=-1)
        np.testing.assert_array_equal(y, y_exp)

第一和第二项速度测试表明,对于子采样高速数据,numpy解决方案大约快100倍,对于超采样低速数据,大约快3倍。


0
这是一个无需使用numpy的版本,签名相同。数据需要按递增顺序排列,因为通过将列表用作嵌套函数默认值的“聪明”使用方式进行转储(100倍加速因子):
def interp0(x, xp, yp):
    """Zeroth order hold interpolation w/ same
    (base)   signature  as numpy.interp."""
    from collections import deque

    def func(x0, xP=deque(xp), yP=deque(yp)):
      if x0 <= xP[0]:
        return yP[0]
      if x0 >= xP[-1]:
        return yP[-1]    
      while x0 > xP[0]:
        xP.popleft()     # get rid of default
        y = yP.popleft() # data as we go.
      return y

return map(func, x)

0
有点晚了,但这是我想到的:
from numpy import zeros, array, sign

def signal_zoh(x,y,epsilon = 0.001):
    """
        Fills in the data from a Zero-Order Hold (stair-step) signal
    """
    deltaX = array(x[1:],dtype='float') - x[:-1]
    fudge = min(deltaX) *epsilon
    retX = zeros((len(x)*2-1,))
    retY = zeros((len(y)*2-1,))
    retX[0::2] = x
    retX[1::2] = x[1:]+fudge*sign(deltaX)
    retY[0::2] = y
    retY[1::2] = y[:-1]
    return retX,retY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接