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?
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']))