How to fit three gaussian peaks in python? - python

I'm trying to fit the three peaks using python. I'm able to fit the first peak, but having problem in converging the fitting function to the next two peaks. Can someone please help me?
I guess there is some problem with the initial guesses!
Here is the code and figure:
from __future__ import division
import numpy as np
import scipy.signal
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.family'] = 'sans-serif'
""" Fitting Function"""
def _2gauss(x, amp1, cen1, sigma1, amp2, cen2, sigma2):
return amp1*(1/(sigma1*(np.sqrt(2*np.pi))))*(np.exp((-1.0/2.0)*(((x-cen1)/sigma1)**2))) + \
amp2*(1/(sigma2*(np.sqrt(2*np.pi))))*(np.exp((-1.0/2.0)*(((x-cen2)/sigma2)**2)))+ \
amp3*(1/(sigma3*(np.sqrt(2*np.pi))))*(np.exp((-1.0/2.0)*(((x-cen3)/sigma3)**2)))
data_12 = np.loadtxt("ExcitationA.txt", skiprows=30, dtype=np.float64)
xData, yData = np.hsplit(data_12,2)
x = xData[:,0]
y = yData[:,0]
n = len(x)
amp1 = 400
sigma1 = 10
cen1 = 400
amp2 = 400
sigma2 = 5
cen2 = 400
amp3 = 340
sigma3 = 6
cen3 = 340
popt, pcov = curve_fit(_2gauss, x, y, p0= [amp1, cen1, sigma1, amp2, cen2, sigma2])
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(x, y, 'b', markersize=1, label="12°C")
ax.plot(x, _2gauss(x, *popt), markersize='1',label="Fit function", linewidth=4, color='purple')
plt.show()

