Skip to content Skip to sidebar Skip to footer

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 m

Solution 1:

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

Solution 2:

As of version 1.10.0, numpy.interp takes a period keyword: http://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html

Solution 3:

I think the following code does exactly what you ask for. First, it unwraps the angles such that there never is a jump larger than 180 degrees between consecutive values (see MATLAB's nice documentation for unwrap), then interpolation is performed, and finally np.mod is used such that the returned angles are in the interval [0, 360).

import numpy as np

def circular_interpolation(x: np.ndarray, xp: np.ndarray, fp: np.ndarray, period: float=360) -> np.ndarray:
    """
    One dimensional linear interpolation for monotonically increasing sample points where points first are unwrapped,
    secondly interpolated and finally bounded within the specified period.
    
    :param x: The x-coordinates at which to evaluate the interpolated values.
    :param xp: The x-coordinates of the data points, must be increasing.
    :param fp: The y-coordinates of the data points, same length as `xp`.
    :param period: Size of the range over which the input wraps.
    :return: The interpolated values, same shape as `x`.
    """
    
    y = np.mod(np.interp(x, xp, np.unwrap(fp, period=period)), period)
    return y

Note that the period option for np.unwrap is new in version 1.21.0 of Numpy. For older versions of Numpy, you could convert to radians before performing the interpolation.

Post a Comment for "Bounded Circular Interpolation In Python"