如何解决更新字典和检查键的最快方法
| 我正在建立一个非常长的字符串(〜1G)的字典,其中key是固定长度的k-mer,value是所有出现的位置。当k大(> 9)时,预构建k-mer词典是没有意义的,因为并非所有值都会出现并且会夸大表格。 目前,我正在执行以下任务:def hash_string(st,mersize):
stsize = len(st)
hash = {}
r = stsize-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
if mer in hash:
hash[mer].append(i)
else:
hash[mer] = [i]
return hash
# test for function hash_st above
mer3 = hash_string(\"ABCDABBBBBAAACCCCABCdddd\",3)
最耗时的步骤(我做过cProfile)是查找遇到的键(随着我们在字符串中移动),是新键还是已经存在。最快的方法是什么?
(我目前正在测试一种避免该步骤的两遍策略(对于大型序列,这要快得多),在该方法中,我首先通过简单地覆盖双精度字来构建键列表。然后,我不必检查密钥是否存在-我用这些密钥为dict播种,然后在第二遍时在遇到它们时进行附加操作。)
但是,我仍然有兴趣总结一下,在Python中查找字典键的最快方法,因为这是一种常见的模式:
如果存在密钥,则追加新条目,否则,创建密钥并添加第一个元素。
什么是此模式的最快实现?
解决方法
我会用
collections.defaultdict
:
import collections
...
hash = collections.defaultdict(list)
r = stsize-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
hash[mer].append(i)
尽管从没对它进行过分析,但它与if ... else
无关。
,通常,使用的方法将取决于您的数据。我已经构建了一些简单的测试,这些测试使用不同类型的数据来说明时序如何变化。
使用的字符串:
问题中的字符串。
较大的伪随机字符串(假定散列中具有更多不同的mers / key)。
哈希中几乎没有不同的mer / key的字符串。
这是一些测试各种方法的快速代码(我赞成the4ѭ的答案,因为它似乎是最快的)。
import random
from timeit import Timer
from collections import defaultdict
def test_check_first(st,mersize):
\"\"\" Look for the existance of the mer in the dict.
\"\"\"
mer_hash = {}
r = len(st)-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
if mer in mer_hash:
mer_hash[mer].append(i)
else:
mer_hash[mer] = [i]
return mer_hash
def test_throw_exception(st,mersize):
\"\"\" Catch the KeyError thown if a mer doesn\'t exist in the dict.
\"\"\"
mer_hash = {}
r = len(st)-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
try:
mer_hash[mer].append(i)
except KeyError:
mer_hash[mer] = [i]
return mer_hash
def test_defaultdict(st,mersize):
\"\"\" Use a defaultdict.
\"\"\"
mer_hash = defaultdict(list)
r = len(st)-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
mer_hash[mer].append(i)
return mer_hash
def test_dict_setdefault(st,mersize):
\"\"\" Use dict\'s setdefault method
\"\"\"
mer_hash = {}
r = len(st)-mersize+1
for i in range(0,r):
mer = st[i:i+mersize]
mer_hash.setdefault(mer,[]).append(i)
return mer_hash
def gen_pseudorandom_string(size):
\"\"\" Generate a larger,more \"random\" string of data.
\"\"\"
# only use four letters
letters = (\'A\',\'B\',\'C\',\'D\')
return \'\'.join(random.choice(letters) for i in range(size))
if __name__ == \'__main__\':
# test functions
test_strings = (\'ABCDABBBBBAAACCCCABCDDDD\',gen_pseudorandom_string(1000),\'A\'*100 + \'B\'*100 + \'C\'*100 + \'D\'*100)
mer_size = 3
passes = 10000
for string in test_strings:
display_string = string if len(string) <= 30 else string[:30] + \'...\'
print \'Testing with string: \"\' + display_string + \'\" and mer size: \' + str(mer_size) + \' and number of passes: \' + str(passes)
t1 = Timer(\"test_check_first(string,mer_size)\",\"from __main__ import test_check_first,string,mer_size\")
print \'\\tResults for test_check_first: \',print \"%.2f usec/pass\" % (1000000 * t1.timeit(number=passes)/passes)
t2 = Timer(\"test_throw_exception(string,\"from __main__ import test_throw_exception,mer_size\")
print \'\\tResults for test_throw_exception: \',print \"%.2f usec/pass\" % (1000000 * t2.timeit(number=passes)/passes)
t3 = Timer(\"test_defaultdict(string,\"from __main__ import test_defaultdict,mer_size\")
print \'\\tResults for test_defaultdict: \',print \"%.2f usec/pass\" % (1000000 * t3.timeit(number=passes)/passes)
t4 = Timer(\"test_dict_setdefault(string,\"from __main__ import test_dict_setdefault,mer_size\")
print \'\\tResults for test_dict_setdefault: \',print \"%.2f usec/pass\" % (1000000 * t4.timeit(number=passes)/passes)
这是在计算机上运行时得到的结果:
Testing with string: \"ABCDABBBBBAAACCCCABCDDDD\" and mer size: 3 and number of passes: 10000
Results for test_check_first: 8.70 usec/pass
Results for test_throw_exception: 22.78 usec/pass
Results for test_defaultdict: 10.61 usec/pass
Results for test_dict_setdefault: 8.88 usec/pass
Testing with string: \"BACDDDADAAABADBDADDBBBCAAABBBC...\" and mer size: 3 and number of passes: 10000
Results for test_check_first: 305.19 usec/pass
Results for test_throw_exception: 320.62 usec/pass
Results for test_defaultdict: 254.56 usec/pass
Results for test_dict_setdefault: 342.55 usec/pass
Testing with string: \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...\" and mer size: 3 and number of passes: 10000
Results for test_check_first: 114.23 usec/pass
Results for test_throw_exception: 107.96 usec/pass
Results for test_defaultdict: 94.11 usec/pass
Results for test_dict_setdefault: 125.72 usec/pass
,字典具有detdefault
方法,该方法可以执行您想要的操作,但不确定它会快多少。
因此,您的新模式可以是:
hash.setdefault(mer,[]).append(i)
,您也可以尝试基于异常的方法:
# Python2-3 compatibility
try: xrange
except NameError: xrange= range
for i in xrange(r):
mer = st[i:i+mersize]
try: hash[mer].append(i)
except KeyError: # not there
hash[mer]= [i]
请注意,如果在大多数情况下未找到ѭ10this,它将比您的方法慢,但是在大多数情况下却未找到faster10。您知道自己的数据,可以做出选择。
另外,最好不要掩盖诸如ѭ11之类的内建函数。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。