As there are 9 parameters, to obtain a good fit, the initial values for those parameters should be close. An idea is to experiment drawing
p0 = [amp1, cen1, sigma1, amp2, cen2, sigma2, amp3, cen3, sigma3]
ax.plot(x, _2gauss(x, *p0))
until the parameters are more or less equal. In this example, it is important that the centers cen1, cen2 and cen3 are close to the observed local maxima (340, 355, 375).
Once you have reasonable initial values, you can start the fit. Also note that in the originally posted example code amp3, cen3, sigma3 are missing as parameters to the function _2gauss.
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
def gauss_1(x, amp1, cen1, sigma1):
return amp1 * (1 / (sigma1 * (np.sqrt(2 * np.pi)))) * (np.exp((-1.0 / 2.0) * (((x - cen1) / sigma1) ** 2)))
def gauss_3(x, amp1, cen1, sigma1, amp2, cen2, sigma2, amp3, cen3, sigma3):
""" Fitting Function"""
return amp1 * (1 / (sigma1 * (np.sqrt(2 * np.pi)))) * (np.exp((-1.0 / 2.0) * (((x - cen1) / sigma1) ** 2))) + \
amp2 * (1 / (sigma2 * (np.sqrt(2 * np.pi)))) * (np.exp((-1.0 / 2.0) * (((x - cen2) / sigma2) ** 2))) + \
amp3 * (1 / (sigma3 * (np.sqrt(2 * np.pi)))) * (np.exp((-1.0 / 2.0) * (((x - cen3) / sigma3) ** 2)))
x = np.array([300.24, 301.4, 302.56, 303.72, 304.88, 306.04, 307.2, 308.36, 309.51, 310.67, 311.83, 312.99, 314.04, 314.93, 315.77, 316.56, 317.3, 318.03, 318.77, 319.5, 320.23, 321.02, 321.86, 325.76, 326.6, 327.54, 328.49, 329.17, 329.69, 330.27, 330.84, 331.16, 335.85, 336.37, 337.05, 337.79, 339.58, 341.43, 342.42, 343.87, 345.01, 346.07, 346.91, 347.53, 348.06, 348.53, 348.89, 351.33, 351.8, 352.11, 352.42, 352.75, 353.15, 353.6, 354.04, 354.36, 354.87, 355.77, 356.72, 357.36, 357.83, 358.25, 358.69, 358.96, 359.29, 359.61, 359.93, 360.25, 360.58, 360.86, 361.16, 361.39, 361.61, 361.96, 362.3, 362.62, 363.0, 363.43, 363.94, 364.55, 365.18, 366.14, 367.3, 368.19, 368.82, 369.45, 370.03, 371.07, 371.54, 371.96, 372.31, 372.69, 373.11, 373.52, 373.99, 374.67, 375.68, 376.58, 377.11, 377.54, 377.81, 378.09, 378.4, 378.71, 378.94, 379.08, 379.3, 379.52, 379.73, 379.95, 380.17, 380.34, 380.61, 380.82, 380.99, 381.22, 381.44, 381.66, 381.88, 382.1, 382.32, 382.53, 382.75, 382.97, 383.24, 383.74, 384.0, 384.28, 384.49, 384.71, 384.92, 385.14, 385.36, 385.58, 385.9, 386.26, 386.6, 386.92, 387.29, 387.71, 388.31, 388.84, 389.53, 390.38, 391.39, 392.56, 393.72, 394.89, 396.05, 397.22, 397.69, 398.38, 398.86, 399.54, 400.02, 400.71, 401.18, 401.87, 402.34, 403.03, 403.19, 404.19, 405.36, 406.52, 407.68, 408.84, 410.01, 411.17, 412.33, 413.49, 414.65, 415.81, 416.98, 417.61])
y = np.array([3.6790e-01, 4.1930e-01, 4.6530e-01, 5.1130e-01, 5.6300e-01, 6.1750e-01, 6.6780e-01, 7.2950e-01, 7.8830e-01, 8.4960e-01, 9.0950e-01, 9.6660e-01, 1.0463e+00, 1.1324e+00, 1.2241e+00, 1.3026e+00, 1.3889e+00, 1.4780e+00, 1.5598e+00, 1.6432e+00, 1.7318e+00, 1.8256e+00, 1.9050e+00, 2.1595e+00, 2.2477e+00, 2.3343e+00, 2.4183e+00, 2.5115e+00, 2.5970e+00, 2.6825e+00, 2.7657e+00, 2.8198e+00, 3.8983e+00, 3.9956e+00, 4.0846e+00, 4.1526e+00, 4.2787e+00, 4.2256e+00, 4.2412e+00, 4.2731e+00, 4.3265e+00, 4.4073e+00, 4.4905e+00, 4.5831e+00, 4.6717e+00, 4.7660e+00, 4.8395e+00, 5.6288e+00, 5.7239e+00, 5.8141e+00, 5.9076e+00, 6.0026e+00, 6.1034e+00, 6.2157e+00, 6.3235e+00, 6.4114e+00, 6.5063e+00, 6.5709e+00, 6.5175e+00, 6.4349e+00, 6.3479e+00, 6.2638e+00, 6.2102e+00, 6.0616e+00, 5.9664e+00, 5.8697e+00, 5.7625e+00, 5.6546e+00, 5.5494e+00, 5.4404e+00, 5.3384e+00, 5.2396e+00, 5.1462e+00, 5.0412e+00, 4.9467e+00, 4.8592e+00, 4.7655e+00, 4.6709e+00, 4.5807e+00, 4.4803e+00, 4.3947e+00, 4.3347e+00, 4.3286e+00, 4.3918e+00, 4.4800e+00, 4.5637e+00, 4.6489e+00, 4.8435e+00, 4.9454e+00, 5.0396e+00, 5.1258e+00, 5.2200e+00, 5.3082e+00, 5.3945e+00, 5.4874e+00, 5.5974e+00, 5.6396e+00, 5.5880e+00, 5.4984e+00, 5.4082e+00, 5.3213e+00, 5.2270e+00, 5.1271e+00, 5.0247e+00, 4.9258e+00, 4.8324e+00, 4.7317e+00, 4.6336e+00, 4.5323e+00, 4.4258e+00, 4.3166e+00, 4.2152e+00, 4.1011e+00, 3.9754e+00, 3.8646e+00, 3.7401e+00, 3.6061e+00, 3.4715e+00, 3.3381e+00, 3.2120e+00, 3.0865e+00, 2.9610e+00, 2.8361e+00, 2.7126e+00, 2.6289e+00, 2.2796e+00, 2.1818e+00, 2.0747e+00, 1.9805e+00, 1.8864e+00, 1.7942e+00, 1.7080e+00, 1.6236e+00, 1.5279e+00, 1.4145e+00, 1.2931e+00, 1.1805e+00, 1.0785e+00, 9.8490e-01, 8.9590e-01, 7.9850e-01, 7.0670e-01, 6.2110e-01, 5.2990e-01, 4.4250e-01, 3.7360e-01, 3.1090e-01, 2.5880e-01, 2.0680e-01, 1.6760e-01, 1.4570e-01, 1.2690e-01, 1.1060e-01, 9.5900e-02, 9.0600e-02, 8.0600e-02, 7.0600e-02, 5.8100e-02, 4.4200e-02, 4.4200e-02, 4.4200e-02, 4.1400e-02, 3.4900e-02, 2.4200e-02, 1.9600e-02, 1.5300e-02, 1.5000e-02, 1.1800e-02, 1.3200e-02, 7.8000e-03, 5.0000e-03, 1.0000e-02, 4.6000e-03, 0.0])
amp1 = 100
sigma1 = 9
cen1 = 375
amp2 = 100
sigma2 = 7
cen2 = 355
amp3 = 100
sigma3 = 10
cen3 = 340
p0 = [amp1, cen1, sigma1, amp2, cen2, sigma2, amp3, cen3, sigma3]
y0 = gauss_3(x, *p0)
popt, pcov = curve_fit(gauss_3, x, y, p0=p0)
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(x, y, 'b', label="given curve")
ax.plot(x, y0, 'g', ls=':', label="initial fit params")
ax.plot(x, gauss_3(x, *popt), ls=':', label="Fit function", linewidth=4, color='purple')
for i, (a, c, s )in enumerate( popt.reshape(-1, 3)):
ax.plot(x, gauss_1(x, a, c, s), ls='-', label=f"gauss {i+1}", linewidth=1, color='crimson')
ax.legend()
ax.autoscale(axis='x', tight=True)
plt.show()

