Chad - 1 year ago 258

Python Question

I'm attempting to do some curve fitting within a class instance method, and the curve_fit function is giving my class instance method too many arguments.

The code is

`class HeatData(hx.HX):`

"""Class for handling data from heat exchanger experiments."""

then several lines of methods that work fine, then my function is:

`def get_flow(pressure_drop, coeff):`

"""Sets flow based on coefficient and pressure drop."""

flow = coeff * pressure_drop**0.5

return flow

and the curve_fit function call

`def set_flow_array(self):`

"""Sets experimental flow rate through heat exchanger"""

flow = self.flow_data.flow

pressure_drop = self.flow_data.pressure_drop

popt, pcov = spopt.curve_fit(self.get_flow, pressure_drop, flow)

self.exh.flow_coeff = popt

self.exh.flow_array = ( self.exh.flow_coeff * self.exh.pressure_drop**0.5 )

gives the error

`get_flow() takes exactly 2 arguments (3 given)`

I can make it work by defining get_flow outside of the class and calling it like this:

`spopt.curve_fit(get_flow, pressure_drop, flow)`

but that's no good because it really needs to be a method within the class to be as versatile as I want. How can I get this work as a class instance method?

I'd also like to be able to pass self to get_flow to give it more parameters that are not fit parameters used by curve_fit. Is this possible?

Answer Source

Unlucky case, and maybe a bug in curve_fit. curve_fit uses inspect to determine the number of starting values, which gets confused or misled if there is an extra `self`

.

So giving a starting value should avoid the problem, I thought. However, there is also an isscalar(p0) in the condition, I have no idea why, and I think it would be good to report it as a problem or bug:

```
if p0 is None or isscalar(p0):
# determine number of parameters by inspecting the function
import inspect
args, varargs, varkw, defaults = inspect.getargspec(f)
```

edit: avoiding the scalar as starting value

```
>>> np.isscalar([2])
False
```

means that the example with only 1 parameter works if the starting value is defined as [...], e.g.similar to example below:

```
mc.optimize([2])
```

An example with two arguments and a given starting value avoids the inspect call, and everything is fine:

```
import numpy as np
from scipy.optimize import curve_fit
class MyClass(object):
def get_flow(self, pressure_drop, coeff, coeff2):
"""Sets flow based on coefficient and pressure drop."""
flow = coeff * pressure_drop**0.5 + coeff2
return flow
def optimize(self, start_value=None):
coeff = 1
pressure_drop = np.arange(20.)
flow = coeff * pressure_drop**0.5 + np.random.randn(20)
return curve_fit(self.get_flow, pressure_drop, flow, p0=start_value)
mc = MyClass()
print mc.optimize([2,1])
import inspect
args, varargs, varkw, defaults = inspect.getargspec(mc.get_flow)
print args, len(args)
```

EDIT: This bug has been fixed so bound methods can now be passed as the first argument for curve_fit, if you have a sufficiently new version of scipy.

Commit of bug fix submission on github