如何将Sympy中命名表达式字符串转为表达式并计算简化比值?
SymPy表达式名称字符串转表达式并计算简化比值问题
实际问题描述
我使用SymPy定义了多个表达式,示例如下:
import sympy as sp a, b = sp.symbols('a,b', real=True, positive=True) Xcharles_YclassA_Zregion1 = 1.01 * a**1.01 * b**0.99 Xbob_YclassA_Zregion1 = 1.009999 * a**1.01 * b**0.99 Xbob_YclassA_Zregion2 = 1.009999 * a**1.01 * b**0.99000000001 ...
通过表达式名称区分不同选项(如charles、bob)和类别(如X)。需要一个函数,接收两个表达式名称字符串(如'Xcharles_YclassA_Zregion1'和'Xbob_YclassA_Zregion1'),返回它们的简化比值(示例中为1.00000099009999),以此快速对比结果差异(不关注写法差异,如2*a和a*2视为相同)。
注意事项
- 示例表达式为硬编码,实际场景中表达式来自大量其他运算。
- 并非所有选项组合都存在,表达式名称表是稀疏的。
- 改用
dict存储可能解决问题,但需修改大量代码。 - 不清楚
Symbol类与表达式的正式区别,希望明确该点辅助解决问题。
已尝试方案(未成功)
尝试编写如下函数:
def verify_ratio(vstr1, vstr2): """Compare the result of two different computations of the same quantity""" ratio = sp.N(sp.parsing.sympy_parser.parse_expr(vstr1)) / sp.parsing.sympy_parser.parse_expr(vstr2) print(vstr1 + ' / ' + vstr2, '=', sp.N(ratio)) return
但无法正常工作,测试代码及输出如下:
import sympy as sp a, b = sp.symbols('a,b', real=True, positive=True) expr2 = 1.01 * a**1.01 * b**0.99 print(type(expr2), '->', expr2) expr2b = sp.parsing.sympy_parser.parse_expr('expr2') print(type(expr2b), '->', expr2b) expr2c = sp.N(sp.parsing.sympy_parser.parse_expr('expr2')) print(type(expr2c), '->', expr2c) expr2d = sp.sympify('expr2') print(type(expr2d), '->', expr2d)
输出:
<class 'sympy.core.mul.Mul'> -> 1.01*a**1.01*b**0.99 <class 'sympy.core.symbol.Symbol'> -> expr2 <class 'sympy.core.symbol.Symbol'> -> expr2 <class 'sympy.core.symbol.Symbol'> -> expr2
可见parse_expr和sympify均将字符串'expr2'识别为Symbol,而非对应的表达式1.01 * a**1.01 * b**0.99。需要实现将表达式名称字符串转换为对应表达式的功能,并完成比值计算。
解决方案
1. 明确Symbol与表达式的区别
- Symbol:SymPy中代表变量的基础对象,比如
a、b,用来表示未知量,属于sympy.core.symbol.Symbol类型。 - 表达式(Expression):由Symbol、数字、运算符组合而成的复杂对象,比如
1.01*a**1.01*b**0.99属于sympy.core.mul.Mul类型,是多个Symbol和数字的乘积,本质是SymPy中各种运算类的实例。
2. 实现表达式名称字符串转表达式
parse_expr和sympify的作用是解析表达式文本(比如'1.01*a**1.01*b**0.99'),而非获取当前命名空间中已定义的变量。要根据字符串名称获取已定义的表达式,可直接通过Python的命名空间字典(globals()或locals())实现。
3. 正确的比值计算函数
import sympy as sp def verify_ratio(vstr1, vstr2): # 从全局命名空间获取对应的表达式对象 expr1 = globals().get(vstr1) expr2 = globals().get(vstr2) # 处理表达式不存在的情况 if expr1 is None: raise ValueError(f"表达式 '{vstr1}' 未定义") if expr2 is None: raise ValueError(f"表达式 '{vstr2}' 未定义") # 计算比值并简化(自动处理写法差异,如2*a和a*2视为相同) ratio = sp.simplify(expr1 / expr2) # 转换为数值结果(若比值为常数) numeric_ratio = sp.N(ratio) print(f"{vstr1} / {vstr2} = {numeric_ratio}") return numeric_ratio
4. 测试示例
a, b = sp.symbols('a,b', real=True, positive=True) Xcharles_YclassA_Zregion1 = 1.01 * a**1.01 * b**0.99 Xbob_YclassA_Zregion1 = 1.009999 * a**1.01 * b**0.99 # 调用函数 verify_ratio('Xcharles_YclassA_Zregion1', 'Xbob_YclassA_Zregion1')
输出:
Xcharles_YclassA_Zregion1 / Xbob_YclassA_Zregion1 = 1.00000099009999
补充说明
- 如果表达式是在函数内部定义的,需将
globals()替换为locals(),确保从当前局部命名空间获取变量。 - 若担心全局命名空间污染,可提前将所有表达式存入一个字典(比如
expr_dict = {'Xcharles_YclassA_Zregion1': ...}),然后通过expr_dict.get(vstr1)获取,这是更规范的方式,但需要修改部分代码来填充字典。
内容的提问来源于stack exchange,提问作者sancho.s ReinstateMonicaCellio
相关产品推荐
相关产品推荐

