Python 字符串上的单交换
我需要找到一种更快的方法,以以下方式在8-11字符串中查找交换: 给定字符串“stdilgnley”,查找字母的所有单字母交换:Python 字符串上的单交换,python,string,algorithm,bioinformatics,Python,String,Algorithm,Bioinformatics,我需要找到一种更快的方法,以以下方式在8-11字符串中查找交换: 给定字符串“stdilgnley”,查找字母的所有单字母交换: list_AA = ['A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I', 'L', 'K', 'M', 'F', 'P', 'S', 'T', 'W', 'Y', 'V'] i、 e,对于字符串中的每个字母,用列表中的一个替换原始字符串中的每个字母。产出将是: atdilgnley RTDILGN
list_AA = ['A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I', 'L', 'K', 'M',
'F', 'P', 'S', 'T', 'W', 'Y', 'V']
i、 e,对于字符串中的每个字母,用列表中的一个替换原始字符串中的每个字母。产出将是:
atdilgnley
RTDILGNLYE
恩蒂尔格尼
...
萨迪尔格尼
斯迪尔格尼
斯迪尔格尼
...
...
斯特迪尔格尼夫
总共200个新串(串中每个位置20个交换)。
到目前为止,我所拥有的:
def _create_swaps(original_str):
list_peps = []
for i in range(len(original_str)):
for k in range(len(list_AA)):
list_peps.append(_insert_aa(original_str, i, list_aa[k]))
#remove original string
return [i for i in list_peps if i != original_str]
def _insert_aa(string, index, aa):
list_string_elements = list(string)
del list_string_elements[index]
hash_string.insert(index, aa)
return "".join(hash_string)
由于这需要重复10**6次,这是大型项目中最慢的步骤。是否有一种更快地找到此类掉期的方法(通过消除”。加入、插入、步骤/通过动态查找掉期)
供参考:
ncalls tottime percall cumtime percall文件名:lineno(函数)
185275200 330.286 0.000 429.295 0.000型号。py:233(_insert_aa)
975240 147.322 0.000 616.979 0.001型号。py:225(_创建_交换)
185280201/185280197 59.137 0.000 59.138 0.000{“str”对象的方法“连接”}
185275208 39.8750.000 39.8750.000{“列表”对象的“插入”方法}
975240 21.027 0.000 21.027 0.000型号。py:231()
186746064 18.516 0.000 18.516 0.000{“列表”对象的“附加”方法}
这应该更快:
def _insert_aa(string, index, aa):
return string[0:index] + aa + string[index+1:]
编辑:只能对头部和尾部进行一次切片,并按如下方式重复使用:
def generate_all_variants(string, replacements):
for i in range(len(string)):
head = string[:i]
tail = string[i+1:]
for letter in replacements:
yield head + letter + tail
for variant in generate_all_variants("abcd", ['1', '2', '3']):
print(variant)
这是一个更清晰的版本,你正在寻找的,即使你已经选择了一个答案(它不是最pythonic)
您永远不应该使用range来获取iterable的索引,如果您想对其进行pythonic处理,应该使用enumerate
>>> def swaps(s, lst):
... for index, _ in enumerate(s):
... for letter in lst:
... temp = list(s)
... temp[index] = letter
... yield ''.join(temp)
...
>>> list_AA = ['A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I', 'L', 'K', 'M', 'F', 'P', 'S', 'T', 'W', 'Y', 'V']
>>> s = 'STDILGNLYE'
>>>
>>> for _ in swaps(s, list_AA):
... print _
...
ATDILGNLYE
RTDILGNLYE
NTDILGNLYE
..........
GTDILGNLYE
HTDILGNLYE
ITDILGNLYE
另外,python3中的一种简化方法:
>>> def swaps(s, lst):
... for i, _ in enumerate(s):
... yield from ['%s%s%s' % (s[:i], x, s[i+1:]) for x in lst]
...
>>> swaps(s,list_AA)
<generator object swaps at 0x10c9205c8>
>>> a=_
>>> next(a)
'ATDILGNLYE'
>>> next(a)
'RTDILGNLYE'
>>> next(a)
'NTDILGNLYE'
>>> next(a)
'DTDILGNLYE'
以下是对这三个方面的基准测试:
s='STDILGNLYE'
list_AA=['A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I', 'L', 'K', 'M', 'F',
'P', 'S', 'T', 'W', 'Y', 'V']
# the correct sample size
list_new = list_AA * (10**6 // len(list_AA))
def swaps0(string, replacements):
for i in range(len(string)):
head = string[:i]
tail = string[i+1:]
for letter in replacements:
yield head + letter + tail
def swaps1(s, lst):
for i, _ in enumerate(s):
yield from ['%s%s%s' % (s[:i], x, s[i+1:]) for x in lst]
def swaps2(s, lst):
for index, _ in enumerate(s):
for letter in lst:
temp = list(s)
temp[index] = letter
yield ''.join(temp)
timeit [_ for _ in swaps0(s, list_new)]
timeit [_ for _ in swaps1(s, list_new)]
timeit [_ for _ in swaps2(s, list_new)]
In [9]: timeit [_ for _ in swaps0(s, list_new)]
1 loop, best of 3: 2.61 s per loop
In [10]: timeit [_ for _ in swaps1(s, list_new)]
1 loop, best of 3: 6.57 s per loop
In [11]: timeit [_ for _ in swaps2(s, list_new)]
1 loop, best of 3: 8.61 s per loop
值得吗?我想说,这取决于您希望这个样本大小增长多少,以及您运行代码的频率
如果代码不会频繁运行(比如,每小时数百次),并且样本量不会呈指数增长(达到1050或10100),那么我会说要提高可读性
如果要随着样本量的增加而频繁地计算,那么就考虑性能
最后,我们剩下一个折衷的解决方案,将枚举与头/尾拆分相结合:
def swap3(s, lst):
for i, _ in enumerate(s):
head, tail = s[:i], s[i+1:]
yield from ['%s%s%s'%(head,c,tail) for c in lst]
In [16]: timeit [_ for _ in swap3(s, list_new)]
1 loop, best of 3: 3.99 s per loop
你需要发出所有生成的字符串,还是只需要数一数?@Steve我需要所有的字符串。正如您从\u create\u swaps
的返回调用中所看到的,它返回除原始字符串之外的所有已创建字符串。您可能想尝试找出一种方法,用映射()替换其中一个操作。
…请参见循环效率…当然,分析总是比理论上的好。
“”.join
总是比使用+
进行连接更快。您的编辑似乎是我正在寻找的解决方案。不过,为什么要坚持使用+
而不是”。join
?join函数只使用一个参数,通常是一个列表,但创建列表需要时间。@Carlo”。join
并不总是更快。对于短字符串,尤其是长度小于10的字符串,由于产生的开销,它的速度通常较慢。但是,切片和连接速度更快。timeit变量=[v代表v在生成所有变量(s,列表AA)]10000个循环,每个循环的最佳值为3:34.3µs timeit变量=[v代表v在交换(s,列表AA)]1000个循环,每个循环的最佳值为3:271µsloop@steve我用一种更简单的方法更新了我的答案,使用了Python3Also,python的“禅”就是这么简单,可读代码比有微优化的丑陋代码要好。优化只是一个微观优化。您需要替换大量字符才能显著提高速度。@CharlesAddis事实上,对于这一特定目的,最好使用微优化,因为我将对大量字符串执行交换,正如我所指出的。这是我代码中最大的瓶颈。不管怎样,谢谢你的输入,我从你的答案中学到了一两件事。值得一提的是,将其转换为C可以提高速度。您可以通过使用Cython获得一些重要的优化。另一方面,在我的一个高级项目中,将图形操作从Python转换为C可以减少约6个数量级的处理时间,因为在C中,您可以更有效地处理图形,而无需所有开销。(>30小时vs
def swap3(s, lst):
for i, _ in enumerate(s):
head, tail = s[:i], s[i+1:]
yield from ['%s%s%s'%(head,c,tail) for c in lst]
In [16]: timeit [_ for _ in swap3(s, list_new)]
1 loop, best of 3: 3.99 s per loop