Python: How to evaluate a function which is string?

前端 未结 5 1959
悲&欢浪女
悲&欢浪女 2021-01-16 23:22

I get some models from database as

f(t)=(2.128795454425367)+(208.54359721863273)*t+(26.098128487929266)*t^2+(3.34369909584111)*t^3+(-0.3450228278737971)*t^4+         


        
相关标签:
5条回答
  • 2021-01-16 23:30

    As NPE says, the right answer here is to write a parser (and simple interpreter) for your expression language.

    Or, even better, if at all possible, generate the expressions in Python in the first place, instead of in a language which is almost but not quite compatible with a subset of Python.

    Or, even better, if the language is just a way to represent the list of coefficients for a polynomial, just represent it as a list of coefficients, which will be a lot easier to parse than any actual general-purpose language. For example, let's say the database held this:

    2.128795454425367, 208.54359721863273, 26.098128487929266, 3.34369909584111, -0.3450228278737971, -0.018630757967458885, 0.0015029038553239819
    

    Then, to execute that in Python, you'd do this:

    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, really want to do this without changing what's in the database, you could just transform it into a Python expression and eval 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 again, you probably don't want to do this.

    And, if you do, you probably want to write a transformer that works on your actual language, rather than on a stab-in-the-dark guess at your language based on a single example…

    0 讨论(0)
  • 2021-01-16 23:34

    if you trust your sources you can do it like this with regex and eval:

    # deletes the simicolon and everything before the space
    my_str = start_str.split('=')[1][:-1]
    # change ^ to ** because that's 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)]
    
    0 讨论(0)
  • 2021-01-16 23:42

    If performance isn't a major concern -- and if you're only evaluating it at 12 points, I suspect it's not -- then you can leverage the handy sympy library to do a lot 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 up in a function which 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 then apply, even to more complex cases beyond the easy reach of regex:

    >>> 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
    
    0 讨论(0)
  • 2021-01-16 23:43

    If you want to parse the 'function' string, you could 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.4085456893295, 4230.676492114911, 4514.949840987468, 4738.019242139209]
    

    This approach assumes that the function string will always be of the form

    c0 + c1 t + c2 t^2 + c3 t^4 + ... cn t^(n+1)
    

    and works by extracting the floating point numbers from the string and using them to generate an actual Python function.

    0 讨论(0)
  • 2021-01-16 23:50

    You can store the function as a python expersion 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

    0 讨论(0)
提交回复
热议问题