如何用Regex和Polars高效解析数学公式中的变量名?
解决Polars中无法用环视正则提取数学公式变量名的问题
由于Polars的字符串正则基于RE2引擎,不支持环视断言(比如(?!\()),可以换一种思路:先提取所有符合变量/函数命名规则的标识符,再过滤掉后面紧跟(的函数名,既能利用Polars原生矢量化操作的高效性,又能得到目标变量名。
实现代码
import polars as pl # 正则匹配所有符合命名规则的标识符(包括带(的函数名) FORMULA_PATTERN = r"\b[A-Za-z][A-Za-z0-9_]*\b\(?" # 示例公式 formulas = ["3*sin(x1+x2)+A_0", "ab*exp(2*x)"] # 提取并过滤得到变量名 result = ( pl.Series(formulas) .str.extract_all(FORMULA_PATTERN) # 过滤掉包含(的元素(即函数名) .list.eval(pl.element().filter(~pl.element().str.contains("(", literal=True))) ) print(result) # 输出: # Series: '' [list[str]] # [ # ["x1", "x2", "A_0"] # ["ab", "x"] # ]
思路说明
- 正则调整:把原方案中的负向环视
(?!\()替换成可选匹配\(?,这样会捕获两类内容:- 普通变量名(如
x1、A_0) - 带括号前缀的函数名(如
sin(、exp()
- 普通变量名(如
- 过滤处理:借助Polars的
list.eval和filter方法,移除包含(的元素,剩下的就是纯变量名。
性能对比(基于提供的基准测试)
测试结果显示三种方案的性能差异:
parse_lookahead(map_elements调用Python原生re):10.7 ms/循环parse_no_lookahead_and_filter(Polars原生操作):1.31 ms/循环parse_baseline(未过滤函数名):708 μs/循环
可见,这种方案既准确排除了函数名,性能又接近基准水平,比用map_elements快一个数量级,完全发挥了Polars矢量化计算的优势。
注意事项
- 绝大多数数学公式中,
(前的标识符都是函数名,该逻辑能覆盖常规场景;若存在变量名后紧跟(的极端情况,需根据实际需求微调规则。 - 使用
literal=True处理(,是为了避免将其当作正则元字符,确保匹配准确。
内容的提问来源于stack exchange,提问作者user11062613
相关产品推荐
相关产品推荐

