Python: how to evaluate a function that is a string?
I am getting some models from the database as
f(t)=(2.128795454425367)+(208.54359721863273)*t+(26.098128487929266)*t^2+(3.34369909584111)*t^3+(-0.3450228278737971)*t^4+(-0.018630757967458885)*t^5+(0.0015029038553239819)*t^6;
which is like a string.
I need to evaluate this function for t in range(1, 13)
Now I have to manually copy these functions and run them
print [1.2381648958643592 + \
153.55656654019816 * t +\
22.99318731025164 * (t**2) +\
11.060577906796075 * (t**3) +\
-1.3465054084767891 * (t**4) + \
0.016926765998876842 * (t**5) +\
0.001500086893490721 * (t**6) for t in range(1, 13)]
Is there a better way to do this in python?
source to share
If performance is not a major issue, and if you're only evaluating it at 12 points, I suspect it isn't - then you can use the handy sympy to do most of the work for you. For example:
>>> import sympy
>>> sympy.sympify("t**5 - t + 3")
t**5 - t + 3
>>> sympy.sympify("t**5 - t + 3").subs({"t": 10})
99993
We can wrap this in a function that returns a function:
import sympy
def definition_to_function(s):
lhs, rhs = s.split("=", 1)
rhs = rhs.rstrip('; ')
args = sympy.sympify(lhs).args
f = sympy.sympify(rhs)
def f_func(*passed_args):
argdict = dict(zip(args, passed_args))
result = f.subs(argdict)
return float(result)
return f_func
which we can apply, even in more complex cases that are not available to a regular expression:
>>> s = "f(t)=(2.128795454425367)+(208.54359721863273)*t+(26.098128487929266)*t^2+(3.34369909584111)*t^3+(-0.3450228278737971)*t^4+(-0.018630757967458885)*t^5+(0.0015029038553239819)*t^6;"
>>> f = definition_to_function(s)
>>> f(0)
2.128795454425367
>>> f(10)
4230.6764921149115
>>> f = definition_to_function("f(a,b,c) = sin(a)+3*b-4*c")
>>> f(1,2,3)
-5.158529015192103
>>> import math
>>> math.sin(1)+3*2-4*3
-5.158529015192103
source to share
If you want to parse the "function" string, you can do something like this:
import re
s = "f(t)=(2.128795454425367)+(208.54359721863273)*t+(26.098128487929266)*t^2\
+(3.34369909584111)*t^3+(-0.3450228278737971)*t^4+(-0.018630757967458885)*t^5\
+(0.0015029038553239819)*t^6;"
def f(t):
l = map(float, re.findall("-?\\d+\\.\\d+", s))
return sum(b * t**a for a,b in enumerate(l))
print map(f, xrange(1,13))
[239.75206957484252, 544.337732955938, 921.544112756058, 1366.6221363666925, 1864.8848673959649, 2393.2591324279497, 2922.9192385578326, 3423.0027817028927, 3865.40859389
This approach assumes that the function string will always be
c0 + c1 t + c2 t ^ 2 + c3 t ^ 4 + ... cn t ^ (n + 1)
and works by extracting floating point numbers from a string and using them to create a real Python function.
source to share
You can store this function as a python expansion in your database, and when you get the string, just do something like eval (funcstr.replace ('x', 'yvalue')).
To show you an example:
funcstr = '2*x+5'
evalpoint = funcstr.replace('x', '5')
val = eval(funcstr)
At this point, val should be evaluated to 15
source to share
As NPE says, the correct answer here is to write a parser (and a simple interpreter) for your expression language.
Or, better yet, if at all possible, first generate the expressions in Python rather than in a language that is almost, but not entirely, compatible with a subset of Python.
Or, even better, if a language is just a way of representing a list of coefficients for a polynomial, just think of it as a list of coefficients that is much easier to parse than any real general-purpose language. For example, suppose the following was specified in the database:
2.128795454425367, 208.54359721863273, 26.098128487929266, 3.34369909584111, -0.3450228278737971, -0.018630757967458885, 0.0015029038553239819
Then, to accomplish this in Python, you would do the following:
def eval_polynomial(polynomial, value):
coefficients = [float(x.strip()) for x in polynomial.split(',')]
return sum(coefficient * (value**exponent)
for exponent, coefficient in enumerate(coefficients))
Then:
>>> [eval_polynomial(expr, t) for t in range(1, 13)]
But if you really want to do it without changing what's in the database, you can simply convert it to a Python expression and evaluate it:
>>> expr = 'f(t)=(2.128795454425367)+(208.54359721863273)*t+(26.098128487929266)*t^2+(3.34369909584111)*t^3+(-0.3450228278737971)*t^4+(-0.018630757967458885)*t^5+(0.0015029038553239819)*t^6;'
>>> removef = re.sub(r'f\((\w+)\)=', 'lambda \1: ', expr)
>>> fixpower = re.sub(r'(\w+)\^(\d+)', r'(\1**\2)', removef)
>>> nosemi = fixpower.replace(';', '')
>>> func = eval(nosemi)
>>> [func(t) for t in range(1, 13)]
[239.75206957484252, 544.337732955938, 921.544112756058, 1366.6221363666925, 1864.8848673959649, 2393.2591324279497, 2922.9192385578326, 3423.0027817028927, 3865.4085456893295, 4230.676492114911, 4514.949840987468, 4738.019242139209]
But you probably don't want to do this.
And if you do, you probably want to write a transformer that works in your actual language, rather than guessing in your language based on one example ...
source to share
if you trust your sources you can do it with regex and eval:
# deletes the simicolon and everything before the space
my_str = start_str.split('=')[1][:-1]
# change ^ to ** because that the squared operator
my_str = re.sub('\^', '**', my_str)
# substitute the t for the numbers 1 to 13 and evaluate the string
results = [eval(re.sub('t', str(t), my_str)) for t in range(1,13)]
source to share