Resample a numpy array

67,299

Solution 1

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

Solution 2

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

Solution 3

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)

Solution 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]

Solution 5

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.])
Share:
67,299

Related videos on Youtube

Basj
Author by

Basj

I work on R&D involving Python, maths, machine learning, deep learning, data science, product design, and MacGyver solutions to complex problems. I love prototyping, building proofs-of-concept. For consulting/freelancing inquiries : [email protected]

Updated on July 16, 2022

Comments

  • Basj
    Basj almost 2 years

    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

    • wflynny
      wflynny about 9 years
      What is the desired behavior? Linear interpolation or nearest neighbor in the array?
    • Basj
      Basj about 9 years
      @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)
    • reptilicus
      reptilicus about 9 years
      probably have to use scipy.interpolate.interp1d or one of the other interpolation routines in scipy
    • hpaulj
      hpaulj about 9 years
      '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).
    • Basj
      Basj about 9 years
      @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.
    • Alejandro
      Alejandro about 9 years
      The example you are using, in particular using indexing and resampling as the same thing is very confusing. I suggest you to modify your question by removing the indexing part. I guess your problem is to interpolate a function that map (0,1,2,3,.... N) to (0,1,2,3,....,N), and you are trying to find the value at intermediate points. To do so, use scipy.interpolation tools, such us interp1d
    • hpaulj
      hpaulj about 9 years
      Isn't audio downsampling more involved than simply throwing away half of the data? The literature talks about sinc interpolation and filtering.
  • Basj
    Basj about 9 years
    Nice as well! Do you think it is more efficient that the other solution, in terms of speed?
  • EngineeredE
    EngineeredE about 9 years
    probably not faster than scipy / numpy solution. just giving you options.
  • Basj
    Basj over 5 years
    Working @hpaulj but scipy.signal.resample can be very slow!
  • Noprogexprnce mathmtcn
    Noprogexprnce mathmtcn about 5 years
    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/…
  • fabda01
    fabda01 about 5 years
    @Noprogexprncemathmtcn If the signal is not periodic you might want to use some kind of interpolation. Check this answer stackoverflow.com/a/55747293/6327658