对于特定的应用程序,我做了一个 GUI 来操作一些数据(内部:numpy 1D 数组),并绘制它们。
最终用户可以在 UI 中选择绘制各种系列
a
、b
、c
。
现在我还需要允许 a
、
b
、
c
的 “自定义组合”。更准确地说,用户(不懂 Python/Numpy,但可以学习一些关键字)应该在 GUI 文本框中输入一个“公式”,然后我的程序应该将其转录为真正的 numpy 代码(可能使用 eval(...)
,这里很少有安全问题,因为最终用户是唯一的用户),并绘制数据。
最终用户输入示例:
a * 3 + 1.234 * c - d
a + b.roll(2)
a + b / b.max() * a.max()
例如,允许的语法是:基本算术(+ * - / 和括号)、浮点数、
a.max()
和 a.roll(3)
来移位数组。
问题:Numpy 或 Scipy 中是否有函数提供这样一种方法来用基本算术语法解释数组组合?
对于代数部分,您可以使用 numexpr 库可以利用它。例如,以下代码片段将起作用:
import numpy as np
import numexpr as ne
a = np.random.rand(10)
b = np.random.rand(10)
c = np.random.rand(10)
d = np.random.rand(10)
ne.evaluate("a * 3 + 1.234 * c - d")
遗憾的是,该库并没有立即涵盖其他两种情况,但这可以通过一些字符串解析轻松实现。包含所有功能的最终版本可能如下所示:
import numpy as np
import numexpr as ne
import re
a = np.random.rand(10)
b = np.random.rand(10)
c = np.random.rand(10)
d = np.random.rand(10)
def expression_eval(
expression:str, a:np.array, b: np.array, c:np.array, d:np.array
) -> np.array:
#Snippet to manage max values:
a_max = a.max()
b_max = b.max()
c_max = c.max()
d_max = d.max()
for label in ["a", "b", "c", "d"]:
expression = expression.replace(f"{label}.max()", f"{label}_max")
#Snippet to manage rolling windows:
pattern = r'(\w)\.roll\((\d+)\)'
matches = re.findall(pattern, expression)
if matches: roll_results = [(match[0], int(match[1])) for match in matches]
else: roll_results = []
rolls = {}
for arr, window in roll_results:
expression = expression.replace(f"{arr}.roll({window})", f"{arr}_roll_{window}")
rolls[f"{arr}_roll_{window}"] = np.concatenate([
vars()[arr][window:],
np.zeros(window)
])
return ne.evaluate(expression, global_dict=rolls)
#Evaluation:
expression_1 = "a * 3 + 1.234 * c - d"
expression_2 = "a + b / b.max() * a.max()"
expression_3 = "a + b.roll(3) + c.roll(2) + d.roll(4)"
print(f"{expression_1}\n{expression_eval(expression_1, a, b, c, d)}\n")
print(f"{expression_2}\n{expression_eval(expression_2, a, b, c, d)}\n")
print(f"{expression_3}\n{expression_eval(expression_3, a, b, c, d)}\n")
本质上,我们在评估代数表达式之前将变量的每个函数替换为其计算值。请注意,对于滚动窗口,我们可以使用具有更动态方法的字典来适应滚动窗口的多种可能性。