Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scipy.optimize.curve_fit, TypeError: unsupported operand type

I've done a search and the problem seems similar to Python scipy: unsupported operand type(s) for ** or pow(): 'list' and 'list' however the solution posted there did not work and I think it may actually be different.

I am trying to fit a curve to data using scipy.curve_fit, when I leave all 3 parameters free everything works correctly and I get the expected result.

def func(x,a,b,c):
  return a*np.exp(b*(x**c)) 

popt, pcov = curve_fit(func,x,y)

However when I try to fix one of the values (c=2) as below,

def func2(x,a,b):
  return a*np.exp(b*(x**2))

popt, pcov = curve_fit(func2,x,y)

I get TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'list' using numpy.power(x,2) as suggested in the linked question allows the code to run but produces the wrong result. Anyone see what I'm doing wrong?

Edited to add: Even more confusingly leastsq, which as far I know is used by curve_fit, with the 2nd formula works.

2nd Edit: To those to mentioned the list problems X and Y are now both arrays and the code runs without error. However func2 still produces drastically the wrong result. (I would post the graph here but apparently I need more rep.)

Func 1 curvefit gives [a,b,c] = [ 1.71890826, -0.0239123, 3.17039851] however for func2 it all goes wrong [a,b] = [ -2.88694423e-15, 9.99999998e-01]. I don't understand how such a small change can be causing such a drastic problem, leastsq was able to fit this data with c=2.

like image 213
user1889259 Avatar asked Jan 11 '13 19:01

user1889259


2 Answers

The TypeError occurs because the x being passed to func2 is a list.

Here is an example:

import numpy as np
import scipy.optimize as optimize
def func2(x,a,b):
    return a*np.exp(b*(x**2))

x = np.linspace(0,1,6).reshape(2,-1)
y = func2(x,1,1)
x = x.tolist()
y = y.tolist()
print(x)
# [[0.0, 0.2, 0.4], [0.6000000000000001, 0.8, 1.0]]
print(y)
# [[1.0, 1.0408107741923882, 1.1735108709918103], [1.4333294145603404, 1.8964808793049517, 2.718281828459045]]

popt, pcov = optimize.curve_fit(func2, x, y)
# TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

In this case, func2 maps an array x of shape (2,3) to an array y of shape (2,3). The function optimize.curve_fit expects the return value from func2 to be a sequence of numbers -- not an array.

Fortunately for us, in this case, func2 is operating element-wise on each component of x -- there is no interaction between the elements of x. So it really makes no difference if we pass an array x of shape (2,3) or an 1D array of shape (6,).

If we pass an array of shape (6,) then func2 will return an array of shape (6,). Perfect. That's will do just fine:

x = np.asarray(x).ravel()
y = np.asarray(y).ravel()
popt, pcov = optimize.curve_fit(func2, x, y)
print(popt)
# [ 1.  1.]
like image 64
unutbu Avatar answered Oct 02 '22 23:10

unutbu


What x values did you use? Following example works for me.

from scipy.optimize import curve_fit
import numpy as np

def func2(x,a,b):
  return a*np.exp(b*(x**2))

x = np.linspace(0,4,50)
y = func2(x, 2.5, 2.3)
yn = y + 6.*np.random.normal(size=len(x))
popt, pcov = curve_fit(func2,x,yn)
print popt, pcov

It gives the result depending on the random function:

[ 1.64182333  2.00134505] [[  1.77331612e+11  -6.77171181e+09]
 [ -6.77171181e+09   2.58627411e+08]]

Are your x and yn values of type list? Following example gives your error message:

print range(10)**2

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
like image 31
Holger Avatar answered Oct 03 '22 00:10

Holger