Анализатор формул, похожий на электронную таблицу? - PullRequest
2 голосов
/ 24 февраля 2012

У меня есть список словарей, таких как:

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)

Ответы [ 2 ]

1 голос
/ 25 февраля 2012

Мне кажется, что настоящая проблема - поставить цифры там, где есть слова.

Один из способов сделать это может быть с re.sub() и некоторым форматированием словаря (я не знаю, как их настоящее имя, но здесь Есть несколько примеров) .

Код:

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[ ]}.

Чтобы узнать больше о модуле re, взгляните на официальный документ .

Как работает форматирование:

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

Поставьте этодве вещи вместе с некоторыми strip() здесь и там, чтобы иметь чистые строки, и вы в конечном итоге код выше.

1 голос
/ 24 февраля 2012

Сделайте что-то подобное, и вы станете счастливее.

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 очень, очень легко читать, понимать и изменять.

...