33

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

7
  • What is the desired behavior? Linear interpolation or nearest neighbor in the array? Commented Mar 16, 2015 at 19:32
  • @wflynny Both would work... If nearest neighboor, probably it's even not necessary to duplicate the array in memory, just a new "view" of the array might be possible, right? (At the end I'll probably use linear interp for better quality) Commented Mar 16, 2015 at 19:34
  • 1
    probably have to use scipy.interpolate.interp1d or one of the other interpolation routines in scipy Commented Mar 16, 2015 at 19:38
  • 'resampling' is an unusual way of describing the ::2 way of indexing. numpy arrays (and Python lists) are not primarily seen as samples (though their values may represent samples of something else). Commented Mar 16, 2015 at 21:34
  • @hpaulj I used the word resampling, because I use a numpy array for audio data contained in a .WAV files. Doing this on this array is called "resampling" in audio / or "repitching", it depends on how we use it. Commented Mar 16, 2015 at 21:43

5 Answers 5

35

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.])
Sign up to request clarification or add additional context in comments.

Comments

35

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"):

Comments

13

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)

3 Comments

Working @hpaulj but scipy.signal.resample can be very slow!
Hello and thanks for your answer and upvoted (not my question)!. But what if the signal isn't periodic? Is there a way to resample then? I asked a related queston here- stats.stackexchange.com/questions/398752/…
@Noprogexprncemathmtcn If the signal is not periodic you might want to use some kind of interpolation. Check this answer stackoverflow.com/a/55747293/6327658
4

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.])

Comments

4

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]

2 Comments

Nice as well! Do you think it is more efficient that the other solution, in terms of speed?
probably not faster than scipy / numpy solution. just giving you options.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.