Create scipy curve fitting definitions for fourier series dynamically - python
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()
Related
Approximation of 2-dimensional inverse Fourier transform with non-centred grid
I am trying to approximate the inverse of the Fourier transform in two dimensions. For the inverse Fourier transform, I use the definition, $$ F^{-1}[\phi](x, y) = \dfrac{1}{4\pi} \int_{-\infty}^{\infty}\int_{-\infty}^{\infty} e^{-i (ux + wy} \phi(u, w) \text{d}u \text{d}w, $$ for the inverse of the Fourier transform. The integral will be approximated by use of the trapezoidal integration rule. The grid structure is chosen as $x_j = x_0 + j\Delta_x$, $y_k = y_0 + k\Delta_y$, $u_n = \left(n - \frac{N}{2}\right) \Delta_u$ and $w_m = \left(m - \frac{N}{2}\right) \Delta_u$, for $j, k, n, m, N \in \mathbb{N}$. By choosing the relations $\Delta_u \Delta_x = \dfrac{2\pi}{N}$ and $\Delta_w \Delta_y = \dfrac{2\pi}{N}$, the approximation of the two dimensional Fourier transform can be written in terms of the fast Fourier transform (FFT). I implemented the results in Python, and tried to recover the bivariate normal density for different parameters $\sigma$ and $\mu$. As the normal density decays to zero, the grid can be constructed in terms of the mean and the variance. Below, I have included the approximation in code: from matplotlib import pyplot as plt import matplotlib.patches as mpatches import numpy as np from scipy.fft import fft, fft2 from scipy.stats import multivariate_normal i = complex(0.0, 1.0) def chF(uu, ww, mu, sigma): return np.exp( i * (uu * mu[0] + ww * mu[1]) - 0.5 * ( uu**2 * sigma[0,0] + ww**2 * sigma[1,1] + uu * ww * (sigma[0,1] + sigma[1,0]) ) ) def pdf(xx, yy, mu, sigma): norm = multivariate_normal(mu, sigma) return norm.pdf(np.dstack([xx, yy])) def powerSequence(a, N, offset=0): # Returns a sequence of a ^ (n + offset) for n in N return a ** (np.arange(N) + offset) def powerSequence2d(a, N1, N2, offset=0): # Returns a sequence of a ^ (n + j + offset) for n,j in N return a ** (np.array([ j + np.arange(N2) for j in range(0, N1)]) + offset) def createSimple1dGrid(c1, c2, c4=0, N:int=2**9, L:float=10): x0 = c1 - L * np.sqrt(c2) dx = 2 * L * np.sqrt(c2 + np.sqrt(c4)) / N du = 2 * np.pi / (N * dx) x = x0 + np.arange(N) * dx u = (np.arange(N) - N / 2) * du return x, u, du def createSimpleGrid(c11, c21, c12, c22, c14=0, c24=0, N:int=2**9, L1:float=10, L2:float=10): x, u, du = createSimple1dGrid(c11, c12, c14, N=N, L=L1) y, w , dw = createSimple1dGrid(c21, c22, c24, N=N, L=L2) return x, y, u, w, du, dw def inverseTransform(x, y, u, w, du, dw, phinn, N): # Compute edges of the trapezoidal integration rule for 2-dimensional integrals. e1 = np.exp(-i * (u[0] * x + w[0] * y)) * phinn[0, 0] e2 = np.exp(-i * (u[0] * x + w[N-1] * y)) * phinn[0, N-1] e3 = np.exp(-i * (u[N-1] * x + w[0] * y)) * phinn[N-1, 0] e4 = np.exp(-i * (u[N-1] * x + w[N-1] * y)) * phinn[N-1, N-1] e = 0.25 * (e1 + e2 + e3 + e4) # Compute boundaries of the trapezoidal integration rule # for 2-dimensional integrals. These can be written in # one-dimensional Fourier transforms. b1 = np.exp(-i * w[0] * y) * powerSequence(-1, N)\ * fft(phinn[:, 0] * np.exp(-i * x[0] * u)) b2 = np.exp(-i * w[N-1] * y) * powerSequence(-1, N)\ * fft(phinn[:, N-1] * np.exp(-i * x[0] * u)) b3 = np.exp(-i * u[0] * x) * powerSequence(-1, N)\ * fft(phinn[0, :] * np.exp(-i * y[0] * w)) b4 = np.exp(-i * u[N-1] * x) * powerSequence(-1, N)\ * fft(phinn[N-1, :] * np.exp(-i * y[0] * w)) b = 0.5 * (b1 + b2 + b3 + b4) # Compute IFFT-2d func = phinn * np.exp(-i *(x[0] * u + y[0] * w)) invFour2d = powerSequence2d(-1, N, N)\ * fft2(func) invTransform = 1 / (4 * np.pi**2) * du * dw * (invFour2d - b + e) return invTransform def test_recover_normal_density_off_2d_non_centred(): # Initialize parameters of the two-dimensional normal density s1, s2 = 4.0, 10.0 mu, sigma = np.array([10, 10]), np.array([ [s1, 0.0], [0.0, s2] ]) # Create the Fourier grid N, L = 2**9, 10 x, y, u, w, du, dw = createSimpleGrid(mu[0], mu[1], s1, s2, N=N, L1=L, L2=L) uu, ww = np.meshgrid(u, w, indexing="ij") xx, yy = np.meshgrid(x, y, indexing="ij") phi = chF(uu, ww, mu, sigma) expected = pdf(xx, yy, mu,sigma) # Compute the inverse transform result = inverseTransform(x, y, u, w, du, dw, phi, N) # Plot results _, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(xx, yy, result) ax.plot_wireframe(xx, yy, expected, color="r", rstride=10, cstride=10) # manually make legend for plot col1_patch = mpatches.Patch(color="b", label="approximation") col2_patch = mpatches.Patch(color="r", label="expected") legends = [col1_patch, col2_patch] ax.legend(handles=legends) plt.show() The resulting plot is given as: bivariate normal approximation The approximation is correct, however, misplaced on the grid. I thought this had something to do with the way the fft algorithm places the origin. However, trying different shifts from scipy.fft import fftshift, ifftshift only worsen the results. Using different values for $\mu$ yielded results that were closer to the approximation or sometimes even further off. How do I get the approximation result to coincide with the expected plot of the bivariate normal?
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.
How to fit three gaussian peaks in 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()
Sine ploting python
How to get one graph which consist of two different sinusoidal waves? I wrote this code but it makes two separate waves.. Fs = 1000 f = 2 sample = 1000 sample_rate= 0.1 x = np.arange(sample) noise = 0.0003*np.asarray(random.sample(range(0,1000),sample)) y = np.sin(2 * np.pi * f * x / Fs)+noise f1 = 10 x1 = np.arange(sample) y1 = np.sin(2 * np.pi * f1 * x / Fs)+noise plt.plot(x, y, x1, y1) plt.xlabel('Time(s)') plt.ylabel('Amplitude(V)') plt.show() I got this but I need to get this one
Aside from the "spike" joining the two different signals, this looks more like what you're looking for: import numpy as np import matplotlib.pyplot as plt rng = np.random.default_rng() Fs = 1000 def generate_noisy_signal(*, length, f, noise_amp=0): x = np.arange(length) noise = noise_amp * rng.random(length) return np.sin(2 * np.pi * f * x / Fs) + noise signal1 = generate_noisy_signal(length=1000, f=2, noise_amp=0.3) signal2 = generate_noisy_signal(length=1000, f=10, noise_amp=0.3) + 1.5 signal = np.concatenate([signal1, signal2]) plt.plot(signal) plt.xlabel("Time(s)") plt.ylabel("Amplitude(V)") plt.show()
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.