Related

curve_fit() got multiple values for argument 'p0' - What does this mean for the curve fit?

So, I wrote this script to interpret and graph data from a dynamic light scattering experiment. However, when I run the program I get this curvefit error for p0. Does any one have any idea what the problem could be? I've tried various things without any success. I need all values, so it doesn't really make much sense. Here is the script:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def exp_fun(x, A, tau,beta):
res = A * np.exp(-(x / tau) ** beta)
return res
def exp_fun2(x, A, tau, tau2):
res = (1-A) * np.exp(-(x / tau)) + ( A) * np.exp(-(x / tau2))
return res
def exp_fun3(x, A1, tau, tau2, tau3,A2):
res = (1 - A1-A2) * np.exp(-(x / tau)) + (A1) * np.exp(-(x / tau2))+ (A2) * np.exp(-(x / tau3))
return res
def exp_fun2_str1(x, A, tau, tau2, beta):
res = (1-A) * np.exp(-(x / tau)) + ( A) * np.exp(-(x / tau2) ** beta)
return res
def exp_fun2_str2(x, A, tau, tau2, beta, beta2):
res = (1-A)*np.exp(-(x / tau) ** beta) + ( A) * np.exp(-(x / tau2) ** beta2)
return res
filepath = "C:\\ref-bsa-10\\"
filename = 'BSA1000'
filenamestatic='bsa10table.txt'
numfiles = 24
# load the static data
static = np.loadtxt(filepath + filenamestatic , skiprows=1, usecols=[0, 1,8])
#following code changed due to unicode errors: probably because of text files
#static = pd.read_csv(filepath + filenamestatic , delimiter='\t',skiprows=1)
#staticdata= np.array(static.values)
#angles=staticdata[:,0]
q=np.sqrt(static[:,1])*10**-6
#load and analyse dynamics data
tau1 = np.zeros([numfiles])
tau2 = np.zeros([numfiles])
A = np.zeros([numfiles])
beta1 = np.zeros([numfiles])
beta2 = np.zeros([numfiles])
dbeta1 = np.zeros([numfiles])
dbeta2 = np.zeros([numfiles])
dtau2 = np.zeros([numfiles])
dtau1 = np.zeros([numfiles])
dA = np.zeros([numfiles])
colore = plt.cm.jet(np.linspace(0, 1, numfiles))
plt.figure()
ave_n=5
for i in range(numfiles):
data = pd.read_csv(filepath + filename + "{:02d}_averaged_corr.txt".format(i), delimiter='\t', skiprows=2)
print(data)
a = np.array(data.values)
g2=a[:, 1] / np.mean(a[1:10, 1])
dg2=a[:, 2] / np.mean(a[1:10, 1])
t=a[:, 0]
plt.errorbar(t, g2, dg2, color=colore[i])
popt, pcov = curve_fit(exp_fun, t, g2, dg2,
# A tau beta
bounds=([0.9, 0, 0, ],
[1.1, 10 ** 9, 2, ]),
p0 =([1, 100, 1, ]) )
plt.plot(t, exp_fun(t, popt[0], popt[1], popt[2]), color='red',
linestyle='dashed')
A[i] = popt[0]
dA[i] = np.sqrt(np.diag(pcov))[0]
tau1[i] = popt[1]
dtau1[i] = np.sqrt(np.diag(pcov))[1]
# tau2[i] = popt[2]
# dtau2[i] = np.sqrt(np.diag(pcov))[2]
beta1[i] = popt[2]
dbeta1[i] = np.sqrt(np.diag(pcov))[2]
# beta2[i] = popt[4]
# dbeta2[i] = np.sqrt(np.diag(pcov))[4]
plt.xlabel('$t$ (ms)')
plt.xscale('log')
plt.ylabel(r'$g_2$-1')
plt.grid('on')
plt.figure()
plt.errorbar(q,A, dA,marker='o',linestyle='none')
plt.xlabel('q ($\mathrm{\mu}$m$^{-1}$)')
plt.ylabel('A')
plt.grid('on')
#plt.figure()
#plt.errorbar(q**4,1/tau2, 1/(tau2**2)*dtau2,marker='o',linestyle='none')
#plt.xlabel('q$^4$ ($\mathrm{\mu}$m$^{-4}$)')
#plt.ylabel('$\Gamma_2$ (s$^{-1}$)')
#plt.grid('on')
plt.figure()
plt.errorbar(q**2,1/tau1, 1/(tau1**2)*dtau1,marker='o',linestyle='none')
poptDp, pcovDp = curve_fit(lambda x, *p: x * p[0], q ** 2,1/tau1, p0=[1, 0], bounds=(0, 1000),
sigma=1/(tau1**2)*dtau1, ftol=1e-14, xtol=1e-14) # old: 1e-15
plt.plot(q** 2 , q** 2 * poptDp[0] , color='magenta')
D=poptDp[0]
dD= np.sqrt(np.diag(pcovDp))[0]
plt.xlabel('q$^2$ ($\mathrm{\mu}$m$^{-2}$)')
plt.ylabel('$\Gamma_1$ (s$^{-1}$)')
plt.grid('on')
R=10*10**-9
Dms=D*10**3*(10**-6)**2
kb=1.380649*10**(-23)
eta=kb *(273.15+21)/(6*np.pi*R*Dms)
#comparing two functions
#plt.figure()
#plt.errorbar(q,1/tau2, 1/(tau2**2)*dtau2,marker='o',linestyle='none')
#plt.errorbar(q,1/tau1, 1/(tau1**2)*dtau1,marker='o',linestyle='none')
#plt.xlabel('q ($\mathrm{\mu}$m$^{-1}$)')
#plt.ylabel('$\Gamma$ (s$^{-1}$)')
#plt.grid('on')
#plt.xscale('log')
#plt.yscale('log')
plt.figure()
plt.errorbar(q,beta1, dbeta1,marker='o',linestyle='none')
#plt.errorbar(q,beta2, dbeta2,marker='o',linestyle='none')
plt.grid('on')
plt.xlabel('q ($\mathrm{\mu}$m$^{-1}$)')
plt.ylabel('KWW')

