2012-02-24 48 views
2

我的字典,如清单:Python电子表格式的公式分析器?

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

我想一个Python函数,它接受一个类似电子表格的输入字符串(请阅读从下面@lott评论)公式,如:

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) 
+0

这类事情的现有库:[pandas](http://pandas.pydata.org/)。 – 2012-02-24 19:51:54

+0

熊猫对我所需要的东西来说太复杂了。 – kfk 2012-02-24 19:59:20

回答

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]匹配任何字母字符。
    • +之后告诉匹配一个或多个,在我们的情况下,字母字符,toghter。
    • 括号用于分组。这意味着里面的内容将成为一个群体。由于我们只有一对括号,它们将成为组1。
  • r'{d[\1]}'是替换。
    • \1代表“放置组编号1”。
    • 所以基本上是打包什么与{d[]}匹配。

要了解更多关于重新模块看一看的official doc

格式化工作原理:

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

将这个两个东西togheter一些strip()在这里和那里有干净的字符串,你最终会上面的代码。

+0

嘿,谢谢。我理解几乎所有。正是这样:r'{d [1]}'。为什么是“1”? – kfk 2012-02-24 20:42:03

+0

@kfk:'\ 1'代表第一组:模式中的内容是什么。再次看看代码我发现了一个bug:匹配应该用'[a-zA-Z]'来代替,而不是用'\ w'来代替,因为一个字符包括nubers,'\ w'就像'[A-ZA-Z0-9_]'。我修正了这个更新代码:) – 2012-02-24 21:11:29

1

做这样的事情,你会更开心。

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是非常,非常容易阅读,理解和修改。

+0

是的,这很容易,但我需要它是一个字符串公式。想象钥匙a b和c。我需要做的事情,如:sum((a * b + c)/ a)... – kfk 2012-02-24 19:58:48

+1

@kfk:为什么它需要是一个字符串?这似乎很愚蠢。这没有什么好的理由。如果它必须是一个字符串,请**更新**问题,以便非常清楚地说明问题。也。搜索,因为“我如何评估一个字符串”已被问到。 – 2012-02-24 20:01:36

+0

@lott:我需要一个小型的网络应用程序。人们必须能够添加计算列。这只能通过从输入字段传递字符串公式来完成。 – kfk 2012-02-24 20:04:12