Based on your last comment (the fact that you might have multiple measurements and you don't care whether a certain set of measurements is above or below the fit), I think what you are looking for is a spline fit. You can do this using the scipy.interpolate B-spline routines. The script below generates three sets of data based on a function (that you can consider to model your system). For the first set, it adds some random error to the function "above" (red dots), for the second below (blue dots) and for the third just above and below (green dots). The black line is a plot of the exact function (without errors).
And then you use the scipy.interpolate.splrep
function to generate a B-spline representation for this data. A B-spline is a piecewise combination of polynomial functions, typically cubic polynomials (cubic B-splines) which have some nice properties. If you want to know more about them, I would strongly suggest to read works by C. de Boor and P. Dierckx. The scipy.interpolate.splrep
function returns a tuple of the knots, the coefficients and the order. Using the scipy.interpolate.splev
function, you can then evaluate the B-spline representation in a number of points in the interval of the fit.
The script below plots an evaluation of the B-spline in magenta.
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as ip
def f(x):
return np.sin(np.pi*2*x)*np.exp(-2*x)
N=20
err=0.15
data = np.zeros([3*N,2])
#red dots above curve
data[:N,0] = np.sort(np.random.rand(N))
data[:N,1] = f(data[:N,0])+err*np.random.rand(N)
#blue dots below curve
data[N:2*N,0] = np.sort(np.random.rand(N))
data[N:2*N,1] = f(data[N:2*N,0])-err*np.random.rand(N)
#green dots above and below curve
data[2*N:,0] = np.sort(np.random.rand(N))
data[2*N:,1] = f(data[2*N:,0])-2*err*(np.random.rand(N)-0.5)
plt.plot(data[:N,0],data[:N,1],'ro')
plt.plot(data[N:2*N,0],data[N:2*N,1],'bo')
plt.plot(data[2*N:,0],data[2*N:,1],'go')
data = data[data[:,0].argsort()]
x = data[:,0]
y = data[:,1]
y_exact = f(x)
plt.plot(x,y_exact,'k')
w = np.ones([len(x),1])
spl = ip.splrep(x,y,w)
xn = np.linspace(0,1.0,100)
sple = ip.splev(xn,spl)
plt.plot(xn,sple,'m')
plt.show()