This question is half programming but also half mathematics. I want to interpolate a set of points by a curve without adding unnecessary extremums staying \"close to the lin
You can use the linear interpolation and then filter it (with a mean filter) :
size = 51.0;
fun = interpolate.interp1d(xp, yp,kind='linear');
filt = (1/size)*np.ones(size);
yc = signal.convolve( fun(xc),filt,'same');
With the parameter size
you can control the smoothing degree.
This is the integrated code:
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from scipy import interpolate,signal
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
list_points=[(-3,0.1),(-2,0.15),(0,4),(2,-6),(4,-2),(7,-0.15),(8,-0.1)]
(xp,yp)=zip(*list_points)
xc=np.linspace(min(xp),max(xp),300)
########################################################
size = 41.0;#Put here any odd number
fun = interpolate.interp1d(xp, yp,kind='linear');
filt = (1/size)*np.ones(size);
yc = signal.convolve(fun(xc),filt,'same');
########################################################
plt.plot(xp,yp,'o',color='black',ms=5)
plt.plot(xc,yc)
plt.plot(xc,fun(xc))
plt.show()
While not exactly the same(?), your question is similar to this one, so perhaps the same answer would be useful. You can try a monotonic interpolator. The PchipInterpolator class (which you can refer to by its shorter alias pchip
) in scipy.interpolate
can be used. Here's a version of your script with a curve created using pchip
included:
import numpy as np
from scipy.interpolate import interp1d, pchip
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
list_points = [(-3,0.1),(-2,0.15),(0,4),(2,-6),(4,-2),(7,-0.15),(8,-0.1)]
(xp,yp) = zip(*list_points)
fun = interp1d(xp,yp,kind='cubic')
xc = np.linspace(min(xp),max(xp),300)
plt.plot(xp,yp,'o',color='black',ms=5)
plt.plot(xc,fun(xc))
fun2 = interp1d(xp,yp,kind='linear')
plt.plot(xc,fun2(xc))
p = pchip(xp, yp)
plt.plot(xc, p(xc), 'r', linewidth=3, alpha=0.6)
plt.show()
The plot it generates is shown below.
Have you tried a quadratic spline instead - though I am not convinced that will help. Another fudge option is to add additional data-points very close to your maximums. e.g. at (-0.05,4) and (1.95, -6) - this would cause the cubic spline algorithm to flatten those areas near the maximum. Depends what you are trying to achieve. There are techniques to constrain maximum and minimum of cubic splines but I am not familiar enough with those or python / matplotlib to help, sorry!