Scipy odeint. Gravitational motion

I solve the motion in gravitational field around the sun with scipy and mathplotlib and have a problem. My solution is not correct. It is not like in example. Formulas that I used.
from scipy.integrate import odeint
import scipy.constants as constants
import numpy as np
import matplotlib.pyplot as plt
M = 1.989 * (10 ** 30)
G = constants.G
alpha = 30
alpha0 = (alpha / 180) * np.pi
v00 = 0.7
v0 = v00 * 1000
def dqdt(q, t):
x = q[0]
y = q[2]
ax = - G * M * (x / ((x ** 2 + y ** 2) ** 1.5))
ay = - G * M * (y / ((x ** 2 + y ** 2) ** 1.5))
return [q[1], ax, q[3], ay]
vx0 = v0 * np.cos(alpha0)
vy0 = v0 * np.sin(alpha0)
x0 = -150 * (10 ** 11)
y0 = 0 * (10 ** 11)
q0 = [x0, vx0, y0, vy0]
N = 1000000
t = np.linspace(0.0, 100000000000.0, N)
pos = odeint(dqdt, q0, t)
x1 = pos[:, 0]
y1 = pos[:, 2]
plt.plot(x1, y1, 0, 0, 'ro')
plt.ylabel('y')
plt.xlabel('x')
plt.grid(True)
plt.show()
How can I fix this?
Maybe you can tell me solution with another method for example with Euler's formula or with using other library.
I will be very greatful if you help me.

Plot a ball rolling down a curve

