【问题标题】:Python: replace function by its expression, in mathematical expression as string, with Sympy?Python:用它的表达式替换函数,在数学表达式中作为字符串,用 Sympy?
【发布时间】:2019-11-04 22:27:37
【问题描述】:

我有一个函数定义,作为一个字符串,比如func_def = "f(x, y) = x + 2*y + 3"
我有一个数学表达式,作为一个字符串,包括那个函数,比如
expr = '2*a*b + f(b+a, 2*b)'

如何在我的表达式中用函数的值替换函数?
换句话说,我如何得到一个字符串expr_eval = "2*a*b + ( (b+a) + 2*(2*b) + 3 )"

是否有一个简单的解决方案,例如 Sympy 使用 sympifysubs 的组合以及其他一些功能?感觉应该有,但是没找到。我必须对包含许多符号的相当多的方程执行此操作,因此分别为每个方程创建 Sympy 符号似乎不是一个好选择。

目前,我使用正则表达式,但我发现这个解决方案难以提出、泛化(具有其他数量变量的其他函数)、缩放和阅读。它是这样的:

import re

func_def = "f(x, y) = x + 2*y + 3"
expr = '2*a*b + f(b+a, 2*b)'

# extract substring with function
str_to_replace = re.findall(r'f\([a-zA-z0-9\*\+\"\']*, [a-zA-z0-9\*\+\"\'/]*\)', expr)
str_to_replace = str_to_replace[0]

# extract function name and arguments in substring
func_name, args, _ = re.split('\(|\)', str_to_replace)
args = args.split(',')
args = [i.strip() for i in args]

# parse the function definition
func_def_lhs, func_def_rhs = func_def.split('=')
func_def_name, args_def, _ = re.split('\(|\)', func_def_lhs)
args_def = args_def.split(',')
args_def = [i.strip() for i in args_def]

# replace each argument in the definition by its expression
for i, arg_def in enumerate(args_def) : 

    func_def_rhs = func_def_rhs.replace(arg_def, '({})' .format(args[i]))

expr_eval = expr.replace(str_to_replace, '({})' .format(func_def_rhs))

【问题讨论】:

    标签: python replace sympy symbolic-math mathematical-expressions


    【解决方案1】:

    您可以使用re.sub

    import re
    
    strip_f = lambda f: (f.split("(")[0], f.split("(")[1][:-1].split(", "))
    
    def explicit(expr, functions):
        d = {}
        for f in functions:
            func_vars, func_def = f.split(" = ")
            func_name, func_vars = strip_f(func_vars)
            d[func_name] = (func_vars, func_def)
    
        def replace(match):
            m = match.groups()[0]
            fn_name, expr_vars = strip_f(m)
            func_vars, s = d[fn_name]
            for fv, ev in zip(func_vars, expr_vars):
                s = s.replace(fv, "("+ev+")")
            s = "("+s+")"
            return s
        return re.sub(r"(.\(([^\)]+,?)+?\))", replace, expr)
    
    
    expr = '2*a*b + f(b+a, 2*b) + g(5, 2*c, 3+a)'
    f1 = "f(x, y) = x + 2*y + 3"
    f2 = "g(x, y, z) = x + y + z - 2"
    
    print(explicit(expr, [f1, f2]))
    

    显示:

    2*a*b + ((b+a) + 2*(2*b) + 3) + ((5) + (2*c) + (3+a) - 2)
    

    正则表达式,分解:

    (                   begin capturing group for function
       .                match any character (function nname)
       \(               match open parenthesis
       (                begin capturing group for variable
         [^\)]+         match at least one non-parenthesis character
         ,?             match a comma if it's there
       )                end variable capturing 
       +?               match at least one variable
       \)               match close parenthesis
    )                   end capturing group
    

    如果您不介意输出是否简化,可以使用您提到的sympy 方法:

    import sympy
    
    expr = '2*a*b + f(b+a, 2*b) + g(5, 2*c, 3+a)'
    f1 = "f(x, y) = x + 2*y + 3"
    f2 = "g(x, y, z) = x + y + z - 2"
    
    def split(fn):
        return str(fn).partition("(")
    
    def check_fn(ex):
        name, sep, rest = split(ex)
        return name and sep
    
    def parse_functions(functions):
        fns = {}
        for f in functions:
            name, _, rest = split(f)
            fn = rest.split(" = ")
            fns[name] = fn[0][:-1].split(", "), fn[1]
        return fns
    
    def expand(expr, functions):
        fns = parse_functions(functions)
    
        def sub_fn(ex):
            with sympy.evaluate(False):
                vs, fn = fns[split(ex)[0]]
                fn = sympy.UnevaluatedExpr(sympy.sympify(fn))
                return fn.subs(dict(zip(vs, str(ex)[2:-1].split(", "))))
    
        return sympy.sympify(expr).replace(check_fn, sub_fn)
    
    print(sympy.StrPrinter({'order':'none'})._print(expand(expr, [f1, f2])))
    

    显示:

    2*a*b + a + b + 2*(2*b) + 3 + 5 + 2*c + a + 3 - 2
    

    请注意,这假设您需要完整的、未简化的、无序的方程。

    【讨论】:

    • 已编辑以添加对多种功能的支持
    • @presenter 我对其进行了更新以添加一个仅使用 sympy 方法且不使用正则表达式的方法。
    • 非常感谢!在第一个解决方案中,您能否详细说明模式"(.\(([^\)]+,?)+?\))"?在第二种解决方案中,您需要from sympy.abc import x, y, f 行做什么?
    • @presenter 添加了解释!另外,sympy 导入不是必需的,所以我删除了它们。 (它们来自我尝试过的一些早期方法。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 2020-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 1970-01-01
    相关资源
    最近更新 更多