Michael Boles
Michael Boles

Reputation: 369

TypeError when fitting surface to 3D scatter data using scipy.optimize.curve_fit()

I have a Pandas DataFrame with columns containing x, y, and z-values.

import pandas as pd
df = pd.DataFrame({'Age': x,
                   'Mileage': y,
                   'Price': z})

Using scipy.optimize.curvefit() I'm able to fit a univariate exponential function y = exp(-bx):

import numpy as np
from scipy.optimize import curve_fit

# fit y to x
def exp_function(x, a, b):
    return a * np.exp(-b * x)

popt, pcov = curve_fit(exp_function, df['Age'],    # x-values
                        df['Price'],               # y-values
                        absolute_sigma=False, maxfev=1000)

# popt
# array([2.81641498e+04, 1.29183078e-01])                      # a, b-values

But when I try to extend the same analysis to 3D, I encounter a TypeError:

# fit z to (x, y)
def exp_function_2(x, y, a, b, c):
    return (a/2) * (np.exp(-b * x) + np.exp(-c * y))

popt, pcov = curve_fit(exp_function_2, 
                       df['Age'],           # x-values
                       df['Mileage'],       # y-values
                       df['Price'],         # z-values
                       absolute_sigma=False, maxfev=1000)


# TypeError: exp_function_2() takes 5 positional arguments but 1518 were given

Seems like it thinks I'm passing 1518 arguments (the length of my Pandas dataframe) into exp_function_2().

Why does my code work for the 2D (x, y) fit but get hung up on the 3D (x, y, z) fit?

Upvotes: 0

Views: 186

Answers (1)

bnaecker
bnaecker

Reputation: 6440

You are calling the method with the incorrect arguments.

The docs indicate that the prototype is curve_fit(f, xdata, ydata, p0=None, ...), where p0 is the starting guess for the parameters of your function. So in the case when you have a TypeError, you are passing all 1518 elements of your frame as the default parameters to your function, which of course only accepts 5 arguments. The fact that your code works in the 2D case is a happy coincidence of not using the p0 keyword-argument at all.

You need to pass both the predictors as a single argument in xdata, and then unpack them inside the exponential function. Something like this (though I'm not sure I have the dataframe indexing correct, I rarely use pandas):

def exp_function_2(x, a, b, c):
    return (a/2) * (np.exp(-b * x['Age']) + np.exp(-c * x['Mileage']))

Upvotes: 2

Related Questions