重采样一个numpy数组。

30

像这样重新采样数组很容易

 a = numpy.array([1,2,3,4,5,6,7,8,9,10])

使用整数重采样因子。例如,使用2倍因子:

b = a[::2]    # [1 3 5 7 9]

但是使用非整数重采样因子,情况就不那么简单了:

c = a[::1.5]    # [1 2 3 4 5 6 7 8 9 10]  => not what is needed...

它应该是(使用线性插值):

[1 2.5 4 5.5 7 8.5 10]

或者(通过在数组中选择最近的邻居)

[1 3 4 6 7 9 10]

如何使用非整数重采样因子对numpy数组进行重采样?

应用示例:音频信号重采样/变速处理


期望的行为是什么?在数组中进行线性插值还是最近邻插值? - wflynny
@wflynny 两种方法都可以... 如果使用最近邻居算法,可能甚至不需要在内存中复制数组,只需创建一个数组的新“视图”即可,对吧?(最终我可能会使用线性插值以获得更好的质量) - Basj
1
可能需要使用scipy.interpolate.interp1d或者其他scipy插值函数之一。 - reptilicus
“重新采样”是描述::2索引方式的一种不寻常的方式。 numpy数组(以及Python列表)并非主要被视为样本(尽管它们的值可能代表其他东西的样本)。 - hpaulj
@hpaulj 我使用了“重采样”这个词,因为我在一个.WAV文件中使用了numpy数组来存储音频数据。在这个数组上进行操作被称为音频中的“重采样”,或者“改变音高”,这取决于我们如何使用它。 - Basj
显示剩余2条评论
5个回答

33

NumPy拥有numpy.interp函数,可进行线性插值:

In [1]: numpy.interp(np.arange(0, len(a), 1.5), np.arange(0, len(a)), a)
Out[1]: array([  1. ,   2.5,   4. ,   5.5,   7. ,   8.5,  10. ])

SciPy有scipy.interpolate.interp1d函数,它可以进行线性和最近邻插值(虽然哪个点是最近的可能不明显):

In [2]: from scipy.interpolate import interp1d
In [3]: xp = np.arange(0, len(a), 1.5)
In [4]: lin = interp1d(np.arange(len(a)), a)

In [5]: lin(xp)
Out[5]: array([  1. ,   2.5,   4. ,   5.5,   7. ,   8.5,  10. ])

In [6]: nearest = interp1d(np.arange(len(a)), a, kind='nearest')

In [7]: nearest(xp)
Out[7]: array([  1.,   2.,   4.,   5.,   7.,   8.,  10.])

27
作为scipy.signal.resample可能非常缓慢,我搜索了其他适用于音频的算法。
看起来Erik de Castro Lopo的SRC(又称Secret Rabbit Code或libsamplerate)是最好的重采样算法之一。
  • It is used by scikit's scikit.samplerate, but this library seems to be complicated to install (I gave up on Windows).

  • Fortunately, there is an easy-to-use and easy-to-install Python wrapper for libsamplerate, made by Tino Wagner: https://pypi.org/project/samplerate/. Installation with pip install samplerate. Usage:

    import samplerate
    from scipy.io import wavfile
    sr, x = wavfile.read('input.wav')  # 48 khz file
    y = samplerate.resample(x, 44100 * 1.0 / 48000, 'sinc_best')  
    

这是一篇关于多种重采样解决方案的有趣阅读/比较文章: http://signalsprocessed.blogspot.com/2016/08/audio-resampling-in-python.html


附录: 重新采样的频率扫描(20hz至20khz)的光谱图比较:

1)原始

2) 使用libsamplerate / samplerate 模块重新采样

3) 使用numpy.interp(“一维线性插值”)重新采样:


13

既然您提到这是来自音频.WAV文件的数据,您可以看一下scipy.signal.resample

使用傅里叶方法沿给定轴将x重新采样为num个样本。

重新采样后的信号从与x相同的值开始,但采样间隔为len(x) / num * (spacing of x)。因为使用了傅里叶方法,所以假定信号是周期性的。

您的线性数组a不适合用于测试,因为它在外观上不是周期性的。但考虑使用sin数据:

x=np.arange(10)
y=np.sin(x)
y1, x1 =signal.resample(y,15,x)  # 10 pts resampled at 15

将这些与任何一个进行比较

y1-np.sin(x1) # or
plot(x, y, x1, y1)

1
在@hpaulj工作很好,但是scipy.signal.resample可能会非常慢! - Basj
你好,感谢您的回答和点赞(不是我的问题)!但是如果信号不是周期性的呢?有没有一种重新采样的方法?我在这里提出了一个相关的问题-https://stats.stackexchange.com/questions/398752/how-to-deal-with-uneven-number-of-intervals-and-hence-uneven-number-of-features - Noprogexprnce mathmtcn
如果信号不是周期性的,则可能需要使用某种插值方法。请查看此答案https://dev59.com/ol4b5IYBdhLWcg3wdxjG#55747293 - fabda01

4

如果你需要整数采样

a = numpy.array([1,2,3,4,5,6,7,8,9,10])
factor = 1.5
x = map(int,numpy.round(numpy.arange(0,len(a),factor)))
sampled = a[x]

很好!你认为从速度上来说,这种解决方案比其他方案更有效吗? - Basj
可能不会比Scipy / Numpy的解决方案更快,只是为您提供选择。 - EngineeredE

4
在信号处理中,您可以将重新采样视为基本上重新调整数组大小并使用最近、线性、立方等方法插值缺失的值或非整数索引的值。
使用 scipy.interpolate.interp1d,您可以通过以下函数实现一维重新采样。
def resample(x, factor, kind='linear'):
    n = np.ceil(x.size / factor)
    f = interp1d(np.linspace(0, 1, x.size), x, kind)
    return f(np.linspace(0, 1, n))

e.g.:

a = np.array([1,2,3,4,5,6,7,8,9,10])
resample(a, factor=1.5, kind='linear')

产量
array([ 1. ,  2.5,  4. ,  5.5,  7. ,  8.5, 10. ])

并且

a = np.array([1,2,3,4,5,6,7,8,9,10])
resample(a, factor=1.5, kind='nearest')

产量
array([ 1.,  2.,  4.,  5.,  7.,  8., 10.])

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