解析逻辑表达式

2024-03-29 14:48:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个任务,我必须根据用户指定的逻辑表达式过滤数据帧。现在,我看到了一个名为PyParser或LARK的模块,我想使用它,但我似乎不知道如何设置它们

我有几个操作符,比如CONTAINSEQUALFUZZY_MATCH等等。另外,我想把一些表达式组合成更复杂的表达式

示例表达式:

ColumnA CONTAINS [1, 2, 3] AND (ColumnB FUZZY_MATCH 'bla' OR ColumnC EQUAL 45)

因此,我希望有一些结构化的Dict或List,按照如何执行它们的顺序列出操作的级别。因此,此示例表达式的预期结果如下所示:

[['ColumnA', 'CONTAINS', '[1, 2, 3]'], 'AND', [['ColumnB', 'FUZZY_MATCH', 'bla'], OR, ['ColumnC', 'EQUAL', '45']]]

或以书面形式:

{
  'EXPR1': {
    'col': 'ColumnA', 
    'oper': 'CONTAINS', 
    'value': '[1, 2, 3]']
  },
  'OPERATOR': 'AND', 
  'EXPR2': {
    'EXPR21': {
      'col': 'ColumnB', 
      'oper': 'FUZZY_MATCH', 
      'value': 'bla'
    }, 
    'OPERATOR': OR, 
    'EXPR22': {
      'col': 'ColumnC', 
      'oper': 'EQUAL', 
      'value': '45'
    }
  }
}

或者类似的。如果你有更好的方法来构建结果,我愿意听取建议。我对这方面还很陌生,所以我相当肯定这方面可以改进


Tags: orand示例value表达式matchcolequal
1条回答
网友
1楼 · 发布于 2024-03-29 14:48:42

有趣的问题:)

似乎是shunting yard算法的一个相对简单的应用。
我已经编写了代码来解析像"((20 - 10 ) * (30 - 20) / 10 + 10 ) * 2"over here这样的表达式

import re


def tokenize(str):
   return re.findall("[+/*()-]|\d+", expression)

def is_number(str):
    try:
        int(str)
        return True
    except ValueError:
        return False


def peek(stack):
    return stack[-1] if stack else None


def apply_operator(operators, values):
    operator = operators.pop()
    right = values.pop()
    left = values.pop()
    values.append(eval("{0}{1}{2}".format(left, operator, right)))


def greater_precedence(op1, op2):
    precedences = {"+": 0, "-": 0, "*": 1, "/": 1}
    return precedences[op1] > precedences[op2]


def evaluate(expression):
    tokens = tokenize(expression)
    values = []
    operators = []
    for token in tokens:
        if is_number(token):
            values.append(int(token))
        elif token == "(":
            operators.append(token)
        elif token == ")":
            top = peek(operators)
            while top is not None and top != "(":
                apply_operator(operators, values)
                top = peek(operators)
            operators.pop()  # Discard the '('
        else:
            # Operator
            top = peek(operators)
            while top is not None and top != "(" and greater_precedence(top, token):
                apply_operator(operators, values)
                top = peek(operators)
            operators.append(token)
    while peek(operators) is not None:
        apply_operator(operators, values)

    return values[0]


def main():
    expression = "((20 - 10 ) * (30 - 20) / 10 + 10 ) * 2"
    print(evaluate(expression))


if __name__ == "__main__":
    main()

我认为我们可以稍微修改代码,使其适用于您的案例:

  1. 我们需要修改在tokenize()中标记输入字符串的方式。
    基本上,给定字符串ColumnA CONTAINS [1, 2, 3] AND (ColumnB FUZZY_MATCH 'bla' OR ColumnC EQUAL 45),我们需要一个令牌列表:
    ['ColumnA', 'CONTAINS', '[1, 2, 3]', 'AND', '(', 'ColumnB', 'FUZZY_MATCH', "'bla'", 'OR', 'ColumnC', 'EQUAL', '45', ')']
    这在很大程度上取决于输入字符串的复杂程度,并且需要一些字符串处理,但它相当简单,我将把这留给您
  2. 修改is_number()函数以检测ColumnA[1, 2, 3]等内容。
    基本上,除了谓词CONTAINS/FUZZY_MATCH/EQUAL、运算符AND/OR和paranethes (/)之外的所有内容
  3. 修改greater_precedence(op1, op2)以返回true,如果op1['CONTAINS', 'EQUAL', ..]之间,而op2['AND', 'OR']之间。
    这是因为我们希望containsequals总是在AND/OR之前进行计算
  4. 修改apply_operator(operators, values)以实现如何计算布尔表达式ColumnA CONTAINS [1, 2, 3]或表达式true AND false的逻辑。
    请记住CONTAINS/FUZZY_MATCH/EQUAL/AND/OR等都是这里的运算符。
    可能你需要在这里写很多if-else的例子,因为这里可能有很多不同的操作符

相关问题 更多 >