问题
I am trying to simply find best fit for malus's law.
I_measured=I_0*(cos(theta)) ^2
When I scatter the plot, it obviously works but with the def form() function I get the error given below.
I googled the problem and it seems that this is not the correct way to curvefit a cosine function.
given data is ..
x_data=x1 in the code below
[ 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0,
60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, 105.0, 110.0, 115.0,
120.0, 125.0, 130.0, 135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0,
170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 205.0, 210.0, 215.0,
220.0, 225.0, 230.0, 235.0, 240.0, 245.0, 250.0, 255.0, 260.0, 265.0,
270.0, 275.0, 280.0, 285.0, 290.0, 295.0, 300.0, 305.0, 310.0, 315.0,
320.0, 325.0, 330.0, 335.0, 340.0, 345.0, 350.0, 355.0, 360.0]
y_data = x2 in the code below
[ 1.69000000e-05 2.80000000e-05 4.14000000e-05 5.89000000e-05
7.97000000e-05 9.79000000e-05 1.23000000e-04 1.47500000e-04
1.69800000e-04 1.94000000e-04 2.17400000e-04 2.40200000e-04
2.55400000e-04 2.70500000e-04 2.81900000e-04 2.87600000e-04
2.91500000e-04 2.90300000e-04 2.83500000e-04 2.76200000e-04
2.62100000e-04 2.41800000e-04 2.24200000e-04 1.99500000e-04
1.74100000e-04 1.49300000e-04 1.35600000e-04 1.11500000e-04
9.00000000e-05 6.87000000e-05 4.98000000e-05 3.19000000e-05
2.07000000e-05 1.31000000e-05 9.90000000e-06 1.03000000e-05
1.49000000e-05 2.34000000e-05 3.65000000e-05 5.58000000e-05
7.56000000e-05 9.65000000e-05 1.19400000e-04 1.46900000e-04
1.73000000e-04 1.99200000e-04 2.24600000e-04 2.38700000e-04
2.60700000e-04 2.74800000e-04 2.84000000e-04 2.91200000e-04
2.93400000e-04 2.90300000e-04 2.86400000e-04 2.77900000e-04
2.63600000e-04 2.45900000e-04 2.25500000e-04 2.03900000e-04
1.79100000e-04 1.51800000e-04 1.32400000e-04 1.07000000e-04
8.39000000e-05 6.20000000e-05 4.41000000e-05 3.01000000e-05
1.93000000e-05 1.24000000e-05 1.00000000e-05 1.13000000e-05
1.77000000e-05]
the code
I_0=291,5*10**-6/(pi*0.35**2) # print(I_0) gives (291, 1.2992240252399621e-05)??
def form(theta, I_0):
return (I_0*(np.abs(np.cos(theta)))**2) # theta is x_data
param=I_0
parame,covariance= optimize.curve_fit(form,x1,x2,I_0)
test=parame*I_0
#print(parame)
#plt.scatter(x1,x2,label='data')
plt.ylim(10**-5,3*10**-4)
plt.plot(x1,form(x1,*parame),'b--',label='fitcurve')
The error I get is:
TypeError: form() takes 2 positional arguments but 3 were given`
i started again with another code shown below.
x1=np.radians(np.array(x1))
x2=np.array(x2)*10**-6
print(x1,x2)
def form(theta, I_0, theta0, offset):
return I_0 * np.cos(np.radians(theta - theta0)) ** 2 + offset
param, covariance = optimize.curve_fit(form, x1, x2)
plt.scatter(x1, x2, label='data')
plt.ylim(0, 3e-4)
plt.xlim(0, 360)
plt.plot(x1, form(x1, *param), 'b-')
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.axes().xaxis.set_major_locator(ticker.MultipleLocator(45))
plt.show()
in the new code. i multiplide the input array with a number.. basically it s still y_data in the first code. when i plot this, i see that function does not fit at all with an added code x1 = np.radians(np.array(x1))
回答1:
Comma
I guess your I_0=291,5*10**-6/(pi*0.35**2) is supposed to be the initial guess for the fit. I don't know why this is expressed in such a complicated way. Using , as decimal separator is the wrong syntax in Python, use . instead. Also, instead of something like 123.4 * 10 ** -5 you can write 123.4e-5 (scientific notation).
Anyway, it turns out you don't even need to specify the initial guess if you do the fit correctly.
Model function
In your model function,
I_measured = I_0 * cos(theta)**2,thetais in radians (0 to 2π), but yourxvalues are in degrees (0 to 360).Your model function doesn't account for any offset in the
xoryvalues. You should include such parameters in the function.
An improved model function would look like this:
def form(theta, I_0, theta0, offset):
return I_0 * np.cos(np.radians(theta - theta0)) ** 2 + offset
(Credits to Martin Evans for pointing out the np.radians function.)
Result
Now the curve_fit function is able to derive values for I_0, theta0, and offset that best fit the model function to your measured data:
>>> param, covariance = optimize.curve_fit(form, x, y)
>>> print 'I_0: {0:e} / theta_0: {1} degrees / offset: {2:e}'.format(*param)
I_0: -2.827996e-04 / theta_0: -9.17118424279 degrees / offset: 2.926534e-04
The plot looks decent, too:
import matplotlib.ticker as ticker
plt.scatter(x, y, label='data')
plt.ylim(0, 3e-4)
plt.xlim(0, 360)
plt.plot(x, form(x, *param), 'b-')
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.axes().xaxis.set_major_locator(ticker.MultipleLocator(45))
plt.show()
(Your x values are from 0 to 360, I don't know why you've set the plot limits to 370. Also, I spaced the ticks in 45 degrees interval.)
Update: The fit results in a negative amplitude I_0 and an offset of about 3e-4, close to the maximum y values. You can guide the fit to a positive amplitude and offset close to zero ("flip it around") by providing a 90 degree initial phase offset:
>>> param, covariance = optimize.curve_fit(form, x, y, [3e-4, 90, 0])
>>> print 'I_0: {0:e} / theta_0: {1} degrees / offset: {2:e}'.format(*param)
I_0: 2.827996e-04 / theta_0: 80.8288157578 degrees / offset: 9.853833e-06
Here's the complete code.
回答2:
The comma in your formula is creating a two object tuple, it does not specify "thousands", as such, you should remove this giving you:
I_O = 0.00757447606715
The aim here is to provide a function that can be adapted to fit your data. Your original function only provided one parameter, which was not enough to enable curve_fit() to get a good fit.
In order to get a better fit, you need to create more variables for your func() to enable the curve fitter more flexibility. In this case for the cos wave, it provides I_O for the amplitude, theta0 for the phase and yoffset.
So the code would be:
import matplotlib.pyplot as plt
from math import pi
from scipy import optimize
import numpy as np
x1 = [ 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0,
60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, 105.0, 110.0, 115.0,
120.0, 125.0, 130.0, 135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0,
170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 205.0, 210.0, 215.0,
220.0, 225.0, 230.0, 235.0, 240.0, 245.0, 250.0, 255.0, 260.0, 265.0,
270.0, 275.0, 280.0, 285.0, 290.0, 295.0, 300.0, 305.0, 310.0, 315.0,
320.0, 325.0, 330.0, 335.0, 340.0, 345.0, 350.0, 355.0, 360.0]
x2 = [ 1.69000000e-05, 2.80000000e-05, 4.14000000e-05, 5.89000000e-05,
7.97000000e-05, 9.79000000e-05, 1.23000000e-04, 1.47500000e-04,
1.69800000e-04, 1.94000000e-04, 2.17400000e-04, 2.40200000e-04,
2.55400000e-04, 2.70500000e-04, 2.81900000e-04, 2.87600000e-04,
2.91500000e-04, 2.90300000e-04, 2.83500000e-04, 2.76200000e-04,
2.62100000e-04, 2.41800000e-04, 2.24200000e-04, 1.99500000e-04,
1.74100000e-04, 1.49300000e-04, 1.35600000e-04, 1.11500000e-04,
9.00000000e-05, 6.87000000e-05, 4.98000000e-05, 3.19000000e-05,
2.07000000e-05, 1.31000000e-05, 9.90000000e-06, 1.03000000e-05,
1.49000000e-05, 2.34000000e-05, 3.65000000e-05, 5.58000000e-05,
7.56000000e-05, 9.65000000e-05, 1.19400000e-04, 1.46900000e-04,
1.73000000e-04, 1.99200000e-04, 2.24600000e-04, 2.38700000e-04,
2.60700000e-04, 2.74800000e-04, 2.84000000e-04, 2.91200000e-04,
2.93400000e-04, 2.90300000e-04, 2.86400000e-04, 2.77900000e-04,
2.63600000e-04, 2.45900000e-04, 2.25500000e-04, 2.03900000e-04,
1.79100000e-04, 1.51800000e-04, 1.32400000e-04, 1.07000000e-04,
8.39000000e-05, 6.20000000e-05, 4.41000000e-05, 3.01000000e-05,
1.93000000e-05, 1.24000000e-05, 1.00000000e-05, 1.13000000e-05,
1.77000000e-05]
x1 = np.radians(np.array(x1))
x2 = np.array(x2)
def form(theta, I_0, theta0, offset):
return I_0 * np.cos(theta - theta0) ** 2 + offset
param, covariance = optimize.curve_fit(form, x1, x2)
plt.scatter(x1, x2, label='data')
plt.ylim(x2.min(), x2.max())
plt.plot(x1, form(x1, *param), 'b-')
plt.show()
Giving you an output of:
The maths libraries work in radians, so your data would need to be converted to radians at some point (where 2pi == 360 degrees). You can either convert your data to radians, or carry out the conversion within your function.
Thanks also to mkrieger1 for the extra parameters.
来源:https://stackoverflow.com/questions/46296263/python-fit-data-to-given-cosine-function