I want to "animate" a circle rolling over the sin graph, I made a code where the circle moves rapidly down a straight line, now I want the same but the acceleration will be changing.
My previous code:
import numpy as np
import matplotlib.pyplot as plt
theta = np.arange(0, np.pi * 2, (0.01 * np.pi))
x = np.arange(-50, 1, 1)
y = x - 7
plt.figure()
for t in np.arange(0, 4, 0.1):
plt.plot(x, y)
xc = ((-9.81 * t**2 * np.sin(np.pi / 2)) / 3) + (5 * np.cos(theta))
yc = ((-9.81 * t**2 * np.sin(np.pi / 2)) / 3) + (5 * np.sin(theta))
plt.plot(xc, yc, 'r')
xp = ((-9.81 * t**2 * np.sin(np.pi / 2)) / 3) + (5 * np.cos(np.pi * t))
yp = ((-9.81 * t**2 * np.sin(np.pi / 2)) / 3) + (5 * np.sin(np.pi * t))
plt.plot(xp, yp, 'bo')
plt.pause(0.01)
plt.cla()
plt.show()
You can do this by numerically integrating:
dt = 0.01
lst_x = []
lst_y = []
t = 0
while t < 10: #for instance
t += dt
a = get_acceleration(function, x)
x += v * dt + 0.5 * a * dt * dt
v += a * dt
y = get_position(fuction, x)
lst_x.append(x)
lst_y.append(y)
This is assuming the ball never leaves your slope! If it does, you'll also have to integrate in y in a similar way as done in x!!
Where your acceleration is going to be equal to g * cos(slope).

Still having trouble with curve fitting

