寻找一种有效的方法来搜索Python(3.6+)列表中的部分字符串。
我有两个列表。 listA是路径名+唯一文件名的字符串列表:
['/pathname/uniquestring.ext', '/pathname/uniquestring.ext', '/pathname/uniquestring.ext' ...]
(使用glob()创建,文件名已全部给出并且已经存在)
listB是词典列表。每个字典具有相同的键集,但具有唯一的值。
[{key1:value1, key2:value2}, {key1:value3, key2:value4}, ...]
(也已经给出)
listB中每个字典中的一个key:value对将具有一个 listA中一个唯一项包含的值。
但是,值在listA的每个项目中出现的位置是不确定的。
我想要的是:对于listB中的每个项目,在listA中找到包含与dict中的k:v对匹配的子字符串的项目, 并创建一个新的字典(或元组列表)作为“查找表”(目的是更正一组图像文件中损坏的exif创建日期)。
示例:
listA = ['/pathname/abdce_654321.ext', '/pathname/a3b4c5_123456.ext', '/pathname/cbeebie_645321_abcde.ext', ...]
listB = [{"id": "123456", "create_date": "23/05/2014"}, ...]
new_dict = {"/pathname/a3b4c5_123456.ext": "23/05/2014, ...}
我完全可以从dict补偿中获得想要的东西,如下所示:
{j:i['create_date'] for j in listA for i in listB if i['id'] in j}
但是,即使对于很小的文件(约5500个项目),这在我(相当老)的笔记本电脑上也要花费12s。
大概是因为我必须使用我的方法遍历整个listB〜5500次。
在Python中有更有效的方法吗?
(nb我不是在寻求有关如何使用python纠正exif数据的建议;这是有关列表中字符串查找的一般化问题)
更正和澄清
谢谢大家到目前为止的关注。
答案 0 :(得分:2)
我可以看到两条评论的内容。最大的问题是:是否需要使用in
,因为仅当我们不知道id在路径字符串中的位置时才需要这样做吗?如果它总是在特定的地方,我们可以提取它并使用恒定时间查找:
def extract_id(path):
# todo
ids = {item['id']: item['create_date'] for item in listB}
new_dict = {path: ids[extract_id(path)] for path in listA}
只有O(N)
,而不是您当前的O(N**2)
。
答案 1 :(得分:0)
首先,这是有助于测试的通用列表:
listA = ['/pathname/abdce_%s.ext' % str(x) for x in range(10000)]
listB = [{'id': str(number), "create_date": "23/05/2014"} for number in range(10000)]
hello = {j: i['create_date'] for j in listA for i in listB if i['id'] in j}
以10000个值运行时,我的机器平均花费8.8秒。 (如果我之后打印字典,则为9.5秒)
现在,如果我们将代码编译为Cython(在C上运行的python超集),那么我的时间将减少到4.4秒。
请参见下面的代码
cpdef dict main():
cdef int x
cdef int number
cdef char j
cdef dict i
listA = ['/pathname/abdce_%s.ext' % str(x) for x in range(10000)]
listB = [{'id': str(number), "create_date": "23/05/2014"} for number in range(10000)]
hello = {j: i['create_date'] for j in listA for i in listB if i['id'] in j}
return hello
答案 2 :(得分:0)
我写了一个小测试台,它会生成类似于您的随机数据,并尝试使用您的原始词典理解功能,以及一个具有优化功能的版本,例如在找到匹配项时提前退出并删除使用过的标签。
match
(您的原件)和match2
(我的)都打印出结果数量,以尝试确保它们等效。
结果颇具说服力...希望能有所帮助。
我的MBP上5000/10000个项目的编号:
import random
import timeit
import string
random.seed(42)
def genrand(n):
return "".join(
random.choice(string.ascii_lowercase + string.digits) for x in range(n)
)
filenames = []
tags = []
for x in range(5000):
id = genrand(8)
filenames.append("/pathname/%s_%s.ext" % (genrand(6), id))
if random.random() < 0.95:
tags.append({"id": id, "date": "date for %s" % id})
def match():
x = {j: i["date"] for j in filenames for i in tags if i["id"] in j}
print(len(x))
def match2():
x = {}
available_tags = tags[:]
for filename in filenames:
for tag in available_tags:
if tag["id"] in filename:
x[filename] = tag
available_tags.remove(tag) # we've used this tag, remove it
break
print(len(x))
print(timeit.timeit(match, number=1))
print(timeit.timeit(match2, number=1))