Bounded Circular Interpolation In Python
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"