【问题标题】:Most efficient way to build a new dict from a list of dicts with 2 common keys?从具有 2 个公共键的字典列表中构建新字典的最有效方法?
【发布时间】:2020-05-19 02:30:13
【问题描述】:

我试图找出从 dicts 列表中构建新 dict 的最佳方法,列表中的每个 dict 都有 2 个公共键,一个公共键值将是新 dict 的键和其他公共键值将是新字典中的值列表中的一个元素。

我设法提供了 4 种不同的解决方案,这是我的示例:

list_of_dicts = [
    {'key_1': 'v2', 'key_2': 'some data 1', 'key_3': 'some random data'},
    {'key_1': 'v1', 'key_2': 'some data 2'},
    {'key_1': 'v1', 'key_2': 'some data 1'},
    {'key_1': 'v2', 'key_2': 'some data 2'}]

使用 collections.defaultdict 的解决方案 1:

from collections import defaultdict

group_by_key_1 = defaultdict(list)


for d in list_of_dicts:
    group_by_key_1[d['key_1']].append(d['key_2'])

group_by_key_1

输出 1:

defaultdict(list,
            {'v2': ['some data 1', 'some data 2'],
             'v1': ['some data 2', 'some data 1']})

使用 dict.setdefault 的解决方案 2:

group_by_key_1 = {}
for d in list_of_dicts:
    group_by_key_1.setdefault(d['key_1'], []).append(d['key_2'])

group_by_key_1

输出 2:

{'v2': ['some data 1', 'some data 2'], 'v1': ['some data 2', 'some data 1']}

解决方案3,如果有任何元素,则追加或添加带有第一个元素的列表:

group_by_key_1 = {}
for d in list_of_dicts:
    if d['key_1'] not in group_by_key_1:
        group_by_key_1[d['key_1']] = [d['key_2']]
    else:
        group_by_key_1[d['key_1']].append(d['key_2'])

group_by_key_1

输出 3:

{'v2': ['some data 1', 'some data 2'], 'v1': ['some data 2', 'some data 1']}

解决方案 4,使用 itertools.groupby

from itertools import groupby
from operator import itemgetter

list_of_dicts.sort(key=itemgetter('key_1'))
group = groupby(list_of_dicts, key=itemgetter('key_1'))
group_by_key_1 = dict((k, [e['key_2'] for e in v]) for k, v in group)
group_by_key_1

输出 4:

{'v1': ['some data 2', 'some data 1'], 'v2': ['some data 1', 'some data 2']}

通常,我使用解决方案 1,但解决方案 2 和 3 似乎也可以,但是,什么解决方案是最有效的方法?或者也许还有其他最好的解决方案?

我想对几百万个 list_of_dicts 使用上述解决方案之一,而 list_of_dicts 中的一个 dict 可以有 10 到 1000 个键。

【问题讨论】:

  • 第一个 collections.defaultdict 对我来说似乎是最干净、最易读的
  • 这不是 itertools.groupby 的用途吗? itertools 是为高效的通用迭代技术/范式而创建的,您所做的事情仅通过名称 groupby 来解释。在这些情况下,这是我的首选。也就是说,我确实相信这个问题对答案留下了很多意见,因此可能不适合本网站。
  • #1 也可能是最快的。也是常见的不公社。
  • @Jab,这需要一种添加不必要的 O(nlogn) 的排序。
  • 对于解决方案 4,您可以使用字典理解:{k: e['key_2'] for e in v for k, v in group}

标签: python list dictionary


【解决方案1】:

如果解决方案的 list_of_dicts 大小在 10 到 100_000 之间进行基准测试,那么 1 号解决方案显示效率最高:

from collections import defaultdict
from itertools import groupby
from operator import itemgetter
from simple_benchmark import BenchmarkBuilder
from random import randrange

b = BenchmarkBuilder()

@b.add_function()
def sol_1(list_of_dicts):

    group_by_key_1 = defaultdict(list)
    for d in list_of_dicts:
        group_by_key_1[d['key_1']].append(d['key_2'])

    return group_by_key_1

@b.add_function()
def sol_2(list_of_dicts):
    group_by_key_1 = {}
    for d in list_of_dicts:
        group_by_key_1.setdefault(d['key_1'], []).append(d['key_2'])

    return group_by_key_1

@b.add_function()
def sol_3(list_of_dicts):
    group_by_key_1 = {}
    for d in list_of_dicts:
        if d['key_1'] not in group_by_key_1:
            group_by_key_1[d['key_1']] = [d['key_2']]
        else:
            group_by_key_1[d['key_1']].append(d['key_2'])

    return group_by_key_1

@b.add_function()
def sol_4(list_of_dicts):
    list_of_dicts.sort(key=itemgetter('key_1'))
    group = groupby(list_of_dicts, key=itemgetter('key_1'))
    group_by_key_1 = dict((k, [e['key_2'] for e in v]) for k, v in group)
    return group_by_key_1


@b.add_arguments('Size of list, number of keys')
def argument_provider():
    for exp in range(2, 5):
        size = 10**exp
        keys_count = 1000
        list_of_dicts = [{f'key_{i}': f'v{i}' for i in range(keys_count)} for _ in range(size)]
        yield size,  list_of_dicts

r = b.run()
r.plot()

输出:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多