使用列表作为值

时间:2017-12-27 06:55:06

标签: python dictionary group-by itertools

我正在寻找一种Pythonic方式或更有效的方法来解决这个问题。我有一个字典,其中设置为值(跨键允许重复)。给定一个列表,我必须创建一个字典,使用主字典中的键将每个类别映射到元素。我举一个例子来说明。

大师词典

{
    "KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
    "KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
    "KeyZ": ['Random Value', 'Foo', 'Bar']
}

输入

['Foo', 'Bar', 'Dog', 'Aron']

输出

{
    "KeyA": ['Aron'],
    "KeyB": ['Bar', 'Foo', 'Dog'],
    "KeyZ": ['Foo', 'Bar']
}

我目前的想法

将组中的各个项目反转为键,然后进行查找。

{
     'Aron'         : ['KeyA'],
     'Foo'          : ['KeyB', 'KeyZ'],
     'Bar'          : ['KeyB', 'KeyZ'],
     'Random Value' : ['KeyA', 'KeyZ']
}

我通过浏览每一组中的每一项来初始化倒置字典。创建这样的字典的大约时间是O(n)。在如此创建的反向字典中查找列表中的项目。说出值Bar。使用信息'Bar': ['KeyB', 'KeyZ']创建新词典。结果字典为{'KeyB': ['Bar'], 'KeyZ': ['Bar']}。对于下一个项目,我必须对现有字典进行一些记账,如密钥是否存在,如果是,则附加到现有列表,依此类推。

使用映射的集合中的in运算符(检查成员资格)到每个键

主词典和输入列表在大多数时候都会非常小。 (所有套装中的独特物品少于500件)。所以我可以检查每个键返回的集合的成员资格并创建一个字典。这显然效率较低,但适用于大多数情况。

我还有一些类似于上面给出的例子的操作。我不想为所有人进行手动记账,因为他们容易出错并且比内置功能慢。

我需要什么?

  • 更好的方法(更快的算法)
  • itertools 中的内置函数,因为它们更快
  • 第三方图书馆
  • 普通Python用户不会想到的一些深奥的理解?

5 个答案:

答案 0 :(得分:5)

在开始转换之前将列表转换为集合怎么样?集合查找比列表中的线性搜索更快。

input_set = set(input)

一旦你拥有它,你可以使用常规字典理解,在我看来:

output = {key: [x for x in value if x in input_set] for key, value in master_dict.items()}

结果:

output == {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']}

答案 1 :(得分:4)

一种方法是在python中使用cross,如下所示:

x={
    "KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
    "KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
    "KeyZ": ['Random Value', 'Foo', 'Bar']
   }
items = ['Foo', 'Bar', 'Dog', 'Aron']

{k:  set(items).intersection(set(v)) for k, v in x.items()}

答案 2 :(得分:1)

使用defaultdict和list comprehension。

from collections import defaultdict

result = defaultdict(list)

d = {
    "KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
    "KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
    "KeyZ": ['Random Value', 'Foo', 'Bar']
   }
items = ['Foo', 'Bar', 'Dog', 'Aron']

[result[k].append(e) for k,v in d.items() for e in v if e in items]

print(result) # defaultdict(<type 'list'>, {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']})

print(dict(result)) # {'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyA': ['Aron'], 'KeyZ': ['Foo', 'Bar']}

答案 3 :(得分:1)

另一种可能的方法:

可能加快检查input_set中是否存在值的搜索时间的一种方法是使用二进制搜索,即O(logn)

以下是一些示例代码,它也使用了方便的collections.defaultdict

from collections import defaultdict

master = {
          "KeyA": ['Aron', 'Ranom Value', 'Abhishek'],
          "KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
          "KeyZ": ['Random Value', 'Foo', 'Bar']
         }    

input_set = ['Foo', 'Bar', 'Dog', 'Aron']

sorted_list = sorted(input_set)

d = defaultdict(list)
for key, value in master.items():
    for v in value:
        if binary_search(sorted_list, v):
            d[key].append(v)

print(d)

哪个输出:

defaultdict(<class 'list'>, {'KeyA': ['Aron'], 'KeyB': ['Foo', 'Bar', 'Dog'], 'KeyZ': ['Foo', 'Bar']})

下面定义了binary_search()

def binary_search(item_list,item):
    first = 0
    last = len(item_list)-1

    while first <= last:
        mid = (first + last)//2
        if item_list[mid] == item :
            return True
        elif item < item_list[mid]:
            last = mid - 1
        else:
            first = mid + 1 
    return False

上面的代码似乎重新发明了轮子。您可以查看bisect模块,它提供了一些调用二进制搜索的方法,而无需编写自己的函数。

注意:为了使用二进制搜索,您还需要预先对值进行排序,即O(nlogn)。我不完全确定这将产生多大的影响,你必须用另一种方法进行一些测试来看看差异。

此外,正如@SuperSaiyan发布的那样,将input_set转换为集合是最有效的方法,因为在最好的情况下设置查找O(1),在最坏的情况下设置O(n)(罕见) )。

答案 4 :(得分:1)

OP提议reverse dictionary。它可以说仍然是pythonic,所以这里是如何实现的。

<强>鉴于

import collections as ct


master_dict = {
    "KeyA": ['Aron', 'Random Value', 'Abhishek'],
    "KeyB": ['Ball', 'Foo', 'Bar', 'Badge', 'Dog'],
    "KeyZ": ['Random Value', 'Foo', 'Bar']
}

input_list = ['Foo', 'Bar', 'Dog', 'Aron']

<强>代码

我们使用collections.defaultdict来简化列表值的创建。

reverse_dict = ct.defaultdict(list)
for k, v in master_dict.items():
    for item in v:
        reverse_dict[item].append(k)
reverse_dict

输出

defaultdict(list,
            {'Abhishek': ['KeyA'],
             'Aron': ['KeyA'],
             'Badge': ['KeyB'],
             'Ball': ['KeyB'],
             'Bar': ['KeyB', 'KeyZ'],
             'Dog': ['KeyB'],
             'Foo': ['KeyB', 'KeyZ'],
             'Random Value': ['KeyA', 'KeyZ']})

现在可以通过键搜索输入,查找比搜索每个字符串列表更快。我们从查找值的输入列表构建最终字典。

final_dict = ct.defaultdict(list)
for v in input_list:
    for k in reverse_dict[v]:
        final_dict[k].append(v)

final_dict

输出

defaultdict(list,
            {'KeyA': ['Aron'],
             'KeyB': ['Foo', 'Bar', 'Dog'],
             'KeyZ': ['Foo', 'Bar']})

@SuperSaiyan建议通过搜索输入列表的来重建主词典的每个键的列表。对于这种特殊应用,这是一种出色而卓越的方法。