【问题标题】:Python spreadsheet-like formula parser?类似于 Python 电子表格的公式解析器?
【发布时间】:2012-02-24 19:39:14
【问题描述】:

我有一个字典列表,例如:

l =[{country:'Italy',sales:100,cost:50}{country:'Italy',sales:130,cost:60}      
    {country:'Germany',sales:110,cost:50}]

我想要一个 python 函数,它采用类似电子表格的输入 string(请从下面的@lott 中读取 cmets)公式,例如:

margin = (sales-cost)/sales

它还给了我:

l = [{country:'Italy',sales:100,cost:50,margin:1} ...]

您知道现有的任何这样做的库吗?或者您知道如何实现它?

我已经有了一个想法,如下所示,但我想要一种更好的方法来解析公式。处理 '()' 等中的块的东西。

parsed_op = {'sales':1,'cost':-1}
calc_field_name = 'smi'
counter = -1
for d in data:
    counter = counter + 1
    calc = sum([float(d[item])*parsed_op[item] for item in parsed_op])
    d[calc_field_name] = calc
    del data[counter]
    data.append(d)

【问题讨论】:

  • 此类事物的现有库:pandas.
  • 熊猫对于我需要的东西来说太复杂和太大了。

标签: python excel parsing formula


【解决方案1】:

在我看来,真正的问题是将数字放在有单词的地方。

一种方法是使用re.sub() 和一些字典格式(我真的不知道他们的真名,但是here 有一些例子)

代码:

import re

dct = {'country': 'Italy', 'sales': 100, 'cost': 50}
formula = 'margin = (sales-cost)/sales'

res_name,operation = formula.split('=')
num_formula = re.sub(r'([a-zA-Z]+)', r'{d[\1]}', operation.strip()).format(d=dct)
num_formula  # '(100-50)/100'

dct[res_name.strip()] = eval(num_formula.format(d=dct))

结果:

{'country': 'Italy', 'cost': 50, 'margin': 0.5, 'sales': 100}

我使用eval() 来评估字符串中的数值运算。通常使用eval() 是一种不好的做法,但这里非常方便。

无论如何,我相信您可以将 eval() 评估替换为其他内容。


快速说明

re.sub() 做了什么:

>>> re.sub(r'([a-zA-Z]+)', r'{d[\1]}', '(sales-cost)/sales')
'({d[sales]}-{d[cost]})/{d[sales]}'
  • r'([a-zA-Z]+)' 是模式。
    • [a-zA-Z] 匹配任何字母字符。
    • 紧随其后的+ 告诉我们匹配一个或多个,在我们的例子中是字母字符。
    • 括号用于分组。这意味着里面的东西将是一个组。因为我们只有一对括号,这将是第 1 组。
  • r'{d[\1]}' 是替换。
    • \1 代表“放第 1 组”。
    • 所以基本上是要包装与{d[]}匹配的内容。

要了解有关 re 模块的更多信息,请查看official doc

格式化的工作原理:

>>> '{d[first]} + {d[second]}'.format(d=dct)
'1 + 2'

把这两个东西和一些strip()放在一起,得到干净的字符串,你就会得到上面的代码。

【讨论】:

  • 嘿,谢谢。我几乎都明白。就是这个:r'{d[\1]}'。为什么是“1”?
  • @kfk: \1 代表第一组:模式中括号内的内容。再看一下代码,我发现了一个错误:匹配应该与[a-zA-Z] 匹配,而不是与\w 匹配,因为单词字符包括nubers,\w 就像[a-zA-Z0-9_]。我修复了这个更新代码:)
【解决方案2】:

做这样的事,你会更快乐。

Metrics = namedtuple('Metrics', 'country,sales,cost' )

Margin = namedtuple( 'Margin', 'country,sales,cost,margin' )

metrics = ( Metrics(**row) for row in l ) # a one-use only generator; not a sequence
margin = [ 
    Margin( m.country, m.sales, m.cost,
       margin= (m.sales-m.cost)/m.sales 
    )
for m in metrics ]

这很有效,因为您的公式 margin= (m.sales-m.cost)/m.sales 非常非常易于阅读、理解和修改。

【讨论】:

  • 是的,这很简单,但我需要它是一个字符串公式。想象一下键 a b 和 c。我需要做这样的事情: sum((a*b + c)/a)...
  • @kfk:为什么它需要是一个字符串?这似乎很愚蠢。没有充分的理由。如果它必须是一个字符串,请更新问题以非常非常清楚地说明这一点。还。搜索,因为已询问“我如何评估字符串”。
  • @lott:我需要在一个小型网络应用程序中使用它。人们必须能够添加计算列。这只能通过从输入字段传递字符串公式来完成。
  • @kfk:请更新这个问题,非常非常清楚地说明这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-09
  • 1970-01-01
  • 2012-09-30
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
相关资源
最近更新 更多