【问题标题】:Merge dictionaries with key combinations使用组合键合并字典
【发布时间】:2014-09-23 10:36:58
【问题描述】:

我有两个字典 A 和 B,它们都有相同的键 a、b 和值。这些键后面的所有 3 个值都是相同大小的 numpy 数组,但 A 和 B 的大小可能不同。 如果找到此链接here,但它仅适用于一维键: 可以将 a(0),b(0) 组合视为笛卡尔空间中的坐标,将 value(0) 视为它们的值。我有两个数据集 A 和 B。 举个例子:

A = {'a': numpy.array([1, 1, 9, 9]),
     'b': numpy.array([0, 1, 0, 1]),
     'value': numpy.array([1, 2, 3, 4])}
B  = {'a': numpy.array([1, 1, 7, 7]),
     'b': numpy.array([0, 1, 0, 1]),
     'value': numpy.array([101, 102, 1003, 1004])}

如果两个键相同,我需要对这些字典的值求和,否则我想附加键和值。 在示例中: 两个字典共享组合键 a:1 和 b:0,以及 a:1 和 b:1。它们的值相加为 1+101=102 和 2+102=104。 组合键 a:9, b:0 和 a:9, b:1 仅在字典 A 中 组合键 a:7, b:0 和 a:7, b:1 仅在字典 B 中 所以我想要这个结果

C = {'a': numpy.array([1, 1, 9, 9, 7, 7]),
     'b': numpy.array([0, 1, 0, 1, 0, 1]),
     'value': numpy.array([102, 104, 3, 4, 1003, 1004 ])}

我想出了一个解决方案,它采用字典 A 并通过添加或附加字典 B 中的内容来修改它。 因此,它首先为 A 中的二维键组合生成一维哈希键,为 B 中的二维键组合生成一个。然后使用 numpy.intersect() 查找两个字典中的公共键,并将 B 的值添加到A 在那个指数。然后我取交集的反转并将不常见的键和值都附加到字典 A.

def example(A, B):
    # generate hash keys (32 bit shift because values in a and b are larger than in example)
    hash_A = map(lambda a, b: (int(a) << 32) + int(b), A['a'], A['b'])
    hash_B = map(lambda a, b: (int(a) << 32) + int(b), B['a'], B['b'])

    # intersection is now 1-dimensional and easy
    intersect = numpy.intersect1d(hash_A, hash_B)

    # common keys
    A['value'][numpy.in1d(hash_A, intersect)] += B['value'][numpy.in1d(hash_B, intersect)]

    # keys only in B and not in A
    only_in_B = numpy.in1d(hash_B, intersect, invert=True)
    if any(only_in_B):
        A['a'] = numpy.append(A['a'], B['a'][only_in_B])
        A['value'] = numpy.append(A['value'], B['value'][only_in_B])
        A['b'] = numpy.append(A['b'], B['b'][only_in_B])

    return A

但我的解决方案似乎太慢而无用,我想不出更快的方法。 使用的 numpy.arrays 有数百万个条目,这是针对几种字典组合完成的。速度是个问题。 任何帮助将不胜感激。

【问题讨论】:

    标签: python numpy dictionary hash merge


    【解决方案1】:

    我会首先将数据结构更改为:

    valuesA = {(A['a'][x], A['b'][x]): A['value'][x] for x in range(len(A['a']))}
    

    这应该给你:

    {(1, 0): 1, (9, 0): 3, (1, 1): 2, (9, 1): 4}
    

    B 也一样:

    valuesB = {(B['a'][x], B['b'][x]): B['value'][x] for x in range(len(B['a']))}
    # {(1, 0): 101, (7, 0): 1003, (1, 1): 102, (7, 1): 1004}
    

    然后将valuesA合并到valuesB中:

    for key, value in valuesA.items():
        valuesB[key] = valuesB.get(key, 0) + value
    

    结果是:

    {(9, 0): 3, (7, 0): 1003, (9, 1): 4, (7, 1): 1004, (1, 0): 102, (1, 1): 104}
    

    如果你真的需要,你可以把它放回原来的样子:

    keys = valuesB.keys()
    C = {'a': [x[0] for x in keys], 'b': [x[1] for x in keys], 'value': [valuesB[x] for x in keys]}
    

    结果是

    {'a': [9, 7, 9, 7, 1, 1], 
     'b': [0, 0, 1, 1, 0, 1], 
     'value': [3, 1003, 4, 1004, 102, 104]}
    

    注意:如果顺序确实很重要,您可以考虑使用OrderedDict 而不是普通的字典,这是一个保留插入顺序的字典。

    【讨论】:

    • 顺序无关紧要。不知道为什么我不认为元组是键。我会测试你的答案的速度。
    • 因此,如果我将您的代码放入一个方法中,我得到的时间大致相同,只是稍微好一点。但是,如果我将 A 的数据以该格式存储并因此在方法内转换 B 的值并省略将结果重新格式化回原始形式,我大约会快 30%。很好,如果没有其他建议,我最终会接受您的回答。
    • 一项优化是确保您在 [valuesA, valuesB] 中的最小值上进行循环合并
    • 我默认这样做。我意识到当我编写这个简单的例子时,我把它做得太简单了:实际上我不是只为一个键“值”做这个,但我还有两个键(让我们称它们为“y”和“z”) ,我想以与键“值”相同的方式总结值。在我的解决方案中,我可以再使用 2 个键,在其中添加值,在您的解决方案中,我很难这样做。有什么建议吗?
    • 抱歉,我的评论有点为时过早。当然,我只是使用相同的元组作为键,值是一个包含三个值的 numpy 数组。一切正常!
    猜你喜欢
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    • 2017-12-16
    • 2018-10-04
    • 1970-01-01
    • 2018-09-18
    相关资源
    最近更新 更多