I already opened a question on this topic, but I wasn't sure, if I should post it there, so I opened a new question here.
I have trouble again when fitting two or more peaks. First problem occurs with a calculated example function.
xg = np.random.uniform(0,1000,500)
mu1 = 200
sigma1 = 20
I1 = -2
mu2 = 800
sigma2 = 20
I2 = -1
yg3 = 0.0001*xg
yg1 = (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (xg - mu1)**2 / (2 * sigma1**2) )
yg2 = (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (xg - mu2)**2 / (2 * sigma2**2) )
yg=yg1+yg2+yg3
plt.figure(0, figsize=(8,8))
plt.plot(xg, yg, 'r.')
I tried two different approaches, I found in the documentation, which are shown below (modified for my data), but both give me wrong fitting data and a messy chaos of graphs (I guess one line per fitting step).
1st attempt:
import numpy as np
from lmfit.models import PseudoVoigtModel, LinearModel, GaussianModel, LorentzianModel
import sys
import matplotlib.pyplot as plt
gauss1 = PseudoVoigtModel(prefix='g1_')
pars.update(gauss1.make_params())
pars['g1_center'].set(200)
pars['g1_sigma'].set(15, min=3)
pars['g1_amplitude'].set(-0.5)
pars['g1_fwhm'].set(20, vary=True)
#pars['g1_fraction'].set(0, vary=True)
gauss2 = PseudoVoigtModel(prefix='g2_')
pars.update(gauss2.make_params())
pars['g2_center'].set(800)
pars['g2_sigma'].set(15)
pars['g2_amplitude'].set(-0.4)
pars['g2_fwhm'].set(20, vary=True)
#pars['g2_fraction'].set(0, vary=True)
mod = gauss1 + gauss2 + LinearModel()
pars.add('intercept', value=0, vary=True)
pars.add('slope', value=0.0001, vary=True)
init = mod.eval(pars, x=xg)
out = mod.fit(yg, pars, x=xg)
print(out.fit_report(min_correl=0.5))
plt.figure(5, figsize=(8,8))
out.plot_fit()
When I include the 'fraction'-parameter, I often get
'NameError: name 'pv1_fraction' is not defined in expr='<_ast.Module object at 0x00000000165E03C8>'.
although it should be defined. I get this Error for real data with this approach, too.
2nd attempt:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import lmfit
def gauss(x, sigma, mu, A):
return A*np.exp(-(x-mu)**2/(2*sigma**2))
def linear(x, m, n):
return m*x + n
peak1 = lmfit.model.Model(gauss, prefix='p1_')
peak2 = lmfit.model.Model(gauss, prefix='p2_')
lin = lmfit.model.Model(linear, prefix='l_')
model = peak1 + lin + peak2
params = model.make_params()
params['p1_mu'] = lmfit.Parameter(value=200, min=100, max=250)
params['p2_mu'] = lmfit.Parameter(value=800, min=100, max=1000)
params['p1_sigma'] = lmfit.Parameter(value=15, min=0.01)
params['p2_sigma'] = lmfit.Parameter(value=20, min=0.01)
params['p1_A'] = lmfit.Parameter(value=-2, min=-3)
params['p2_A'] = lmfit.Parameter(value=-2, min=-3)
params['l_m'] = lmfit.Parameter(value=0)
params['l_n'] = lmfit.Parameter(value=0)
out = model.fit(yg, params, x=xg)
print out.fit_report()
plt.figure(8, figsize=(8,8))
out.plot_fit()
So the result looks like this, in both cases. It seems to plot all fitting attempts, but never solves it correctly. The best fitted parameters are in the range that I gave it.
Anyone knows this type of error? Or has any solutions for this? And does anyone know how to avoid the NameError when calling a model function from lmfit with those approaches?
I have a somewhat tolerable solution for you. Since I don't know how variable your data is, I cannot say that it will work in a general sense but should get you started. If your data is along 0-1000 and has two peaks or dips along a line as you showed, then it should work.
I used the scipy curve_fit and put all of the components of the function together into one function. One can pass starting locations into the curve_fit function. (you can probably do this with the lib you're using but I'm not familiar with it) There is a loop in loop where I vary the mu parameters to find the ones with the lowest squared error. If you are needing to fit your data many times or in some real-time scenario then this is not for you but if you just need to fit some data, launch this code and grab a coffee.
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
import pylab
from matplotlib import cm as cm
import time
def my_function_big(x, m, n, #lin vars
sigma1, mu1, I1, #gaussian 1
sigma2, mu2, I2): #gaussian 2
y = m * x + n + (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (x - mu1)**2 / (2 * sigma1**2) ) + (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (x - mu2)**2 / (2 * sigma2**2) )
return y
#make some data
xs = np.random.uniform(0,1000,500)
mu1 = 200
sigma1 = 20
I1 = -2
mu2 = 800
sigma2 = 20
I2 = -1
yg3 = 0.0001 * xs
yg1 = (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (xs - mu1)**2 / (2 * sigma1**2) )
yg2 = (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (xs - mu2)**2 / (2 * sigma2**2) )
ys = yg1 + yg2 + yg3
xs = np.array(xs)
ys = np.array(ys)
#done making data
#start a double loop...very expensive but this is quick and dirty
#it would seem that the regular optimizer has trouble finding the minima so i
#found that having the near proper mu values helped it zero in much better
start = time.time()
serr = []
_x = []
_y = []
for x in np.linspace(0, 1000, 61):
for y in np.linspace(0, 1000, 61):
cfiti = curve_fit(my_function_big, xs, ys, p0=[0, 0, 1, x, 1, 1, y, 1], maxfev=20000000)
serr.append(np.sum((my_function_big(xs, *cfiti[0]) - ys) ** 2))
_x.append(x)
_y.append(y)
serr = np.array(serr)
_x = np.array(_x)
_y = np.array(_y)
print 'done loop in loop fitting'
print 'time: %0.1f' % (time.time() - start)
gridsize=20
plt.subplot(111)
plt.hexbin(_x, _y, C=serr, gridsize=gridsize, cmap=cm.jet, bins=None)
plt.axis([_x.min(), _x.max(), _y.min(), _y.max()])
cb = plt.colorbar()
cb.set_label('SE')
plt.show()
ix = np.argmin(serr.ravel())
mustart1 = _x.ravel()[ix]
mustart2 = _y.ravel()[ix]
print mustart1
print mustart2
cfit = curve_fit(my_function_big, xs, ys, p0=[0, 0, 1, mustart1, 1, 1, mustart2, 1], maxfev=2000000000)
xp = np.linspace(0, 1000, 1001)
plt.figure()
plt.scatter(xs, ys) #plot synthetic dat
plt.plot(xp, my_function_big(xp, *cfit[0]), '-', label='fit function') #plot data evaluated along 0-1000
plt.legend(loc=3, numpoints=1, prop={'size':12})
plt.show()
pylab.close()
Good luck!
In your first attempt:
pars['g1_fraction'].set(0, vary=True)
The fraction must be a value between 0 and 1, but I believe that cannot be zero. Try to put something like 0.000001, and it will work.

Create scipy curve fitting definitions for fourier series dynamically

