Trying to fit data into sine cosine curve fit using scipy - python

I am new to signal processing in python.
Here I am trying fit data to sine cosine curve using the equation -
A * np.sin(x) + B * np.cos(x) + C.
here is the snippet of the code
def func(x, A, B, C):
return A * np.sin(x) + B * np.cos(x) + C
p0 = 0, 25, 10
popt, pcov = curve_fit(func, x, y)
times = np.linspace(x[0], x[-1], num=21)
plt.plot(x, y, 'o', color='red', label="data")
plt.plot(times, func(times, *popt), '--', color='blue', label="optimized data")
# plt.plot(x, func(x, *popt), '--', color='blue', label="optimized data")
plt.legend()
plt.show()
I am getting below output (in image)
Could anyone help me spotting the mistake or any suggestion with the code

Related

How to do Non-Linar Curve fitting using Python with user defined function?

I have the following dataset:
x = 2.5, 5, 7.5, 10, 20, 30
y = 18035.21768722, 18176.09871938, 18370.22289623, 18430.68522672, 18490.76110193, 18512.69861061
Now, I want to plot this data and fit this data set with my defined function f(x) = (A*K*x/1+K*x) and find the parameters A and K ?
I wrote the following python script but it seems like it can't do the fitting I require:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = np.array([2.5, 5, 7.5, 10, 20, 30])
y = np.array([18035.21768722, 18176.09871938, 18370.22289623, 18430.68522672, 18490.76110193, 18512.69861061])
def func(x, A, K):
return (A*K*x / 1+K*x)
plt.plot(x, y, 'b-', label='data')
popt, pcov = curve_fit(func, x, y)
plt.plot(x, func(x, *popt), 'r-', label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
Can anyone help me with the changes in the python script or a new script where I can properly fit the data with my desired fitting function ?
Try fixing your function definition as suggested by HS-nebula in the comments, and add an initial guess for k using the p0 argument in curve_fit:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = np.array([2.5, 5, 7.5, 10, 20, 30])
y = np.array([18035.21768722, 18176.09871938, 18370.22289623, 18430.68522672, 18490.76110193, 18512.69861061])
def func(x, A, K):
return A*K*x / (1+K*x)
plt.plot(x, y, 'b-', label='data')
popt, pcov = curve_fit(func, x, y, p0=[1, 1e6])
plt.plot(x, func(x, *popt), 'r-', label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

How to fit part of a Cosine curve to data in Python?

Written this code to try and plot a a graph of y = a(1 + cos(bx - pi)) + c to our data collected but when using np.cos it tries to fit an entire cycle of cos onto the data, which doesn't fit our results. Any help on how to fit only a section of the curve to our data would be fab!
Tried to avoid using cos by using maclaurin series expansion but this still doesn't work.
x_data = w
y_data = mean
e = error
from scipy import optimize
def test_func(x, a, b, c):
y = (a/2)*(1 + (1 - (1/2)*(b*x - np.pi)**2 + (1/24)*(b*x - np.pi)**4)) + c
return y
params, params_covariance = optimize.curve_fit(test_func, x_data, y_data)
print(params)
a = params[0]
b = params[1]
c = params[2]
figure(num=None, figsize=(12, 6), dpi=80, facecolor='w', edgecolor='k')
plt.errorbar(x_data, y_data, yerr=e, fmt='o', marker='o', label='Data', markersize=3, color='k', elinewidth=1, capsize=2, markeredgewidth=1)
plt.plot(x_data, test_func(x_data, params[0], params[1], params[2]), label='Fitted function')
plt.legend(loc='best')
plt.ylabel('Interference intensity, $I$')
plt.xlabel('Rotational velocity of interferometer, $w$')
plt.show()
Your question is "how to fit only a section of a curve to our data." This can be accomplished by defining a piece-wise function and fitting a section of your data to each corresponding piece of the function. You need to define the cut-off values that separate the parts of your data and pick which functions to fit to each part.
In order to fit a curve to only a section of the data, you need to only pass the portion of the data to curve_fit that you want to fit. Here are working examples of fitting the data to both a Maclaurin series and a cosine function:
from scipy import optimize
# Generate sample data
np.random.seed(0)
x_data = np.linspace(-np.pi,3*np.pi,101)
y_data = np.cos(x_data) + np.random.rand(len(x_data))/4
idx = (x_data < 0) | (x_data > 2*np.pi)
y_data[idx] = 1 + np.random.rand(sum(idx))/4
e = np.random.rand(len(x_data))/10
# Select part of data to fit
fit_part = ~idx
x_data_to_fit = x_data[fit_part]
y_data_to_fit = y_data[fit_part]
Cosine Function:
def test_func(x, a, b):
y = a*np.cos(b*x)
return y
params, params_covariance = optimize.curve_fit(test_func, x_data_to_fit, y_data_to_fit)
print(params)
a = params[0]
b = params[1]
plt.figure(num=None, figsize=(12, 6), dpi=80, facecolor='w', edgecolor='k')
plt.title('Cosine Function Fit')
plt.errorbar(x_data, y_data, yerr=e, fmt='o', marker='o', label='Data', markersize=3, color='k', elinewidth=1, capsize=2, markeredgewidth=1)
plt.plot(x_data_to_fit, test_func(x_data_to_fit, a, b), label='Fitted function')
plt.legend(loc='best')
plt.ylabel('Interference intensity, $I$')
plt.xlabel('Rotational velocity of interferometer, $w$')
plt.show()
Maclaurin Series:
def test_func(x, a, b, c):
y = (a/2)*(1 + (1 - (1/2)*(b*x - np.pi)**2 + (1/24)*(b*x - np.pi)**4)) + c
return y
params, params_covariance = optimize.curve_fit(test_func, x_data_to_fit, y_data_to_fit)
print(params)
a = params[0]
b = params[1]
c = params[2]
plt.figure(num=None, figsize=(12, 6), dpi=80, facecolor='w', edgecolor='k')
plt.title('MacLaurin Series Fit')
plt.errorbar(x_data, y_data, yerr=e, fmt='o', marker='o', label='Data', markersize=3, color='k', elinewidth=1, capsize=2, markeredgewidth=1)
plt.plot(x_data_to_fit, test_func(x_data_to_fit, a, b, c), label='Fitted function')
plt.legend(loc='best')
plt.ylabel('Interference intensity, $I$')
plt.xlabel('Rotational velocity of interferometer, $w$')
plt.show()
The cosine function matches the data better than the Maclaurin series in this case because the data was generated using a cosine function.

How do i get a smooth fit for my data points, using "scipy.optimize.curve_fit"?

I want to fit some data points using scipy.optimize.curve_fit. Unfortunately I get an unsteady fit and I do not know why.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
M = np.array([730,910,1066,1088,1150], dtype=float)
V = np.array([95.71581923, 146.18564513, 164.46723727, 288.49796413, 370.98703941], dtype=float)
def func(x, a, b, c):
return a * np.exp(b * x) + c
popt, pcov = curve_fit(func, M, V, [0,0,1], maxfev=100000000)
print(*popt)
fig, ax = plt.subplots()
fig.dpi = 80
ax.plot(M, V, 'go', label='data')
ax.plot(M, func(M, *popt), '-', label='fit')
plt.xlabel("M")
plt.ylabel("V")
plt.grid()
plt.legend()
plt.show()
I would acutally expect some kind of a smooth curve. Can someone explain what I am doing wrong here?
You are only plotting the same x points as the original data in your call:
ax.plot(M, V, 'go', label='data')
ax.plot(M, func(M, *popt), '-', label='fit')
To fix this, you can use a wider range - here we use all the values from 700 to 1200:
toplot = np.arange(700,1200)
ax.plot(toplot, func(toplot, *popt), '-', label='fit')

Draw average line of scatter plot with matplotlib python

Im trying to fit an average line in a scatter plot with matplotlib. All im getting is this.
But I want it like this green line
I have tried the following two snippets for fitting the curve:
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(x,p(x),"r-")
and
def func(x, a, b, c):
return a * np.exp(-b * x) + c
popt, pcov = curve_fit(func, x, residual)
plt.plot(x, func(x, *popt), 'r-', label='fit')
As you no provide data I made my own and I've tried this:
N = 10000
xr = np.linspace(-1,6,N)
yr = -1*(np.ones(N)-1+xr) + 10*np.random.rand(N)
x = np.concatenate((xr[0:3800],xr[4900:]))
y = np.concatenate((yr[0:3800],yr[4900:]))
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(x,p(x),"r-")
plt.scatter(x,y, s=2)
plt.show()
```
and this is the output I have, which is the expected output:
If you can share a piece of your data I could test it as well.

Exponential curve fit will not fit

When attempting to plot an exponential curve to a set of data:
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import style
from matplotlib import pylab
import numpy as np
from scipy.optimize import curve_fit
x = np.array([30,40,50,60])
y = np.array([0.027679854,0.055639098,0.114814815,0.240740741])
def exponenial_func(x, a, b, c):
return a*np.exp(-b*x)+c
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(10,60,1000)
yy = exponenial_func(xx, *popt)
plt.plot(x,y,'o', xx, yy)
pylab.title('Exponential Fit')
ax = plt.gca()
fig = plt.gcf()
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.show()
Graph for the above code:
However when I add the data point 20 (x) and 0.015162344 (y):
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import style
from matplotlib import pylab
import numpy as np
from scipy.optimize import curve_fit
x = np.array([20,30,40,50,60])
y = np.array([0.015162344,0.027679854,0.055639098,0.114814815,0.240740741])
def exponenial_func(x, a, b, c):
return a*np.exp(-b*x)+c
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(20,60,1000)
yy = exponenial_func(xx, *popt)
plt.plot(x,y,'o', xx, yy)
pylab.title('Exponential Fit')
ax = plt.gca()
fig = plt.gcf()
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.show()
The above code generates the error
'RuntimeError: Optimal parameters not found: Number of calls to
function has reached maxfev = 800.'
If maxfev is set to maxfev = 1300
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1),maxfev=1300)
The graph is plotted but does not fit the curve correctly. Graph from above code change, maxfev = 1300:
I think this is because points 20 and 30 a too close to each other? For comparison, excel plots the data like this:
How can I plot this curve correctly?
From your data it is obvious that you need a positive exponent, therefore, b needs to be negative as you use a*np.exp(-b*x) + c as the underlying model. However, you start with a positive initial value for b which most likely causes the issues.
If you change
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
to
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, -1e-6, 1))
it works fine and gives the expected outcome.
Alternatively, you could also change your equation to
return a*np.exp(b*x) + c
and start with the same initial values as you had.
Here is the entire code:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
def exponenial_func(x, a, b, c):
return a*np.exp(b*x)+c
x = np.array([20, 30, 40, 50, 60])
y = np.array([0.015162344, 0.027679854, 0.055639098, 0.114814815, 0.240740741])
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(20, 60, 1000)
yy = exponenial_func(xx, *popt)
# please check whether that is correct
r2 = 1. - sum((exponenial_func(x, *popt) - y) ** 2) / sum((y - np.mean(y)) ** 2)
plt.plot(x, y, 'o', xx, yy)
plt.title('Exponential Fit')
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.text(30, 0.15, "equation:\n{:.4f} exp({:.4f} x) + {:.4f}".format(*popt))
plt.text(30, 0.1, "R^2:\n {}".format(r2))
plt.show()

Categories

Resources