【问题标题】:Merge dictionary of tuple items based only on first term of tuple key仅基于元组键的第一项合并元组项字典
【发布时间】:2022-10-05 06:18:45
【问题描述】:

我正在尝试将元组字典转换为元组列表。但是,我需要根据元组键的第一个值这样做。我已经制定了完成此任务的大部分步骤,但我无法弄清楚该过程的第一步。显然,如果有没有这些步骤的更清洁的方法,这些步骤可以忽略,但这是我迄今为止一直在尝试的过程:

input_dict = {("1", "a"): 1.0, ("1", "b"): 2.0, ("2", "a"): 4.0}
desired_output = [(4, "2", "a"), (3, "1", "a")]



# step 1) merge items, summing values based on first term in tuple key, keeping only one occurence of second term in tuple key

## ? can't figure out how to do this step. Does not matter if option 1 or option 2 is produced
desired_step_1_output_option_1 = {("1", "a"): 3.0, ("2", "a"): 4.0}
desired_step_1_output_option_2 = {("1", "b"): 3.0, ("2", "a"): 4.0}

# step 2) order dictionary by value and convert to list of tuples

output_step_2 = sorted(desired_step_1_output_option_1.items(), key=lambda item: item[1], reverse = True)
## Output: [(('2', 'a'), 4.0), (('1', 'a'), 3.0)]

# step 3) Re-order results

output_step_3 = [(keys, value) for value, keys in output_step_2]
## Output: [(4.0, ('2', 'a')), (3.0, ('1', 'a'))]

# step 4) convert values to int, and un-nest tuples

output_step_4 = [(int(value), *keys) for value, keys in output_step_3]
## Output: [(4, '2', 'a'), (3, '1', 'a')]

【问题讨论】:

    标签: python dictionary tuples flatten dictionary-comprehension


    【解决方案1】:

    您可以使用itertools.groupby 按第一项分组。

    使用input_dict.items() 作为输入来保持对完整元组键和值的访问。

    from itertools import groupby
    
    input_dict = {("1", "a"): 1.0, ("2", "a"): 4.0, ("1", "b"): 2.0}
    
    def aggregate(tupleDict):
        l = lambda k: k[0][0]
        for g in groupby(sorted(tupleDict.items(), key=l), key=l):
            group = [(elem[1], elem[0][1]) for elem in g[1]]
            total = int(sum(elem[0] for elem in group))
            yield (total, g[0], group[0][1])
    
    result = [a for a in sorted(aggregate(input_dict), key=lambda k: k[0], reverse=True)]
    print(result)
    

    请注意,g[1] 是一个迭代器。我创建了一个临时的group 列表,这样我就可以访问数值来计算总和,还可以访问第一个组元素来获取其中一个字母。我考虑使用itertools.tee 来获取两个迭代器,以便我可以访问这两个迭代器,但是推进一个迭代器而不推进另一个迭代器会导致第一个迭代器产生的值存储在内存中以供第二个迭代器使用。相反,使用列表更简单,并且内存消耗可能相同。但是,您可能可以通过其他方式进一步优化它。

    您还可以使用字典按第一项分组,并将第二项和数值存储为值。因为这个复合值,设置值有点麻烦:

    temp_dict = {}
    for key, value in input_dict.items():
        temp_dict[key[0]] = (
            key[1],
            temp_dict.setdefault(key[0], (None, 0.0))[1] + value
        )
    
    result = [(int(v[1]), k, v[0])
        for k, v in sorted(temp_dict.items(), key=lambda kv: kv[1][1], reverse=True)]
    

    同样,使用defaultdict 需要一个自定义类,因为它是复合类型,但它有点使它更具可读性:

    from collections import defaultdict
    
    class LetterValue:
        def __init__(self, letter=None, value=0.0):
            self.letter = letter
            self.value = value
        def add(self, letter, value):
            self.letter = letter
            self.value += value
    
    temp_dict = defaultdict(LetterValue)
    
    for key, value in input_dict.items():
        temp_dict[key[0]].add(key[1], value)
    
    result = [(int(v.value), k, v.letter)
        for k, v in sorted(temp_dict.items(), key=lambda kv: kv[1].value, reverse=True)]
    

    【讨论】:

    • 谢谢!这很好用。我正在玩它,并注意到您的方法依赖于按顺序排列的键。随着input_dict = {("1", "a"): 1.0, ("2", "a"): 2.0, ("1", "b"): 4.0} 的稍微修改的输入,我得到了[(4, '1', 'b'), (2, '2', 'a'), (1, '1', 'a')] 的结果,所以我在调用你的aggregate() 函数之前添加了这个:from collections import OrderedDictinput_dict = OrderedDict(sorted(input_dict.items()))this post 现在不管初始顺序如何,它都可以工作
    猜你喜欢
    • 2012-10-11
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多