【问题标题】:python, summing numpy arrays of same dimensions, different shapes with offset and rolling over the endpython,对相同尺寸的numpy数组求和,不同形状的偏移和滚动结束
【发布时间】:2021-05-22 03:57:46
【问题描述】:

我有 2 个 numpy 数组,想用偏移量对它们求和。 总和总是具有数组“a”的形状

a = np.array([1, 1, 1, 1, 1])
b = np.array([5, 6])
sumWithRoll(a, b, offset=1)
print(a)
>> [1, 6, 7, 1, 1]

此外,如果数组“b”很长或偏移量足够大,它应该翻转数组“a”的末尾:

a = np.array([1, 1, 1, 1, 1])
b = np.array([5, 6, 7, 8])
sumWithRoll(a, b, offset=3)
print(a)
>> [8, 9, 1, 6, 7]

我需要这个来合并两个循环播放的声音缓冲区,并希望有一个快速的解决方案,占用更少的内存。

编辑: 我有一个看起来很长的解决方案:

def sumWithRoll(buffer, indata, idx):
    buffLen = len(buffer)
    dataLen = len(indata)
    if dataLen > buffLen:
        indata = indata[0:buffLen]
        dataLen = buffLen
    idx = idx % buffLen
    idx2 = (idx + dataLen) % buffLen
    if idx2 <= idx:
        idx3 = buffLen - idx
        buffer[idx:buffLen] += indata[0:idx3]                   
        buffer[0:idx2] += indata[idx3:buffLen]
    else:
        buffer[idx:idx2] += indata[:]

希望有Pythonic的一两行解决方案

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    试试np.roll:

    import numpy as np
    
    
    def sum_with_roll(a, b, offset=0):
        e = np.zeros(a.shape)
        e[tuple(map(slice, b.shape))] = b
        return a + np.roll(e, offset)
    
    
    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6])
    print(sum_with_roll(a, b, offset=1))
    
    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6, 7, 8])
    print(sum_with_roll(a, b, offset=3))
    

    输出:

    [1. 6. 7. 1. 1.]
    [8. 9. 1. 6. 7.]
    

    对于list 输出:

    def sum_with_roll(a, b, offset=0):
        e = np.zeros(a.shape)
        e[tuple(map(slice, b.shape))] = b
        return (a + np.roll(e, offset)).tolist()
    
    [1.0, 6.0, 7.0, 1.0, 1.0]
    [8.0, 9.0, 1.0, 6.0, 7.0]
    

    对于int 类型输出:

    def sum_with_roll(a, b, offset=0):
        e = np.zeros(a.shape)
        e[tuple(map(slice, b.shape))] = b
        return (a + np.roll(e, offset)).astype(int)
    
    [1 6 7 1 1]
    [8 9 1 6 7]
    

    【讨论】:

    • 看起来很简洁,但在我测试时速度很慢。发现“滚动”比替代方案慢约 50 倍。此处显示:stackoverflow.com/questions/30399534/…
    • 很高兴知道!我会说,这些轮班程序都没有像 roll 那样做。
    【解决方案2】:

    可以使用简单的模运算符来模拟滚动。

    鉴于您想将ab 加在一起的一些索引,它基本上只是在做a[indices] += b

    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6])
    offset = 1
    
    indices = np.arange(offset, len(b)+offset) % len(a)
    a[indices] += b
    # np.add.at(a, indices, b) - if you want to do the operation unbuffered
    

    a 中的输出:

    array([1, 6, 7, 1, 1])
    
    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6, 7, 8])
    offset = 3
    
    indices = np.arange(offset, len(b)+offset) % len(a)
    a[indices] += b
    

    a 中的输出:

    array([8, 9, 1, 6, 7])
    

    您也可以为此使用np.bincount

    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6, 7, 8])
    offset = 3
    
    indices = np.arange(offset, len(b)+offset) % len(a)
    np.bincount(indices, b)+a
    

    输出:

    array([8., 9., 1., 6., 7.])
    

    很遗憾,无法为np.bincount 指定结果缓冲区,这会使+a 操作过时。

    编辑

    如果你有兴趣让它更快,我建议将就地添加分成 2 片:

    a = np.array([1, 1, 1, 1, 1])
    b = np.array([5, 6, 7, 8])
    offset = 3
    
    size = len(a) - offset
    s1 = slice(offset, offset + size)
    s2 = slice(None,size)
    
    size = len(b) - size
    s3 = slice(None, size)
    s4 = slice(size, None)
    
    a[s1] += b[s2]
    a[s3] += b[s4]
    

    输出:

    array([8, 9, 1, 6, 7])
    

    【讨论】:

    • 我喜欢你简洁的解决方案。重复多次模运算会减慢速度吗? len(b) 可能接近百万,因为它是声音样本数组。指数 = np.arange(offset, len(b)+offset) % len(a)
    • 它是矢量化的,因此在隔离时它应该相当快 - 但你应该确定它的时间。 np.roll 方法从切片重构数组,因此在读取/写入内存的次数方面可能会更好github.com/numpy/numpy/blob/v1.20.0/numpy/core/…
    • @slmnv5 我对我的帖子进行了编辑,这可能会为您提供如何优化操作的切入点。
    • 复制粘贴此作品,但如果参数发生变化,例如offset = 2 我得到错误:a[s3] += b[s4] ValueError: non-broadcastable output operand with shape (1,) doesn't match the broadcast shape (3,)
    • 我可能在某个地方出错了。但是这个想法是将数组分成两个连续的部分。第一部分是 b 中的所有值,从某个偏移量开始,直到超出范围并且应该“环绕”。另一部分是从 b 开始到偏移量的所有剩余值。
    猜你喜欢
    • 1970-01-01
    • 2019-05-18
    • 2019-11-26
    • 1970-01-01
    • 2019-10-02
    • 2013-10-28
    • 2017-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多