<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Fitting Plots
Essential for determining the fit of a model to raw data, curve fitting is ubiquitous. Using the scipy.optimize.curve_fit functionality, we can define a function to fit the data.
By the end of this file you should be able to
Step1:  Fit a nonlinear  (exponential) function
Step2:  Now that we have the data, let's see about a fit. We use the curve_fit function from the scipy.optimize module, which requires a function and the data. 
A guess parameter (p0, optional) is important for exponential fits
Step3:  The outputs are the optmized parameters (popt) and the covariance matrix (pcov) of the optimized parameters. 
Next, we apply the optimization parameters to the functions to generate a plot that shows the fit
Step4:  Above, func is called on x_fitted, using the starred (unpacked) expression (list) of popt optimal parameters.
Python starred expressions
Step5:  How accurate are the estimated parameters for the data?
The output of the curve_fit function includes pcov, the estimated covariance of popt. 
Properly estimating the variance is an in-depth statistical problem. However, if we are to trust the assumptions curve_fit make (type of data, relationships between estimated parameters, etc), then the square root of the diagonals of the pcov from above approximates the error. This should be done with much caution!
Step6:  Gaussian fit
<ASSISTANT_TASK:>
Python Code:
# Python imports
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import normal
from scipy.optimize import curve_fit
Explanation: Fitting Plots
Essential for determining the fit of a model to raw data, curve fitting is ubiquitous. Using the scipy.optimize.curve_fit functionality, we can define a function to fit the data.
By the end of this file you should be able to:
1. Fit data to a predefined function
Further reading:  
https://lmfit.github.io/lmfit-py/intro.html
https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.optimize.curve_fit.html 
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html  
Finding error in fitting paramaters:
https://stackoverflow.com/questions/14581358/getting-standard-errors-on-fitted-parameters-using-the-optimize-leastsq-method-i
End of explanation
np.random.seed(1) # Use a seed for the random number generator
X = np.linspace(0, 5, 30)
Y = np.exp(-X) + normal(0, 0.2, 30)
plt.scatter(X,Y)
plt.title('Scatter Plot')
plt.show()
Explanation: Fit a nonlinear  (exponential) function:
End of explanation
def func(x_vals, A, B, C):
    return A * np.exp(-B * x_vals) + C
init_guess = (1, 1e-6, 1) # Use an initial guess (optional)
popt, pcov = curve_fit(func, X, Y, p0=init_guess)
print('Optimized parameters are: {}'.format(popt))
Explanation: Now that we have the data, let's see about a fit. We use the curve_fit function from the scipy.optimize module, which requires a function and the data. 
A guess parameter (p0, optional) is important for exponential fits:
End of explanation
x_fitted = np.linspace(0, 5)
y_fitted = func(x_fitted, *popt) # Apply func using opt to x_fitted
Explanation: The outputs are the optmized parameters (popt) and the covariance matrix (pcov) of the optimized parameters. 
Next, we apply the optimization parameters to the functions to generate a plot that shows the fit:
End of explanation
# Plot
plt.scatter(X,Y)
plt.plot(x_fitted,y_fitted, color="red")
plt.title('Scatter Plot + Fit')
plt.show()
Explanation: Above, func is called on x_fitted, using the starred (unpacked) expression (list) of popt optimal parameters.
Python starred expressions:
*list unpacks a list to create an iterable. For example, *[1,2,3,4] unpacks to 1,2,3,4.
End of explanation
perr = np.sqrt(np.diag(pcov)) # One standard deviation errors in the 
                             #    parameters
print('Estimated errors are: {}'.format(perr))
# Remember that func is: A * np.exp(-B * x_vals) + C
std_p = [perr[0], -perr[1], perr[2]] # Combination of perr for highest values
std_m = [-perr[0], perr[1], -perr[2]]# Combination of perr fpr lowest values
# Determine the plots for the ± standard deviation
y_fitted_p = func(x_fitted, *(popt+std_p))
y_fitted_m = func(x_fitted, *(popt+std_m))
# Plot
plt.scatter(X,Y)
plt.plot(x_fitted,y_fitted, color="red")
plt.plot(x_fitted,y_fitted_p, color="blue", label='+ stdev')
plt.plot(x_fitted,y_fitted_m, color="blue", label='- stdev')
plt.title('Scatter Plot')
plt.legend(loc='best')
plt.show()
Explanation: How accurate are the estimated parameters for the data?
The output of the curve_fit function includes pcov, the estimated covariance of popt. 
Properly estimating the variance is an in-depth statistical problem. However, if we are to trust the assumptions curve_fit make (type of data, relationships between estimated parameters, etc), then the square root of the diagonals of the pcov from above approximates the error. This should be done with much caution!
End of explanation
rand_gen = normal(0,1,1000)               # Generate numbers
bins = np.linspace(-5,5,num=100)
histogram = np.histogram(rand_gen,bins);  # Use histogram to get the
                                          #    distribution
X = histogram[1][:-1]
Y = histogram[0]
plt.scatter(X,Y)
plt.title('Scatter Plot')
plt.show()
def gauss_func(x_vals, ampl, sig, mu):
    return ampl * np.exp(-(-x_vals-mu)**2/(2*sig**2))
bounds_set = ([0,0,0],[max(Y)*10, 10, 1]) # We can set bounds on the fit too
popt, pcov = curve_fit(gauss_func, X, Y, bounds=bounds_set)
x_fitted = np.linspace(-5, 5)
y_fitted = gauss_func(x_fitted, *popt)
plt.scatter(X,Y)
plt.plot(x_fitted,y_fitted, color='red')
plt.title('Scatter Plot + Fit')
plt.show()
Explanation: Gaussian fit:
Here, we generate a lot of normally distributed random numbers and use the histogram of the normally distributed numbers to fit a guassian curve. 
Here, a bounds parameter (bounds) is optional in the same way the p0 was:
End of explanation
<END_TASK>