【问题标题】:What is the most efficient way to sum a dict with multiple keys by one key?用一个键对具有多个键的字典求和的最有效方法是什么?
【发布时间】:2018-06-21 17:08:02
【问题描述】:

我有以下 dict 结构。

product1 = {'product_tmpl_id': product_id,
'qty':product_uom_qty,
'price':price_unit,
'subtotal':price_subtotal,
'total':price_total,
}

然后是产品列表,列表中的每一项都是上面结构的dict

list_ = [product1,product2,product3,.....]

我需要对列表中的项目求和,按 product_tmpl_id 键分组...我正在使用 dictcollections 但它只对 qty 键求和,我需要对除 product_tmpl_id 之外的键求和,这是标准分组方式

c = defaultdict(float)
for d in list_:
    c[d['product_tmpl_id']] += d['qty']
c = [{'product_id': id, 'qty': qty} for id, qty in c.items()]

我知道如何用 for 迭代来做到这一点,但试图寻找一种更 Pythonic 的方式

谢谢

编辑:

需要的是从这里传递:

lst = [
{'Name': 'A', 'qty':100,'price':10},
{'Name': 'A', 'qty':100,'price':10},
{'Name': 'A', 'qty':100,'price':10},
{'Name': 'B', 'qty':100,'price':10},
{'Name': 'C', 'qty':100,'price':10},
{'Name': 'C', 'qty':100,'price':10},
]

到这里

group_lst = [
{'Name': 'A', 'qty':300,'price':30},
{'Name': 'B', 'qty':100,'price':10},
{'Name': 'C', 'qty':200,'price':20},
]

【问题讨论】:

  • 它不会再有pythonic了。你的实现很好。
  • 您的替代方法是寻找处理此类数据结构的库,例如熊猫。对列求和的代码总体上会更简单、更快。
  • Aran-Fey 我的实现不起作用,因为它只求和,一个键“数量”,我需要求和“数量”、“总计”、“小计”、“单位价格”

标签: python python-2.7 python-collections


【解决方案1】:

使用基本的 Python,这并没有变得更好。你可以用itertools.groupby 破解一些东西,但它会很丑,而且可能更慢,当然不太清楚。

不过,正如 @9769953 所建议的,Pandas 是一个很好的包来处理这种结构化的表格数据。

In [1]: import pandas as pd
In [2]: df = pd.DataFrame(lst)
Out[2]:
  Name  price  qty
0    A     10  100
1    A     10  100
2    A     10  100
3    B     10  100
4    C     10  100
5    C     10  100
In [3]: df.groupby('Name').agg(sum)
Out[3]:
      price  qty
Name
A        30  300
B        10  100
C        20  200

如果您不想将数据保留为数据框,则只需要一点额外的魔力:

In [4]: grouped = df.groupby('Name', as_index=False).agg(sum)
In [5]: list(grouped.T.to_dict().values())
Out[5]:
[{'Name': 'A', 'price': 30, 'qty': 300},
 {'Name': 'B', 'price': 10, 'qty': 100},
 {'Name': 'C', 'price': 20, 'qty': 200}]

【讨论】:

    【解决方案2】:

    在冗长的一面,但完成了工作:

    group_lst = []
    lst_of_names = []
    for item in lst:
        qty_total = 0
        price_total = 0
    
        # Get names that have already been totalled
        lst_of_names = [item_get_name['Name'] for item_get_name in group_lst]
    
        if item['Name'] in lst_of_names:
            continue
    
        for item2 in lst:
            if item['Name'] == item2['Name']:
                qty_total += item2['qty']
                price_total += item2['price']
    
        group_lst.append(
            {
                'Name':item['Name'],
                'qty':qty_total,
                'price':price_total
            }
        )
    pprint(group_lst)
    

    输出:

    [{'Name': 'A', 'price': 30, 'qty': 300},
     {'Name': 'B', 'price': 10, 'qty': 100},
     {'Name': 'C', 'price': 20, 'qty': 200}]
    

    【讨论】:

    • 这就是我试图避免的
    • 不要认为有pythonic的方式解决它,除非你选择pandas(或类似的)路线
    【解决方案3】:

    您可以使用defaultdictCounter

    >>> from collections import Counter, defaultdict
    >>> cntr = defaultdict(Counter)
    >>> for d in lst:
    ...     cntr[d['Name']].update(d)
    ...
    >>> res = [dict(v, **{'Name':k}) for k,v in cntr.items()]
    >>> pprint(res)
    [{'Name': 'A', 'price': 30, 'qty': 300},
     {'Name': 'C', 'price': 20, 'qty': 200},
     {'Name': 'B', 'price': 10, 'qty': 100}]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-14
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 1970-01-01
      • 2021-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多