3

I'm new to curve fitting in python, as well as python in general. Currently, I'm trying to use the curve_fit module from scipy to fit 4 spectroscopic peaks.

In a few words, I have data in a text file that has two columns. So my first step was to import the data into two arrays, one containing the xdata, the other the y data. Then I tried to define the function that I was going to fit (four voigt peaks). Finally, when I tried to run the whole thing, I get the following error:

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

As far as I can tell from the curve_fit help page, this error says that I must have at least as many data points as fit parameters, which makes sense. The problem is that my data set has 250 points in it...

Here's my code

import numpy as n
import pyspec as p
from scipy.optimize import curve_fit

file = open('fileName', "r") #open the file
data = n.loadtxt(file) #load the file into an array
freq = n.array(data[:, 0] - n.median(data[:, 0])) #center data on zero. 
counts = n.array(data[:, 1])
error = n.array(data[:, 1]**0.5) #get the error on the counts. Standard poisson error. 

# Define a single voigt profile
def voigt(xdata, amp, cent, FWHM, ep) :
   x = xdata
   C = cent
   F = FWHM
   A = amp
   E = ep
   vmodel = A * ((1 - E)*n.exp(-2.77259 * (n.square(x - C))/n.square(F)) + E / (1 + (4 * n.square(x - C)/F**2)))
   return[vmodel]

   #Define the four peak function

def voigt4(xdata, amp1, amp2, amp3, amp4, pos1, pos2, pos3, pos4, FWHM, ep, Bg):
   voigtp1 = voigt(xdata, amp1, pos1, FWHM, ep)
   voigtp2 = voigt(xdata, amp2, pos2, FWHM, ep)
   voigtp3 = voigt(xdata, amp3, pos3, FWHM, ep)
   voigtp4 = voigt(xdata, amp4, pos3, FWHM, ep)

   voigt4 = (voigtp1 + voigtp2 + voigtp3 + voigtp4 + Bg) # include a background term
    return[voigt4]

   # give an initial guess. The *_in params are initial guesses made by the user. 
   guess =   n.array([amp1_in, amp2_in, amp3_in, amp4_in, pos1_in, pos2_in, pos3_in, pos4_in, 500, 0.5, bkgr_in])

   fit = curve_fit(voigt4, freq, counts, guess) # try to fit

I have no idea why that error comes up.

Cleb
  • 25,102
  • 20
  • 116
  • 151
  • 1
    Try removing the brackets that you have in the return statements. E.g. `return vmodel` and `return voigt4`. – Warren Weckesser Jan 19 '16 at 06:41
  • I agree with @WarrenWeckesser: remove the brackets in your return statements and see whether that already solves the problem. In general: It would be good if you posted the data you use as well which helps to run the code and to fix the problem. – Cleb Jan 19 '16 at 16:24
  • This worked perfectly! Thank you both very much for the answer and the advice, I'll be sure to post the data if I run into anything else. Would you mind explaining why the brackets were the issue? – Julien Refour Jan 19 '16 at 21:41

1 Answers1

1

As already written in the comments, you should remove the brackets in your return statements in your functions voigt and voigt4. The problem with the brackets is that you put your array which you want to return in a list and thereby you reduce the dimensions of your returned object. Consider the following example:

import numpy as np
ar = np.array([1, 2, 3, 4])

Then the command

len(ar)

would return 4 and

a[0]

returns 1 as expected. If you now do

b = [ar] 

as you did in your return statement

b

would be

[array([1, 2, 3, 4])]

and

b[0]

not a single value anymore but the entire original array:

array([1, 2, 3, 4])

which means that you you receive and error like

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-269-33e961e0e4ea> in <module>()
----> 1 b[1]

IndexError: list index out of range

if you tried to access b[1].

So it is then not a big surprise that you receive an error message regarding the dimensions since you reduce the multidimensional object to a one dimensional object.

Cleb
  • 25,102
  • 20
  • 116
  • 151