Xenophilius
Xenophilius

Reputation: 3

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()

Upvotes: 0

Views: 2225

Answers (1)

Nathaniel
Nathaniel

Reputation: 3290

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()

Cosine Function fit

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()

Maclaurin series fit

The cosine function matches the data better than the Maclaurin series in this case because the data was generated using a cosine function.

Upvotes: 1

Related Questions