Python如何将多个不同长度的数组添加到一个数组中

时间:2019-03-28 19:16:00

标签: python numpy

我正在开发一个程序,该程序需要将音频数组与给定的起始索引混合在一起。例如

signal1 = np.array([1,2,3,4])
signal2 = np.array([5,5,5])
signal3 = np.array([7,7,7,7])
sig = np.array([signal1,signal2,signal3])
onset(0, 2, 8)
result = mixing_function(sig,onset)

基于开始,信号2将从索引2添加到信号1,信号3将从索引8添加到混合信号,因此混合部分将被零填充。它应该返回:

[1,2,8,9,5,0,0,0,7,7,7,7]

我不确定为此编写代码的有效方法是什么。现在,我创建了一个最大长度为maxlen的零数组。然后我将sig中的每个元素添加到结果的相应索引范围中:

def mixing_function(sig,onset):
    maxlen = np.max([o + len(s) for o, s in zip(onset, sig)])
    result =  np.zeros(maxlen)
    for i in range(len(onset)):
        result[onset[i]:onset[i] + len(sig[i])] += sig[i] 
    return result

但是,这可能会非常慢,特别是当许多信号混合在一起且起病点不同时。如果有更有效的方法,请提出建议。

非常感谢

J


4 个答案:

答案 0 :(得分:1)

以下是一些针对该问题的不同解决方案的统计信息。通过对实现进行矢量化处理可以使性能最大化,从而可以提高性能,但是除此之外,我认为您还必须尝试使用​​cython或尝试其他编程语言。

import numpy as np
from numba import jit
from time import time
np.random.seed(42)

def mixing_function(sig, onset):
    maxlen = np.max([o + len(s) for o, s in zip(onset, sig)])
    result =  np.zeros(maxlen)
    for i in range(len(onset)):
        result[onset[i]:onset[i] + len(sig[i])] += sig[i] 
    return result

def mix(sig, onset):
    siglengths = np.vectorize(len)(sig)
    maxlen = max(onset + siglengths)
    result = np.zeros(maxlen)
    for i in range(len(sig)):
        result[onset[i]: onset[i]+siglengths[i]] += sig[i]
    return result

@jit(nopython=True)
def mixnumba(sig, onset):
    # maxlen = np.max([onset[i] + len(sig[i]) for i in range(len(sig))])
    maxlen = -1
    for i in range(len(sig)):
        maxlen = max(maxlen, sig[i].size + onset[i])
    result = np.zeros(maxlen)
    for i in range(len(sig)):
        result[onset[i]: onset[i] + sig[i].size] += sig[i]
    return result

def signal_adder_with_onset(data, onset):
    data = np.array(data)
    # Get lengths of each row of data
    lens = np.array([len(i) for i in data])
    #adjust with offset for max possible lengths
    max_size = lens + onset
    # Mask of valid places in each row
    mask = ((np.arange(max_size.max()) >= onset.reshape(-1, 1)) 
            &  (np.arange(max_size.max()) < (lens + onset).reshape(-1, 1)))

    # Setup output array and put elements from data into masked positions
    out = np.zeros(mask.shape, dtype=data.dtype) #could perhaps change dtype here
    out[mask] = np.concatenate(data)
    return out.sum(axis=0)

sigbig = [np.random.randn(np.random.randint(1000, 10000)) for _ in range(10000)]
onsetbig = np.random.randint(0, 10000, size=10000)
sigrepeat = np.repeat(sig, 500000).tolist()
onsetrepeat = np.repeat(onset, 500000)

assert all(mixing_function(sigbig, onsetbig) == mix(sigbig, onsetbig))
assert all(mixing_function(sigbig, onsetbig) == mixnumba(sigbig, onsetbig))
assert all(mixing_function(sigbig, onsetbig) == signal_adder_with_onset(sigbig, onsetbig))

%timeit result = mixing_function(sigbig, onsetbig)
%timeit result = mix(sigbig, onsetbig)
%timeit result = mixnumba(sigbig, onsetbig)
%timeit result = signal_adder_with_onset(sigbig, onsetbig)
# Output
114 ms ± 1.97 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
108 ms ± 2.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
368 ms ± 8.22 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
13.4 s ± 211 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit result = mixing_function(sigrepeat, onsetrepeat)
%timeit result = mix(sigrepeat, onsetrepeat)
%timeit result = mixnumba(sigrepeat, onsetrepeat)
%timeit result = signal_adder_with_onset(sigrepeat.tolist(), onsetrepeat)
# Output
933 ms ± 6.43 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
803 ms ± 21.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
4.07 s ± 85.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
254 ms ± 11.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

