3

I'm trying to fit a lorentzian to one of the peaks in my dataset.

We were given the fit for a gaussian, and aside from the actual fit equation, the code is very similar, so I'm not sure where I am going wrong. I don't see why there is an issue with the dimensions when I'm using curve_fit.

Here are the relevant pieces of my code for a better idea of what I'm talking about.

Reading the CSV file in and trimming it

import csv
import numpy as np 
import matplotlib.pyplot as plt 
from scipy.optimize import curve_fit
from matplotlib.ticker import StrMethodFormatter

#reading in the csv file 

with open("Data-Oscilloscope.csv") as csv_file: 

    csv_reader = csv.reader(csv_file, delimiter=",")
    time =[]
    voltage_raw = [] 

    for row in csv_reader:
        time.append(float(row[3]))
        voltage_raw.append(float(row[4]))
        print("voltage:", row[4])

#trimming the data 

trim_lower_index = 980
trim_upper_index = 1170

time_trim = time[trim_lower_index:trim_upper_index]
voltage_trim = voltage_raw[trim_lower_index:trim_upper_index]

The Gaussian fit given

#fitting the gaussian function 

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


popt, pcov = curve_fit(gauss_function, time_trim, voltage_trim, p0=[1,.4,0.1])
perr = np.sqrt(np.diag(pcov))

#plot of the gaussian fit 

plt.figure(2)
plt.plot(time_trim, gauss_function(time_trim, *popt), label = "fit")
plt.plot(time_trim, voltage_trim, "-b")
plt.show()

My attempted Lorentzian fit

#x is just the x values, a is the amplitude, x0 is the central value, and f is the full width at half max

def lorentz_function(x, a, x0,f):
    w = f/2 #half width at half max
    return a*w/ [(x-x0)**2+w**2]

popt, pcov = curve_fit(lorentz_function, time_trim, voltage_trim, p0=[1,.4,0.1])

I get an error running this that states:

in leastsq raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m)) TypeError: Improper input: N=3 must not exceed M=1

I'm probably missing something very obvious but just can't see it.

Thanks in advance! EDIT: I have taken a look at other, similar questions and went through their explanations, but can't see how those fit in with my code because the number of parameters and dimensions of my inputs should be fine, given that they worked for the gaussian fit.

newbiecode
  • 31
  • 1
  • 4
  • In `return a*w/ [(x-x0)**2+w**2]`, what exactly are you expecting the square brackets to do? – Karl Knechtel Sep 18 '20 at 22:43
  • ah thank you! I didn't notice I typed the equation out that way. Makes sense why it wasn't working now LOL! Tysm :) – newbiecode Sep 18 '20 at 22:50
  • Also, did you verify that `time_trim` and `voltage_trim` contain the data you expect? The error message *appears* to be complaining that you are trying to fit three parameters, but only have one data point. – Karl Knechtel Sep 18 '20 at 22:51
  • yes I've verified that. Removing the square brackets from my equation fixed the issue! – newbiecode Sep 18 '20 at 22:52
  • Now I wonder why the error ends up being reported in that weird way. I would instead expect it to tell you about a `TypeError` in `lorentz_function`. Anyway, glad I could help. – Karl Knechtel Sep 18 '20 at 22:57

2 Answers2

1

You didn't show the full traceback/error so I can only guess where it's happening. It's probably looking at the result returned by lorentz_function, and finding the dimensions to be wrong. So while the error is produced by your function, the testing is in its caller (in this case a level or two down).

def optimize.curve_fit(
    f,
    xdata,
    ydata,
    p0=None,...    # p0=[1,.4,0.1]
    ...  
    res = leastsq(func, p0, 
    ...

curve_fit passes the task to leastsq, which starts as:

def optimize.leastsq(
    func,
    x0,
    args=(), ...

    x0 = asarray(x0).flatten()
    n = len(x0)
    if not isinstance(args, tuple):
        args = (args,)
    shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
    m = shape[0]

    if n > m:
        raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m))

I'm guessing _check_func does

 res = func(x0, *args)     # call your func with initial values

and returning the shape of res. The error says there's a mismatch between what it expects based on the shape of x0 and the result of your func.

I'm guessing that with a 3 element p0, it's complaining that your function returned a 1 element result (due to the []).

lorentz is your function. You don't test the output shape so it can't raise this error.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
1

I had a similar problem yielding this error message. In that case, the array of data passed to the optimize.leastsq was in the matrix form. the data seems to have to be a 1 row array.

For example, the sentence calling the leastsq was

result = optimize.leastsq(fit_func, param0, args=(xdata, ydata, zdata))

xdata, ydata, zdata was in the [ 1 x num ] matrix form. It was

[[200. .... 350.]]

It should be

[200. .... 350.]

So, I had to add the sentence for conversion

xdata = xdata[0, :]

So do ydata and zdata.

I hope this be a help for you.

Peanyatsu
  • 11
  • 2