评估用户输入的模型为python函数

时间:2016-12-10 19:25:49

标签: string function python-3.x anonymous-function

我必须在python 3.4中编写一个需要以下内容的任务:   - 程序要求用户输入由多个子模型以字符串形式制作的数据模型,如下所示:

# the program ask :
please input the submodel1
# user will input:
A = a*x + b*y*exp(3*c/d*x)
# then the program ask :
-> please input the submodel2
# user will input:
B = e*x + f*y*exp(3*h/d*x)
# then the program ask :
-> please input the main model:
# user will input:
Y = A*x - exp(B**2/y)

然后程序采用这些模型(字符串)并对它们执行一些操作,例如将主模型曲线拟合到现有数据并绘制结果并显示参数值。 这里的想法是让用户在运行时自由选择模型,而无需将其编程为应用程序内的功能。 我的问题在于将字符串转换或解释为返回值的python函数。我探索了类似eval()函数的解决方案,但我正在寻找一个类似于MatLab的解决方案

func = sym('string')
func = matlabfunction(func)

创建一个匿名函数,您可以轻松处理它 希望我明确表示,如果需要进一步澄清,请告诉我, 解决方案应该与python 3.4兼容 提前致谢

3 个答案:

答案 0 :(得分:0)

您可以使用以下解析: https://pypi.python.org/pypi/parse 并训练你的解析器识别数学表达式, 看这个例子(python27):
http://www.nerdparadise.com/tech/python/parsemath/

# A really simple expression evaluator supporting the
# four basic math functions, parentheses, and variables.

class Parser:
    def __init__(self, string, vars={}):
        self.string = string
        self.index = 0
        self.vars = {
            'pi' : 3.141592653589793,
            'e' : 2.718281828459045
            }
        for var in vars.keys():
            if self.vars.get(var) != None:
                raise Exception("Cannot redefine the value of " + var)
            self.vars[var] = vars[var]

    def getValue(self):
        value = self.parseExpression()
        self.skipWhitespace()
        if self.hasNext():
            raise Exception(
                "Unexpected character found: '" +
                self.peek() +
                "' at index " +
                str(self.index))
        return value

    def peek(self):
        return self.string[self.index:self.index + 1]

    def hasNext(self):
        return self.index < len(self.string)

    def skipWhitespace(self):
        while self.hasNext():
            if self.peek() in ' \t\n\r':
                self.index += 1
            else:
                return

    def parseExpression(self):
        return self.parseAddition()

    def parseAddition(self):
        values = [self.parseMultiplication()]
        while True:
            self.skipWhitespace()
            char = self.peek()
            if char == '+':
                self.index += 1
                values.append(self.parseMultiplication())
            elif char == '-':
                self.index += 1
                values.append(-1 * self.parseMultiplication())
            else:
                break
        return sum(values)

    def parseMultiplication(self):
        values = [self.parseParenthesis()]
        while True:
            self.skipWhitespace()
            char = self.peek()
            if char == '*':
                self.index += 1
                values.append(self.parseParenthesis())
            elif char == '/':
                div_index = self.index
                self.index += 1
                denominator = self.parseParenthesis()
                if denominator == 0:
                    raise Exception(
                        "Division by 0 kills baby whales (occured at index " +
                        str(div_index) +
                        ")")
                values.append(1.0 / denominator)
            else:
                break
        value = 1.0
        for factor in values:
            value *= factor
        return value

    def parseParenthesis(self):
        self.skipWhitespace()
        char = self.peek()
        if char == '(':
            self.index += 1
            value = self.parseExpression()
            self.skipWhitespace()
            if self.peek() != ')':
                raise Exception(
                    "No closing parenthesis found at character "
                    + str(self.index))
            self.index += 1
            return value
        else:
            return self.parseNegative()

    def parseNegative(self):
        self.skipWhitespace()
        char = self.peek()
        if char == '-':
            self.index += 1
            return -1 * self.parseParenthesis()
        else:
            return self.parseValue()

    def parseValue(self):
        self.skipWhitespace()
        char = self.peek()
        if char in '0123456789.':
            return self.parseNumber()
        else:
            return self.parseVariable()

    def parseVariable(self):
        self.skipWhitespace()
        var = ''
        while self.hasNext():
            char = self.peek()
            if char.lower() in '_abcdefghijklmnopqrstuvwxyz0123456789':
                var += char
                self.index += 1
            else:
                break

        value = self.vars.get(var, None)
        if value == None:
            raise Exception(
                "Unrecognized variable: '" +
                var +
                "'")
        return float(value)

    def parseNumber(self):
        self.skipWhitespace()
        strValue = ''
        decimal_found = False
        char = ''

        while self.hasNext():
            char = self.peek()            
            if char == '.':
                if decimal_found:
                    raise Exception(
                        "Found an extra period in a number at character " +
                        str(self.index) +
                        ". Are you European?")
                decimal_found = True
                strValue += '.'
            elif char in '0123456789':
                strValue += char
            else:
                break
            self.index += 1

        if len(strValue) == 0:
            if char == '':
                raise Exception("Unexpected end found")
            else:
                raise Exception(
                    "I was expecting to find a number at character " +
                    str(self.index) +
                    " but instead I found a '" +
                    char +
                    "'. What's up with that?")

        return float(strValue)

