Resample a numpy array

Question:

It’s easy to resample an array like

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

with an integer resampling factor. For instance, with a factor 2 :

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

But with a non-integer resampling factor, it doesn’t work so easily :

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

It should be (with linear interpolation):

[1 2.5 4 5.5 7 8.5 10]

or (by taking the nearest neighbour in the array)

[1 3 4 6 7 9 10]

How to resample a numpy array with a non-integer resampling factor?

Example of application: audio signal resampling / repitching

Asked By: Basj

||

Answers:

NumPy has numpy.interp which does linear interpolation:

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 has scipy.interpolate.interp1d which can do linear and nearest interpolation (though which point is nearest might not be obvious):

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.])
Answered By: xnx

And if you want the integer sampling

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]
Answered By: EngineeredE

Since you mention this being data from an audio .WAV file, you might look at scipy.signal.resample.

Resample x to num samples using Fourier method along the given axis.

The resampled signal starts at the same value as x but is sampled
with a spacing of len(x) / num * (spacing of x). Because a
Fourier method is used, the signal is assumed to be periodic.

Your linear array a is not a good one to test this on, since it isn’t periodic in appearance. But consider sin data:

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

compare these with either

y1-np.sin(x1) # or
plot(x, y, x1, y1)
Answered By: hpaulj

As scipy.signal.resample can be very slow, I searched for other algorithms adapted for audio.

It seems that Erik de Castro Lopo’s SRC (a.k.a. Secret Rabbit Code a.k.a. libsamplerate) is one of the best resampling algorithms available.

  • 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')  
    

Interesting reading / comparison of many resampling solutions:
http://signalsprocessed.blogspot.com/2016/08/audio-resampling-in-python.html


Addendum: comparison of spectrograms of a resampled frequency sweep (20hz to 20khz):

1) Original

2) Resampled with libsamplerate / samplerate module

3) Resampled with numpy.interp (“One-dimensional linear interpolation”):

Answered By: Basj

In signal processing, you can think of resampling as basically rescaling the array and interpolating the missing values or values with non-integer index using nearest, linear, cubic, etc methods.

Using scipy.interpolate.interp1d, you can achieve one dimensional resampling using the following function

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')

yields

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

and

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

yields

array([ 1.,  2.,  4.,  5.,  7.,  8., 10.])
Answered By: fabda01