Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 基于关键字组合比较词典_Python_Algorithm_Dictionary - Fatal编程技术网

Python 基于关键字组合比较词典

Python 基于关键字组合比较词典,python,algorithm,dictionary,Python,Algorithm,Dictionary,我有一张这样的“记录”清单 data = [ {'id':1, 'name': 'A', 'price': 10, 'url': 'foo'}, {'id':2, 'name': 'A', 'price': 20, 'url': 'bar'}, {'id':3, 'name': 'A', 'price': 30, 'url': 'baz'}, {'id':4, 'name': 'A', 'price': 10, 'url': 'baz'}, {'id':5

我有一张这样的“记录”清单

data = [
    {'id':1, 'name': 'A', 'price': 10, 'url': 'foo'},
    {'id':2, 'name': 'A', 'price': 20, 'url': 'bar'},
    {'id':3, 'name': 'A', 'price': 30, 'url': 'baz'},
    {'id':4, 'name': 'A', 'price': 10, 'url': 'baz'},
    {'id':5, 'name': 'A', 'price': 20, 'url': 'bar'},
    {'id':6, 'name': 'A', 'price': 30, 'url': 'foo'},
    {'id':7, 'name': 'A', 'price': 99, 'url': 'quu'},
    {'id':8, 'name': 'B', 'price': 10, 'url': 'foo'},
]
我想删除“重复”的记录,其中等式由逻辑条件列表定义。列表中的每个元素都是OR条件,所有元素都是and组合在一起的。例如:

filters = [  ['name'],   ['price', 'url']  ]
表示如果两条记录的名称和(价格或url)相等,则认为它们相等。对于上述示例:

For item 1 the duplicates are 4 (by name and price) and 6 (name+url)
For item 2 - 5 (name+price, name+url)
For item 3 - 4 (name+url) and 6 (name+price)
For item 7 there are no duplicates (neither price nor url match)
For item 8 there are no duplicates (name doesn't match)
因此,结果列表必须包含项目1、2、3、7和8

请考虑到这一点

  • 可能还有更多的条件:
    ['name']、['price'、[url']、['weight']、['size']、…
  • 条件列表中的或组可以超过2个项目,例如
    ['name']、['price'、'url'、'weight']…
  • 源代码列表很长,一个
    O(n^2)
    算法是不可能的

避免在
O(n^2)
时间中执行此操作的方法是为您要执行的每个查询建立一个索引。一旦你有了在固定时间内查询任何值的机器,你的
O(n^2)
就会变成
O(n)
,这很平常。您也可以在
O(n)
时间中构建所有索引

假设每个值都有相同的字段,它将如下所示:

indices = defaultdict(lambda: defaultdict(set))
for i, row in enumerate(data):
    for field in 'id', 'name', 'price', 'url':
        key = row[field]
        indices[field][key].add(i)
def search_disj(factors):
    sets = (indices[field][key] for field, key in factors)
    return (data[index] for index in reduce(set.union, sets))
db.execute('CREATE INDEX id_idx ON data (id)')
现在,要搜索特定的值,只需如下:

def search(field, key):
    return (data[index] for index in indices[field][key])
要搜索一组值
ed在一起,只需分别搜索它们,然后
set.union
将它们放在一起,如下所示:

indices = defaultdict(lambda: defaultdict(set))
for i, row in enumerate(data):
    for field in 'id', 'name', 'price', 'url':
        key = row[field]
        indices[field][key].add(i)
def search_disj(factors):
    sets = (indices[field][key] for field, key in factors)
    return (data[index] for index in reduce(set.union, sets))
db.execute('CREATE INDEX id_idx ON data (id)')
要同时搜索一组析取
ed,请对每一个析取执行相同的操作,然后将所有结果一起设置

根据您的数据,只需查找第一个索引,然后线性搜索其他因素的结果,效率可能会更高。您可以通过对字段重新排序来进一步优化,以便首先搜索具有最小
len(索引[field])
的字段。(或者,在本例中,对于disj中的字段,具有最小和(len(索引[field]))的字段)`。)

如果你可以任意嵌套-…的连词的析取的连词,直到你深入到单个元素,你只需要让函数递归地调用其他任何一个元素(对于平面元素有一个基本情况)。您甚至可以将其扩展到完全通用的布尔搜索(尽管您还需要一个
not
操作-
universe-index[field][key]
,其中
universe=set(range(len(data)))
-用于此操作)


如果数据非常大,您可能无法在内存中存储所有索引

或者,即使你可以将所有的索引存储在内存中,缓存甚至页面缺失都会使哈希表变得不理想,在这种情况下,你可能会想基于B-树(例如, BLIST.SCODTECT/<代码>)而不是DICT。等等。缺点是所有这些

n
时间都变为
n log n
,但是如果您需要该功能,或者如果您获得两个数量级的局部性好处,以换取
log(n,base)
成本(结果只有7),那么它是值得的

或者,也可以使用某种类似于dict的磁盘备份存储,比如
anydbm


然而,实际上,您正在构建的是一个只有一个关系(表)的关系数据库。在许多情况下,您最好只使用现成的关系数据库,如Python内置的
sqlite3
。然后,构建索引的代码如下所示:

indices = defaultdict(lambda: defaultdict(set))
for i, row in enumerate(data):
    for field in 'id', 'name', 'price', 'url':
        key = row[field]
        indices[field][key].add(i)
def search_disj(factors):
    sets = (indices[field][key] for field, key in factors)
    return (data[index] for index in reduce(set.union, sets))
db.execute('CREATE INDEX id_idx ON data (id)')
…您只需执行查询,它们就可以以最佳方式神奇地使用正确的索引:

curs = db.execute('SELECT * FROM data WHERE name = ? AND (price = ? OR url = ?)', 
                  filters)

基于Tim Pietzcker的想法,以下内容适用于我:

我们首先将CNF条件(如
a&(b&c)
转换为DNF:
(a&b)|(a&c)
)。使用问题中的列表符号,即
[[a],[b,c]
,DNF将
[[a,b],[a,c]]
。在python中,这就像
itertools.product(*filters)
一样简单

然后我们迭代列表,并为DNF中的每个连接创建一个复合键:

( (a, rec[a]), (b, rec[b]) )
并检查是否已经看到任何钥匙。如果不是,我们认为该记录是唯一的,并将其密钥添加到<代码>参见 SET:

守则:

seen = set()
dnf = list(itertools.product(*filters))

for item in data:
    keys = set(
        tuple((field, item.get(field, None)) for field in conjunct) 
        for conjunct in dnf)
    if keys.isdisjoint(seen):
        seen |= keys
        print item # unique

蒂姆给了我一个主意,真是太好了。如果有人发现这个解决方案有什么问题,请告诉我。

如果你有很多问题,你应该认真考虑使用诸如<代码> SqLye3之类的东西来代替字典。那么问题在哪里呢?你已经设计好了数据。你知道函数应该做什么。问题在哪里?这听起来更像是请为我编写代码。在SQL语句中应该交换
吗?@TimPietzcker:是的,你是对的;我猜想他想要连词的析取,因为这是人们几乎总是想要的,但仔细阅读,他没有。Python索引也有同样的问题。谢谢我会解决的。问题变得更复杂了(几个“和”条件以及几个“或”)。我将撤回我的回答。我没有时间(该睡觉了!)来检查你的答案在这些情况下是否也有效(它看起来肯定比我的更具扩展性,尤其是数据库驱动的那一个)…@TimPietzcker:我认为它只是一堆“and”,每个“and”都是一堆“or”,在这种情况下它仍然有效。如果你更一般(一堆“and”和“s”,每一个都是一堆”或“s”,每一个都可能是一堆“and”和“s”,等等),你只需要从连接函数中递归调用析取函数,反之亦然(在唱校舍摇滚歌曲时)。如果OP要求的话,我可以显示出来。+1“但是,实际上,您正在构建的是一个关系数据库”。