如何对本身就是字典的Python字典值进行分组

时间:2019-04-02 16:02:31

标签: python grouping

我想按以下字典的值分组:

my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
           "Q3": {1: "animal", 2: "vehicle"}, Q4: {1: "animal", 2: "vehicle"}}

结果应如下所示:

result = {("Q1", "Q2"): {0: "no", 1: "yes"}, 
          ("Q3", "Q4"): {1: "animal", 2: "vehicle"}}

我已经尝试过这里列出的解决方案: Grouping Python dictionary keys as a list and create a new dictionary with this list as a value

使用collections.defaultdict无效,因为结果将暗示我用作分组键的字典最终会成为结果字典的键,如下所示:

result = {{0: "no", 1: "yes"}: ["Q1", "Q2"] , 
          {1: "animal", 2: "vehicle"}: ["Q3", "Q4"]}

当然这是行不通的,因为字典的键必须是不可变的。因此,我需要像Frozendict这样的东西,而这在python的标准库中是不可用的。

使用itertools.groupby也不起作用,因为它需要对数据进行排序。但是operator.itemgetter不能对字典进行排序。它说:

TypeError: '<' not supported between instances of 'dict' and 'dict'

因此,我想知道一种解决该问题的Python方法!谢谢您的帮助:)

4 个答案:

答案 0 :(得分:4)

除了使用frozendict之外,您还可以使用字典中项目的 frozenset

intermediate_dict = defaultdict(list)
for k, v in my_dict.items():
    intermediate_dict[frozenset(v.items())].append(k)

result = {tuple(v): dict(k) for k, v in intermediate_dict.items()}

输出:

{('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}

以上假设或要求排序输入,因此在所有情况下都为O(n),而排序为O(n log n)

答案 1 :(得分:3)

假设按值对字典进行排序,则可以使用itertools.groupby

{tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)}

代码

from itertools import groupby

my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
           "Q3": {1: "animal", 2: "vehicle"}, "Q4": {1: "animal", 2: "vehicle"}}

print({tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)})
# {('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}

答案 2 :(得分:0)

  

因此,我需要像Frozendict这样的东西,这在python的标准库中不可用。

您能详细说明一下吗?尽管frozendict不在语言标准中,但是您可以安装扩展名:https://pypi.org/project/frozendict/

或者,您可以将字典变成由(key, value)个项(键排序)组成的元组,以获得不可变,规范且可逆的表示形式,可用作字典键。

(请注意,如果词典中可以包含其他可变值,则可能需要递归执行此操作。)

编辑:如其他答案所指出的,或对项目使用Frozenset()。请注意,这还需要递归确保内部字典的值是不变的。

答案 3 :(得分:0)

这是同时使用frozensetgroupby的另一种方式

from operator import itemgetter
from itertools import groupby

first = itemgetter(0)
second = itemgetter(1)

my_hashes = sorted([(k, hash(frozenset(v))) for k, v in my_dict.items()], key=second)

d = dict()

for k, v in groupby(my_hashes, key=second):
    items = list(v)
    d[tuple(map(first, items))] = my_dict.get(first(first(items)))

print(d)

{('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}, ('Q1', 'Q2'): {0: 'no', 1: 'yes'}}