TypeError: Improper input: N=3 must not exceed M=1, not sure what's wrong with my dimensions?

Viewed 3759

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.

2 Answers

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.

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.

Related