Search code examples
pythonscipydata-fittingscipy-optimize

Errors using curve_fit for Guassian fit of data


I'm trying to do a guassian fit for some experimental data but I keep running into error after error. I've followed a few different threads online but either the fit isn't good (it's just a horizontal line) or the code just won't run. I'm following this code from another thread. Below is my code.

I apologize if my code seems a bit messy. There are some bits from other attempts when I tried making it work. Hence the "astropy" import.

import math as m
import matplotlib.pyplot as plt
import numpy as np
from scipy import optimize as opt
import pandas as pd
import statistics as stats
from astropy import modeling


def gaus(x,a,x0,sigma, offset):
    return a*m.exp(-(x-x0)**2/(2*sigma**2)) + offset

# Python program to get average of a list
def Average(lst):
    return sum(lst) / len(lst)

wavelengths = [391.719, 391.984, 392.248, 392.512, 392.777, 393.041, 393.306, 393.57, 393.835, 394.099, 391.719, 391.455, 391.19, 390.926, 390.661, 390.396]
intensities = [511.85, 1105.85, 1631.85, 1119.85, 213.85, 36.85, 10.85, 6.85, 13.85, 7.85, 511.85, 200.85, 80.85, 53.85, 14.85, 24.85]



n=sum(intensities)
mean = sum(wavelengths*intensities)/n
sigma = m.sqrt(sum(intensities*(wavelengths-mean)**2)/n)


def gaus(x,a,x0,sigma):
    return a*m.exp(-(x-x0)**2/(2*sigma**2))

popt,pcov = opt.curve_fit(gaus,wavelengths,intensities,p0=[1,mean,sigma])
print(popt)


plt.scatter(wavelengths, intensities)

plt.title("Helium Spectral Line Peak 1")
plt.xlabel("Wavelength (nm)")
plt.ylabel("Intensity (a.u.)")

plt.show()

Thanks to the kind user, my curve seems to be working more reasonably well. However, one of the points seems to be back connecting to an earlier point? Screenshot below:

enter image description here


Solution

  • There are two problems with your code. The first is that you are performing vector operation on list which gives you the first error in the line mean = sum(wavelengths*intensities)/n. Therefore, you should use np.array instead. The second is that you take math.exp on python list which again throws an error as it takes a real number, so you should use np.exp here instead.

    The following code solves your problem:

    import matplotlib.pyplot as plt
    import numpy as np
    from scipy import optimize as opt
    
    wavelengths = [391.719, 391.984, 392.248, 392.512, 392.777, 393.041,
                   393.306, 393.57, 393.835, 394.099, 391.719, 391.455,
                   391.19, 390.926, 390.661, 390.396]
    intensities = [511.85, 1105.85, 1631.85, 1119.85, 213.85, 36.85, 10.85, 6.85,
                   13.85, 7.85, 511.85, 200.85, 80.85, 53.85, 14.85, 24.85]
    
    wavelengths_new = np.array(wavelengths)
    intensities_new = np.array(intensities)
    
    n=sum(intensities)
    mean = sum(wavelengths_new*intensities_new)/n
    sigma = np.sqrt(sum(intensities_new*(wavelengths_new-mean)**2)/n)
    
    def gaus(x,a,x0,sigma):
        return a*np.exp(-(x-x0)**2/(2*sigma**2))
    
    popt,pcov = opt.curve_fit(gaus,wavelengths_new,intensities_new,p0=[1,mean,sigma])
    print(popt)
    
    plt.scatter(wavelengths_new, intensities_new, label="data")
    plt.plot(wavelengths_new, gaus(wavelengths_new, *popt), label="fit")
    plt.title("Helium Spectral Line Peak 1")
    plt.xlabel("Wavelength (nm)")
    plt.ylabel("Intensity (a.u.)")
    plt.show()