给定切片列表,如何基于它们分离序列?
我有很长的氨基酸字符串,我想根据列表中的起止值进行拆分。一个例子可能是解释它的最明确的方式:
str = "MSEPAGDVRQNPCGSKAC"
split_points = [[1,3], [7,10], [12,13]]
output >> ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']
额外的括号是显示从split_points列表中选择的元素。我不认为开始 - 停止点会重叠。
我有一堆可行的想法,但看起来非常低效(代码长度明智),似乎必须有一个很好的pythonic方式来做到这一点。
你有分裂字符串的奇怪方法:
def splitter( s, points ):
c = 0
for x,y in points:
yield s[c:x]
yield "(%s)" % s[x:y+1]
c=y+1
yield s[c:]
print list(splitter(str, split_points))
# => ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']
# if some start and endpoints are the same remove empty strings.
print list(x for x in splitter(str, split_points) if x != '')
这是一个简单的解决方案。抓取该点指定的每个集合。
In[4]: str[p[0]:p[1]+1] for p in split_points]
Out[4]: ['SEP', 'VRQN', 'CG']
要获得括号:
In[5]: ['(' + str[p[0]:p[1]+1] + ')' for p in split_points]
Out[5]: ['(SEP)', '(VRQN)', '(CG)']
这是完成交易的更干净的方式:
results = []
for i in range(len(split_points)):
start, stop = split_points[i]
stop += 1
last_stop = split_points[i-1][1] + 1 if i > 0 else 0
results.append(string[last_stop:start])
results.append('(' + string[start:stop] + ')')
results.append(string[split_points[-1][1]+1:])
以下所有解决方案都很糟糕,而且比其他任何方式更有趣,请不要使用它们!
这更像是一个WTF解决方案,但我想我会发布它,因为它在评论中被要求:
split_points = [(x, y+1) for x, y in split_points]
split_points = [((split_points[i-1][1] if i > 0 else 0, p[0]), p) for i, p in zip(range(len(split_points)), split_points)]
results = [string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in split_points] + [string[split_points[-1][1][1]:]]
results = '\n'.join(results).split()
还在试图找出一个班轮,这里有两个:
split_points = [((split_points[i-1][1]+1 if i > 0 else 0, p[0]), (p[0], p[1]+1)) for i, p in zip(range(len(split_points)), split_points)]
print '\n'.join([string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in split_points] + [string[split_points[-1][1][1]:]]).split()
并且永远不应该使用的一个班轮:
print '\n'.join([string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in (((split_points[i-1][1]+1 if i > 0 else 0, p[0]), (p[0], p[1]+1)) for i, p in zip(range(len(split_points)), split_points))] + [string[split_points[-1][1]:]]).split()
这里有一些可行的代码。
result = []
last_end = 0
for sp in split_points:
result.append(str[last_end:sp[0]])
result.append('(' + str[sp[0]:sp[1]+1] + ')')
last_end = sp[1]+1
result.append(str[last_end:])
print result
如果您只想要括号中的部分,它会变得更简单:
result = [str[sp[0]:sp[1]+1] for sp in split_points]
这是一个将split_points转换为常规字符串切片然后打印出适当切片的解决方案:
str = "MSEPAGDVRQNPCGSKAC"
split_points = [[1, 3], [7, 10], [12, 13]]
adjust = [s for sp in [[x, y + 1] for x, y in split_points] for s in sp]
zipped = zip([None] + adjust, adjust + [None])
out = [('(%s)' if i % 2 else '%s') % str[x:y] for i, (x, y) in
enumerate(zipped)]
print out
>>> ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']
>>> str =“MSEPAGDVRQNPCGSKAC”
>>> split_points = [[1,3],[7,10],[12,13]]
>>>
>>> all_points = sum(split_points,[0])+ [len(str)-1]
>>> map(lambda i,j:str [i:j + 1],all_points [: - 1],all_points [1:])
['MS','SEP','PAGDV','VRQN','NPC','CG','GSKAC']
>>>
>>> str_out = map(lambda i,j:str [i:j + 1],all_points [: - 1:2],all_points [1 :: 2])
>>> str_in = map(lambda i,j:str [i:j + 1],all_points [1:-1:2],all_points [2 :: 2])
>>> sum(map(list,zip(['(%s)'%s for s in str_in],str_out [1:])),[str_out [0]])
['MS','(SEP)','PAGDV','(VRQN)','NPC','(CG)','GSKAC']
可能不是为了优雅,而是因为我可以在一个oneliner :)
>>> reduce(lambda a,ij:a[:-1]+[str[a[-1]:ij[0]],'('+str[ij[0]:ij[1]+1]+')',
ij[1]], split_points, [0])[:-1] + [str[split_points[-1][-1]+1:]]
['M', '(SEP)', 'PAGD', '(VRQN)', 'NP', '(CG)', 'SKAC']
也许你喜欢它。这里有一些解释:
在你的问题中,你传递了一组切片,并且隐含地你也希望得到补片组(以生成未加括号的[是英语?]切片)。所以基本上,每个切片[i,j]都缺少先前的j。例如[7,10]缺少3和[1,3]缺少0。
reduce
进程列表和每一步都通过 到目前为止的输出 (a
)加上 下一个输入元素 (ij
)。诀窍是,除了生成普通输出之外,我们每次都会添加一个额外的变量---一种内存 - 这是在下一步中检索的 a[-1]
。在这个特定的例子中,我们存储了最后的j值,因此我们始终拥有完整的信息来提供未加密的子串和子括号的子串。
最后,使用[:-1]删除内存,并用原始str的其余部分替换 [str[split_points[-1][-1]+1:]]
。