TL.DR。 通过使用np.vectorize来提高随机性能的长信号的maxlen,可以提高边际性能(快10%左右)。请注意,对于许多小信号,@ Paritosh Singh回答的执行速度要快于其他信号。

答案 1 :(得分:0)

这是可以解决问题的尝试。

def signal_adder_with_onset(data, onset):
    # Get lengths of each row of data
    lens = np.array([len(i) for i in data])
    #adjust with offset for max possible lengths
    max_size = lens + onset
    # Mask of valid places in each row
    mask = ((np.arange(max_size.max()) >= onset.reshape(-1, 1)) 
            &  (np.arange(max_size.max()) < (lens + onset).reshape(-1, 1)))

    # Setup output array and put elements from data into masked positions
    out = np.zeros(mask.shape, dtype=data.dtype) #could perhaps change dtype here
    out[mask] = np.concatenate(data)
    return out.sum(axis=0)

import numpy as np
signal1 = np.array([1,2,3,4])
signal2 = np.array([5,5,5])
signal3 = np.array([7,7,7,7])
sig = np.array([signal1,signal2,signal3])
onset = np.array((0, 2, 8))
result = signal_adder_with_onset(sig, onset)
print(result)
#[1 2 8 9 5 0 0 0 7 7 7 7]

编辑:矢量化操作只会添加更多数据,而添加少量数据则会更慢。

为比较而添加

import time

def signal_adder_with_onset(data, onset):
    # Get lengths of each row of data
    lens = np.array([len(i) for i in data])
    #adjust with offset for max possible lengths
    max_size = lens + onset
    # Mask of valid places in each row
    mask = ((np.arange(max_size.max()) >= onset.reshape(-1, 1)) 
            &  (np.arange(max_size.max()) < (lens + onset).reshape(-1, 1)))

    # Setup output array and put elements from data into masked positions
    out = np.zeros(mask.shape, dtype=data.dtype) #could perhaps change dtype here
    out[mask] = np.concatenate(data)
    return out.sum(axis=0)

def mixing_function(sig,onset):
    maxlen = np.max([o + len(s) for o, s in zip(onset, sig)])
    result =  np.zeros(maxlen)
    for i in range(len(onset)):
        result[onset[i]:onset[i] + len(sig[i])] += sig[i] 
    return result

import numpy as np
signal1 = np.array([1,2,3,4])
signal2 = np.array([5,5,5])
signal3 = np.array([7,7,7,7])
sig = np.array([signal1,signal2,signal3])
sig = np.repeat(sig, 1000000)
onset = np.array((0, 2, 8))
onset = np.repeat(onset, 1000000)
start1 = time.time()
result = signal_adder_with_onset(sig, onset)
end1 = time.time()
start2 = time.time()
result2 = mixing_function(sig,onset)
end2 = time.time()
print(f"Original function: {end2 - start2} \n Vectorized function: {end1 - start1}")
print(result)
#Output:
Original function: 9.28258752822876 
 Vectorized function: 2.5798118114471436
[1000000 2000000 8000000 9000000 5000000 0 0 0 7000000 7000000 7000000
 7000000]

答案 2 :(得分:0)

如果偏移信号,然后将它们放在数据帧中,NaN将添加到列中,以使所有行的长度相同。然后,您可以执行df.sum()。但是,这将返回浮点数而不是整数。

答案 3 :(得分:0)

尝试将等长的numpy零数组插入适当的信号中,然后简单地执行3 numpy数组加法。应该可以大大加快速度。

def mixing_function(sig,onset):
    maxlen = np.max([o + len(s) for o, s in zip(onset, sig)])
    sig1 = np.zeros(maxlen)
    sig2 = np.zeros(maxlen)
    sig3 = np.zeros(maxlen)
    sig1[onset[0]:onset[0] + len(sig[0])] = sig[0]
    sig2[onset[1]:onset[1] + len(sig[1])] = sig[1]
    sig3[onset[2]:onset[2] + len(sig[2])] = sig[2]
    result = sig1+sig2+sig3
    print(sig1)
    print(sig2)
    print(sig3)
    print(result)