在numpy数组中查找满足条件的大量连续值

paf*_*fcu 20 python search numpy

我将一些音频数据加载到numpy数组中,我希望通过查找静音部分来分割数据,即音频幅度在一段时间内低于某个阈值的部分.

一个非常简单的方法是这样的:

values = ''.join(("1" if (abs(x) < SILENCE_THRESHOLD) else "0" for x in samples))
pattern = re.compile('1{%d,}'%int(MIN_SILENCE))                                                                           
for match in pattern.finditer(values):
   # code goes here
Run Code Online (Sandbox Code Playgroud)

上面的代码找到至少MIN_SILENCE个连续元素小于SILENCE_THRESHOLD的部分.

现在,显然,上面的代码非常低效,并且滥用正则表达式.是否有其他方法更有效,但仍然会产生同样简单和短的代码?

Joe*_*ton 33

这是一个基于numpy的解决方案.

我认为(?)它应该比其他选项更快.希望它相当清楚.

但是,它确实需要两倍于各种基于发生器的解决方案的内存.只要您可以在内存中保存数据的单个临时副本(对于diff),以及与数据长度相同的布尔数组(每个元素1位),它应该非常高效...

import numpy as np

def main():
    # Generate some random data
    x = np.cumsum(np.random.random(1000) - 0.5)
    condition = np.abs(x) < 1

    # Print the start and stop indicies of each region where the absolute 
    # values of x are below 1, and the min and max of each of these regions
    for start, stop in contiguous_regions(condition):
        segment = x[start:stop]
        print start, stop
        print segment.min(), segment.max()

def contiguous_regions(condition):
    """Finds contiguous True regions of the boolean array "condition". Returns
    a 2D array where the first column is the start index of the region and the
    second column is the end index."""

    # Find the indicies of changes in "condition"
    d = np.diff(condition)
    idx, = d.nonzero() 

    # We need to start things after the change in "condition". Therefore, 
    # we'll shift the index by 1 to the right.
    idx += 1

    if condition[0]:
        # If the start of condition is True prepend a 0
        idx = np.r_[0, idx]

    if condition[-1]:
        # If the end of condition is True, append the length of the array
        idx = np.r_[idx, condition.size] # Edit

    # Reshape the result into two columns
    idx.shape = (-1,2)
    return idx

main()
Run Code Online (Sandbox Code Playgroud)

  • @daryl - 感谢您注意到这一变化!相反,做"d = np.diff(condition.astype(int))"可能更清楚,尽管这主要是个人偏好的问题. (2认同)

And*_*rew 7

稍微草率,但简单快速,如果你不介意使用scipy:

from scipy.ndimage import gaussian_filter
sigma = 3
threshold = 1
above_threshold = gaussian_filter(data, sigma=sigma) > threshold
Run Code Online (Sandbox Code Playgroud)

这个想法是数据的安静部分将平滑到低幅度,而响亮的区域则不会.调整'sigma'以影响'安静'区域必须有多长; 调整'阈值'以影响它必须是多么安静.对于大sigma,这会减慢,此时使用基于FFT的平滑可能会更快.

这样做的另一个好处是,单个"热像素"不会破坏您的静音发现,因此您对某些类型的噪声不太敏感.


And*_*bis 6

有一个非常方便的解决方案scipy.ndimage.对于数组:

a = array([1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0])
Run Code Online (Sandbox Code Playgroud)

这可能是应用于另一个数组的条件的结果,找到连续的区域就像这样简单:

regions = scipy.ndimage.find_objects(scipy.ndimage.label(a)[0])
Run Code Online (Sandbox Code Playgroud)

然后,可以对这些区域应用任何功能,例如:

[np.sum(a[r]) for r in regions]
Run Code Online (Sandbox Code Playgroud)