I'd like to achieve a fourier series development for a x-y-dataset using numpy and scipy.
At first I want to fit my data with the first 8 cosines and plot additionally only the first harmonic. So I wrote the following two function defintions:
# fourier series defintions
tau = 0.045
def fourier8(x, a1, a2, a3, a4, a5, a6, a7, a8):
return a1 * np.cos(1 * np.pi / tau * x) + \
a2 * np.cos(2 * np.pi / tau * x) + \
a3 * np.cos(3 * np.pi / tau * x) + \
a4 * np.cos(4 * np.pi / tau * x) + \
a5 * np.cos(5 * np.pi / tau * x) + \
a6 * np.cos(6 * np.pi / tau * x) + \
a7 * np.cos(7 * np.pi / tau * x) + \
a8 * np.cos(8 * np.pi / tau * x)
def fourier1(x, a1):
return a1 * np.cos(1 * np.pi / tau * x)
Then I use them to fit my data:
# import and filename
filename = 'data.txt'
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
z, Ua = np.loadtxt(filename,delimiter=',', unpack=True)
tau = 0.045
# plot data
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
# fits
popt, pcov = curve_fit(fourier8, z, Ua)
# further plots
Ua_fit8 = fourier8(z,*popt)
Ua_fit1 = fourier1(z,popt[0])
p2, = plt.plot(z,Ua_fit8)
p3, = plt.plot(z,Ua_fit1)
plt.show()
which works as desired:
But know I got stuck making it generic for arbitary orders of harmonics, e.g. I want to fit my data with the first fifteen harmonics and plot the first three harmonics.
How could I achieve that without defining fourier1, fourier2, fourier3 ... , fourier15?
Here is the example text file data.txt to play with it:
1.000000000000000021e-03,2.794682735905079767e+02
2.000000000000000042e-03,2.792294526290349950e+02
2.999999999999999629e-03,2.779794770690260179e+02
4.000000000000000083e-03,2.757183469104809888e+02
5.000000000000000104e-03,2.734572167519349932e+02
5.999999999999999258e-03,2.711960865933900209e+02
7.000000000000000146e-03,2.689349564348440254e+02
8.000000000000000167e-03,2.714324329982829909e+02
8.999999999999999320e-03,2.739299095617229796e+02
1.000000000000000021e-02,2.764273861251620019e+02
1.100000000000000110e-02,2.789248626886010243e+02
1.199999999999999852e-02,2.799669443683339978e+02
1.299999999999999940e-02,2.795536311643609793e+02
1.400000000000000029e-02,2.791403179603880176e+02
1.499999999999999944e-02,2.787270047564149991e+02
1.600000000000000033e-02,2.783136915524419805e+02
1.699999999999999775e-02,2.673604939531239779e+02
1.799999999999999864e-02,2.564072963538059753e+02
1.899999999999999953e-02,2.454540987544889958e+02
2.000000000000000042e-02,2.345009011551709932e+02
2.099999999999999784e-02,1.781413355804160119e+02
2.200000000000000219e-02,7.637540203022649621e+01
2.299999999999999961e-02,-2.539053151996269975e+01
2.399999999999999703e-02,-1.271564650701519952e+02
2.500000000000000139e-02,-2.289223986203409993e+02
2.599999999999999881e-02,-2.399383538664330047e+02
2.700000000000000316e-02,-2.509543091125239869e+02
2.800000000000000058e-02,-2.619702643586149975e+02
2.899999999999999800e-02,-2.729862196047059797e+02
2.999999999999999889e-02,-2.786861050144170235e+02
3.099999999999999978e-02,-2.790699205877460258e+02
3.200000000000000067e-02,-2.794537361610759945e+02
3.300000000000000155e-02,-2.798375517344049968e+02
3.399999999999999550e-02,-2.802213673077350222e+02
3.500000000000000333e-02,-2.776516459805940258e+02
3.599999999999999728e-02,-2.750819246534539957e+02
3.700000000000000511e-02,-2.725122033263129993e+02
3.799999999999999906e-02,-2.699424819991720028e+02
3.899999999999999994e-02,-2.698567311502329744e+02
4.000000000000000083e-02,-2.722549507794930150e+02
4.100000000000000172e-02,-2.746531704087540220e+02
4.199999999999999567e-02,-2.770513900380149721e+02
4.299999999999999656e-02,-2.794496096672759791e+02
4.400000000000000439e-02,-2.800761105821779893e+02
4.499999999999999833e-02,-2.800761105821779893e+02
4.599999999999999922e-02,-2.800761105821779893e+02
4.700000000000000011e-02,-2.800761105821779893e+02
4.799999999999999406e-02,-2.788333722531979788e+02
4.900000000000000189e-02,-2.763478955952380147e+02
5.000000000000000278e-02,-2.738624189372779938e+02
5.100000000000000366e-02,-2.713769422793179729e+02
5.199999999999999761e-02,-2.688914656213580088e+02
5.299999999999999850e-02,-2.715383673199499981e+02
5.400000000000000633e-02,-2.741852690185419874e+02
5.499999999999999334e-02,-2.768321707171339767e+02
5.600000000000000117e-02,-2.794790724157260229e+02
5.700000000000000205e-02,-2.804776351435970128e+02
5.799999999999999600e-02,-2.798278589007459800e+02
5.899999999999999689e-02,-2.791780826578950041e+02
5.999999999999999778e-02,-2.785283064150449945e+02
6.100000000000000561e-02,-2.778785301721940186e+02
6.199999999999999956e-02,-2.670252067497989970e+02
6.300000000000000044e-02,-2.561718833274049985e+02
6.400000000000000133e-02,-2.453185599050100052e+02
6.500000000000000222e-02,-2.344652364826150119e+02
6.600000000000000311e-02,-1.780224826854309867e+02
6.700000000000000400e-02,-7.599029851345700592e+01
6.799999999999999101e-02,2.604188565851649884e+01
6.900000000000000577e-02,1.280740698304900036e+02
7.000000000000000666e-02,2.301062540024639986e+02
7.100000000000000755e-02,2.404921248105050040e+02
7.199999999999999456e-02,2.508779956185460094e+02
7.299999999999999545e-02,2.612638664265870148e+02
7.400000000000001021e-02,2.716497372346279917e+02
7.499999999999999722e-02,2.773051723900500178e+02
7.599999999999999811e-02,2.782301718928520131e+02
7.699999999999999900e-02,2.791551713956549747e+02
7.799999999999999989e-02,2.800801708984579932e+02
7.900000000000001465e-02,2.810051704012610116e+02
8.000000000000000167e-02,2.785107135689390248e+02
8.099999999999998868e-02,2.760162567366169810e+02
8.200000000000000344e-02,2.735217999042949941e+02
8.300000000000000433e-02,2.710273430719730072e+02
8.399999999999999134e-02,2.706544464035359852e+02
8.500000000000000611e-02,2.724031098989830184e+02
8.599999999999999312e-02,2.741517733944299948e+02
8.699999999999999400e-02,2.759004368898779944e+02
8.800000000000000877e-02,2.776491003853250277e+02
8.899999999999999578e-02,2.783445666445250026e+02
8.999999999999999667e-02,2.790400329037249776e+02
I would approach this by defining a single function fourier that accepts a variable number of arguments, using a loop through the cosines to evaluate the function for each of your input points:
def fourier(x, *a):
ret = a[0] * np.cos(np.pi / tau * x)
for deg in range(1, len(a)):
ret += a[deg] * np.cos((deg+1) * np.pi / tau * x)
return ret
Because this function takes a variable number of arguments, you need to provide a starting position of the correct dimensionality as a hint to the curve_fit function so it knows the number of cosines to use. In the example you provide with 8 cosines:
popt, pcov = curve_fit(fourier, z, Ua, [1.0] * 8)
Now, the use case you're asking about (fitting with 15 harmonics and plotting the first three) can be accomplished with:
# Fit with 15 harmonics
popt, pcov = curve_fit(fourier, z, Ua, [1.0] * 15)
# Plot data, 15 harmonics, and first 3 harmonics
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
p2, = plt.plot(z, fourier(z, *popt))
p3, = plt.plot(z, fourier(z, popt[0], popt[1], popt[2]))
plt.show()
based on this thread.
Passing additional arguments using scipy.optimize.curve_fit?
# import and filename
filename = 'data.txt'
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
z, Ua = np.loadtxt(filename,delimiter=',', unpack=True)
tau = 0.045
def make_fourier(na, nb):
def fourier(x, *a):
ret = 0.0
for deg in range(0, na):
ret += a[deg] * np.cos((deg+1) * np.pi / tau * x)
for deg in range(na, na+nb):
ret += a[deg] * np.sin((deg+1) * np.pi / tau * x)
return ret
return fourier
popt, pcov = curve_fit(make_fourier(15,15), z, Ua, [0.0]*30)
# Plot data, 15 harmonics, and first 3 harmonics
fig = plt.figure()
ax1 = fig.add_subplot(111)
p1, = plt.plot(z,Ua)
p2, = plt.plot(z, (make_fourier(15,15))(z, *popt))
#p3, = plt.plot(z, (make_fourier(8,8))(z, popt[0], popt[1], popt[2]))
plt.show()

Categories