【问题标题】:How to sum all the values that belong to the same key?如何对属于同一键的所有值求和?
【发布时间】:2018-09-11 05:18:19
【问题描述】:

我正在从数据库中提取数据并假设我有这样的东西:

    Product Name    Quantity
    a               3
    a               5
    b               2
    c               7

我想根据产品名称对数量求和,所以这就是我想要的:

    product = {'a':8, 'b':2, 'c':7 }

这是我从数据库中获取数据后要做的事情:

    for row in result:
       product[row['product_name']] += row['quantity']

但这会给我:'a'=5 only,而不是 8。

【问题讨论】:

    标签: python python-3.x pymysql


    【解决方案1】:

    选项 1:熊猫

    这是一种方法,假设您从 pandas 数据帧 df 开始。该解决方案的复杂度为 O(n log n)。

    product = df.groupby('Product Name')['Quantity'].sum().to_dict()
    
    # {'a': 8, 'b': 2, 'c': 7}
    

    这个想法是您可以执行groupby 操作,该操作会生成一个按“产品名称”索引的系列。然后使用to_dict()方法转换成字典。

    选项 2:collections.Counter

    如果您从结果列表或迭代器开始,并希望使用 for 循环,则可以使用 collections.Counter 来获得 O(n) 复杂度。

    from collections import Counter
    
    result = [['a', 3],
              ['a', 5],
              ['b', 2],
              ['c', 7]]
    
    product = Counter()
    
    for row in result:
        product[row[0]] += row[1]
    
    print(product)
    # Counter({'a': 8, 'c': 7, 'b': 2})
    

    选项 3:itertools.groupby

    您还可以对itertools.groupby 使用字典推导式。这需要事先排序。

    from itertools import groupby
    
    res = {i: sum(list(zip(*j))[1]) for i, j in groupby(sorted(result), key=lambda x: x[0])}
    
    # {'a': 8, 'b': 2, 'c': 7}
    

    【讨论】:

    • 如果产品按'product_name' 以外的某个键排序,groupby 会发生什么情况?
    • pandas.DataFrame.groupby 不需要事先明确排序。这发生在后台。这就是为什么它是 O(n log n) 解决方案而不是集合。计数器 O(n) 解决方案。
    • 感谢您的信息,学到了新东西!我发现groupby 在列表中的行为有点令人惊讶。
    • @AndreyTyukin,是的 - 不应将数据框中的 pandas groupby 与列表中的 itertools groupby 混淆。 itertools 方法首先需要显式排序。
    【解决方案2】:

    如果你坚持使用循环,你可以这样做:

    # fake data to make the script runnable
    result = [
      {'product_name': 'a', 'quantity': 3},
      {'product_name': 'a', 'quantity': 5},
      {'product_name': 'b', 'quantity': 2},
      {'product_name': 'c', 'quantity': 7}
    ]
    
    # solution with defaultdict and loops
    from collections import defaultdict
    
    d = defaultdict(int)
    for row in result:
      d[row['product_name']] += row['quantity']
    
    print(dict(d))
    

    输出:

    {'a': 8, 'b': 2, 'c': 7}
    

    【讨论】:

      【解决方案3】:

      既然你提到了熊猫

      df.set_index('ProductName').Quantity.sum(level=0).to_dict()
      Out[20]: {'a': 8, 'b': 2, 'c': 7}
      

      【讨论】:

        【解决方案4】:

        使用tuple 存储结果。

        编辑:


        不清楚提到的数据是否真的是一个数据框。

        如果是,那么li = [tuple(x) for x in df.to_records(index=False)]


        li = [('a', 3), ('a', 5), ('b', 2), ('c', 7)]
        d = dict()
        for key, val in li:
            val_old = 0
            if key in d:
                val_old = d[key]
            d[key] = val + val_old
        print(d)
        

        输出

        {'a': 8, 'b': 2, 'c': 7}
        

        【讨论】:

        • 我认为OP不会轻易影响查询返回的数据格式。 int(str(...)) 有什么用?我认为不应该显式调用dunder __contains__,而是使用x in d。此外,看看其他答案:一个是defaultdict,另一个是Counter。两个版本看起来都稍微简洁一些。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-11
        • 1970-01-01
        • 2021-10-03
        • 2022-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多