pythonnumpyscipyinterpolation

bounded circular interpolation in python


My question is something similar to the question here. In simple term I have a time series angle data which is bounded between [0, 360]. I need to compute an iterpolation between measurements. Currently, I am using scipy.interpolate.interp1d. To make my question clear here is an example,

import numpy as np
from scipy import interpolate
data = np.array([[0, 2, 4], [1, 359, 1]]) # first row time index, second row angle measurements
f = interpolate.interp1d(data[0, :], data[1, :], kind='linear', bounds_error=False, fill_value=None)
f([1, 3])

this will result in [ 180., 180.]. However between time 2 and time 4 the angle changed from 359 to 1, that is only a 2 degree change and the interpolated value at 3 should have been 0. The angles are changing in CCW direction through time.

Finally, my question is this,

Is there any standard module that I can use to achieve this?

Just because I want to avoid custom method as much as possible!


Solution

  • Just add the 360° complement each time you detect there is a jump and revert back to the first 360 degrees by using the modulo operation. For example:

    In [1]: import numpy as np
    
    In [2]: from scipy import interpolate
    
    In [3]: data = np.array([[0, 2, 4, 6, 8], [1, 179, 211, 359, 1]])
    
    In [4]: complement360 = np.rad2deg(np.unwrap(np.deg2rad(data[1])))
    
    In [5]: complement360
    Out[5]: array([   1.,  179.,  211.,  359.,  361.])
    
    In [6]: f = interpolate.interp1d(data[0], complement360, kind='linear', bounds_error=False, fill_value=None)
    
    In [7]: f(np.arange(9))
    Out[7]: array([   1.,   90.,  179.,  195.,  211.,  285.,  359.,  360.,  361.])
    
    In [8]: f(np.arange(9))%360
    Out[8]: array([   1.,   90.,  179.,  195.,  211.,  285.,  359.,    0.,    1.])
    

    Remark, I did add a few extra values here, as otherwise there is no realistic way for np.unwrap to know in which direction the angle is increasing, and that is probably also how you know it is increasing in that way (the difference between consecutive values is less than 180° unless there's an actual discontinuity).

    If however you really have data that makes angular jumps larger than 180° between 2 consecutive items, but you know the direction in which the angles are changing (e.g. CCW) and that it is changing monotonously, then you could detect it like so:

    In [31]: data = np.array([1, 359, 1, 60, 359, 177, 2])  # mock-data
    
    In [32]: jumps = np.diff(data)<0  # assumptions: angle increases stricly monotonously CCW
    
    In [33]: np.hstack((data[0], data[1:] + np.cumsum(np.sign(d)<0)*360))
    Out[33]: array([   1,  359,  361,  420,  719,  897, 1082])