def evaluate(expression, vars={}):
    try:
        p = Parser(expression, vars)
        value = p.getValue()
    except Exception as (ex):
        msg = ex.message
        raise Exception(msg)

    # Return an integer type if the answer is an integer
    if int(value) == value:
        return int(value)

    # If Python made some silly precision error
    # like x.99999999999996, just return x + 1 as an integer
    epsilon = 0.0000000001
    if int(value + epsilon) != int(value):
        return int(value + epsilon)
    elif int(value - epsilon) != int(value):
        return int(value)

    return value

print evaluate("1 + 2 * 3")
print evaluate("(1 + 2) * 3")
print evaluate("-(1 + 2) * 3")
print evaluate("(1-2)/3.0 + 0.0000")
print evaluate("1 + pi / 4")
print evaluate("(a + b) / c", { 'a':1, 'b':2, 'c':3 })
print evaluate("(x + e * 10) / 10", { 'x' : 3 })
print evaluate("1.0 / 3 * 6")
print evaluate("(1 - 1 + -1) * pi")
print evaluate("pi * e")

答案 1 :(得分:0)

我通过创建一个小解析器找到了我的问题的解决方案,或者让我们说一个基于python库PLY的非常小的dsl,请参考这个网页了解详细信息: http://www.dabeaz.com/ply/ply.html#ply_nn1 和这个项目: https://github.com/maldoinc/mamba

答案 2 :(得分:0)

lmfit(http://lmfit.github.io/lmfit-py/)有一个您可能会觉得有用的ExpressionModel:

/_sql

这使用asteval(http://newville.github.io/asteval),因此支持相当丰富的Python语法子集和numpy常量和ufunc,包括import numpy as np import matplotlib.pyplot as plt from lmfit.models import ExpressionModel # make fake data x = np.linspace(-10, 10, 201) amp, cen, wid = 3.4, 1.8, 0.5 y = amp * np.exp(-(x-cen)**2 / (2*wid**2)) / (np.sqrt(2*np.pi)*wid) y += np.random.normal(size=len(x), scale=0.01) # create model from expression model= ExpressionModel("amp * exp(-(x-cen)**2 /(2*wid**2))/(sqrt(2*pi)*wid)") # fit data to that model result = model.fit(y, x=x, amp=5, cen=5, wid=1) # show results print(result.fit_report()) plt.plot(x, y, 'bo') plt.plot(x, result.init_fit, 'k--') plt.plot(x, result.best_fit, 'r-') plt.show() sqrtexp,{{ 1}},等等。