tBuLi / symfit

Symbolic Fitting; fitting as it should be.
http://symfit.readthedocs.org
MIT License
236 stars 19 forks source link

sympy expression with arbitrary variables/parameters #349

Open kevinxxq opened 2 years ago

kevinxxq commented 2 years ago

Hi, @tBuLi , thank you for the package. Just as https://stackoverflow.com/questions/55580926/how-to-extend-the-number-of-symfit-parameters-arbitrarily-in-python, I would like to wrap this package for api: model: (dict of) sympy expression(s). e.g; a * x+ b variables: x,y parameters: a,b

Here is your example:

a, b = parameters('a, b')
x, y = variables('x, y')
model = {y: a * x + b}

# Fit will use its default settings
fit = Fit(model, x=xdata, y=ydata)
fit_result = fit.execute()

Now it turns to a different arbitrary way: a,b may be c,d, x,y may be x1,y1

I will pass a list of varibles and parameters such as "a, b, c, d" and "x, y, z" and a related sympy expression.

How can I handle it?

Thank you very much!

tBuLi commented 2 years ago

Hi @kevinxxq, thanks for your question! I'm not sure I understand your problem tough. The symfit model dict can have any number of elements in it, e.g.

x_1, x_2, y_1, y_2 = variables('x_1, x_2, y_1, y_2')
y0, a_1, a_2, b_1, b_2 = parameters('y0, a_1, a_2, b_1, b_2')

model_dict = {
    y_1: y0 + a_1 * exp(- b_1 * x_1),
    y_2: y0 + a_2 * exp(- b_2 * x_2),
}

such as the example taken from here: https://symfit.readthedocs.io/en/stable/fitting_types.html#fitting-multiple-datasets. Maybe that helps? If not, maybe you can reformulate the question? I've had a look at the stackoverflow question but that deals with automatically generating the model, is that your problem?

kevinxxq commented 2 years ago

Thank you for so quick reply.

Not true. I don't know the left part of below declarification: x_1, x_2, y_1, y_2 = variables('x_1, x_2, y_1, y_2')

The variables come from the caller when the function is called,the signature of my function is something as below.

def my_fit_function(expr,list_var,list_para,dict_data(key_var,list_data))->list_para:
expr: a*x+b
list_var:[x,y]
list_para:[a,b]
dict_data:{x:[1,2,...10],y:[2,4,6...20]}

Thank you.

pckroon commented 2 years ago

It's a bit unclear to me what you're exactly asking, and your code snippet also does not add clarity.

The variables function does no magic. It simply returns a list of Variable objects. Beyond that x_1, x_2, y_1, y_2 = variables('x_1, x_2, y_1, y_2') is just tuple unpacking. So you could do:

list_of_variables = variables('x, y, z')
x, y, z = list_of_variables  # This is optional! You can also use list_of_variables[0] to get x, etc.
kevinxxq commented 2 years ago

@pckroon Thank you for involving. Sorry for the inconvenience.

Yes, I understand that the variables are tuple unpacking.

The problem is that I don't know the symbols for a sympy expression. I can't define x,y=variables("x,y) since it may be x_1,x_2=variables("x_1,x_2)

The way it would be possible or I expect is:

list_of_variables = variables('x, y, z')
list_of_parameters = parameters('a, b, c')

fit=Fit(expr,list_of_variables,list_of_parameters,dict_data(key_var,list_data),...)
fit_result = fit.execute()

Thank you.

pckroon commented 2 years ago

It's still not clear to me what you need/want. If you need the symbol names to pass the relevant data to Fit you can use dictionary unpacking. Otherwise I really need you to phrase your question more carefully: What do you want to achieve, what have you tried, what functionality are you missing.

Jhsmit commented 2 years ago

Do you know the number of variables and/or parameters?


from symfit import variables, parameters
from functools import reduce
from operator import add

num_terms = 5
var_names = [f'x_{i}' for i in range(num_terms)]
par_names = [f'a_{i}' for i in range(num_terms)]

var_tup = variables(', '.join(var_names))
par_tup = parameters(', '.join(par_names))

expression = reduce(add, [a*v**i for i, (v, a) in enumerate(zip(var_tup, par_tup))])

expression
>>> a_0 + a_1*x_1 + a_2*x_2**2 + a_3*x_3**3 + a_4*x_4**4

You can then do fitting by passing a dict with var_names as keys

kevinxxq commented 2 years ago

Thank you ALL, sorry that is me to consider it too simplistic or idealized: def my_fit_function(expr,list_var,list_para,dict_data(key_var,list_data))->list_para: It will work as a powerful funtion to any expresion, any number of variables/parameters.

I just want to wrap the code in python to serve other clients who don't know anything about python. They need pass the expression, list of variables in str, list of paramers in str, dict(str_variable, data), then they can get the fitted parameters(list of parameters).

So I expect:

fit=Fit(expr,list_of_variables,list_of_parameters,dict_data(key_var,list_data),...)   # here inside Fit class, zip(list_of_variables) would be helpful to evaluate expr. Sorry I didn't get into the internal implentation
fit_result = fit.execute()

It may be impossible now, I will try to split it to serveral different functions.

Thank you again.

pckroon commented 2 years ago

Ok. You'll need a way to turn your string expression into a sympy expression with the appropriate Variable/Parameter objects. That is out of scope for this issue/discussion though. It doesn't help for this discussion that your signature has a list_var, but it's actually a string, same for expr.

If you have a string of variables as you feed them to variables you know they're comma-separated, so you can do my_string_of_variables.split(',') to get all your variable names. Otherwise all Variable objects have a name attribute and str(my_var) will also do what you expect.

It's still not clear to me where you get stuck, we won't write your adapter function for you.