繁体   English   中英

仅基于元组键的第一项合并元组项的字典

[英]Merge dictionary of tuple items based only on first term of tuple key

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

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')]

您可以使用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来获取两个迭代器,以便我可以访问这两个迭代器,但是推进其中一个迭代器而不是另一个迭代器会导致第一个迭代器产生的值存储在 memory 中以供第二个迭代器使用。 使用列表更简单,memory 消耗可能是相同的。 但是,您可能可以通过其他方式进一步优化它。

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

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需要自定义 class ,但这有点使其更具可读性:

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)]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM