我刚刚开始学习python,在这里我有一个排序的蛋白质序列列表(总共59,000个序列),其中有些重叠。例如,我在这里列出了一个玩具清单:
ABCDE
ABCDEFG
ABCDEFGH
ABCDEFGHIJKLMNO
CEST
DBTSFDE
DBTSFDEO
EOEUDNBNUW
EOEUDNBNUWD
EAEUDNBNUW
FEOEUDNBNUW
FG
FGH
我想删除那些较短的重叠部分,而只保留最长的重叠部分,这样所需的输出将如下所示:
ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EAEUDNBNUW
FEOEUDNBNUWD
FGH
我该怎么办?我的代码如下:
with open('toy.txt' ,'r') as f:
pattern = f.read().splitlines()
print pattern
for i in range(0, len(pattern)):
if pattern[i] in pattern[i+1]:
pattern.remove(pattern[i])
print pattern
我收到错误消息:
['ABCDE', 'ABCDEFG', 'ABCDEFGH', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGH', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']
Traceback (most recent call last):
File "test.py", line 8, in <module>
if pattern[i] in pattern[i+1]:
IndexError: list index out of range
答案 0 :(得分:15)
还有其他可行的答案,但是没有一个可以解释您的实际问题。实际上,您实际上已经接近有效的解决方案,在我看来,这是最易读的答案。
该错误是由于您在使用range()
检查索引时正在变异同一列表而引起的。
因此,在增加i
变量的同时,您要从列表中删除项目,这在某一点上不可避免地导致index error
。
因此,这是您的初始代码的有效版本,并进行了一些更改,
pattern = ["ABCDE","ABCDEFG","ABCDEFGH","ABCDEFGHIJKLMNO","CEST","DBTSFDE","DBTSFDEO","EOEUDNBNUW","EAEUDNBNUW","FG","FGH"]
output_pattern = []
for i in range(0, (len(pattern)-1)):
if not pattern[i] in pattern[i+1]:
output_pattern.append(pattern[i])
# Adding the last item
output_pattern.append(pattern[-1])
print (output_pattern)
>>>> ['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']
请注意,如果您的列表先前已按照注释部分中提到的顺序进行排序,则此代码将起作用。
这段代码在做什么?
基本上,它使用与初始答案相同的逻辑,在列表上进行迭代,并检查下一项是否包含当前项。但是,使用另一个列表并迭代直到 beforely 项目,将解决您的索引问题。但是现在有一个问题,
我该如何处理最后一个项目?
由于列表已排序,因此您可以认为最后一项始终是唯一的。这就是为什么我使用
output_pattern.append(pattern[-1])
,它将添加初始列表的最后一项。
重要提示
此答案是针对OP最初的问题而写的,他想保持更长的重叠时间,我根据同一列表中的下一项引用。如@Chris_Rands所述,如果您的关注与生物学任务有关,并且需要找到任何重叠之处,则此解决方案不适合您的需求。
此代码无法识别潜在重叠的示例,
pattern = ["ACD", "AD", "BACD"]
,它将在不删除可能的"ACD"
重叠的情况下输出相同的结果。现在,作为一个澄清,这意味着算法要复杂得多,我最初认为这超出了问题的要求范围。如果您遇到这种情况,我可能在这里完全错了,但我确实认为C ++实现似乎更合适。看看@Chris_Rands在评论部分中建议的CD-Hit算法。
答案 1 :(得分:5)
您可以使用groupby()
和max()
在这里提供帮助:
from itertools import groupby
with open('toy.txt') as f_input:
for key, group in groupby(f_input, lambda x: x[:2]):
print(max(group, key=lambda x: len(x)).strip())
这将显示:
ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EOEUDNBNUW
EAEUDNBNUW
FGH
groupby()
通过基于函数返回匹配项的列表来工作,在这种情况下,连续的行具有相同的前两个字符。然后max()
函数获取此列表并返回长度最长的列表项。
答案 2 :(得分:4)
for %%i in (*.png) do mkdir "%%~ni"
输出:
['ABCDEFGHIJKLMNO','CEST','DBTSFDEO','EOEUDNBNUW','EAEUDNBNUW', 'FGH']
答案 3 :(得分:1)
with open('demo.txt') as f:
lines = f.readlines()
l_lines = len(lines)
n_lst = []
for i, line in enumerate(lines):
line = line.strip()
if i == l_lines - 1:
if lines[-2] not in line:
n_lst.append(line)
break
if line not in lines[i + 1]:
n_lst.append(line)
print(n_lst)
输出
['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']
答案 4 :(得分:1)
您可以使用二叉树,其插入过程将尝试查找该值之前的节点:
class Tree:
def __init__(self, val=None):
self.left, self.value, self.right = None, val, None
def insert_val(self, _val):
if self.value is None or _val.startswith(self.value):
self.value = _val
else:
if _val < self.value:
getattr(self.left, 'insert_val', lambda x:setattr(self, 'left', Tree(x)))(_val)
else:
getattr(self.right, 'insert_val', lambda x:setattr(self, 'right', Tree(x)))(_val)
def flatten(self):
return [*getattr(self.left, 'flatten', lambda :[])(), self.value, *getattr(self.right, 'flatten', lambda :[])()]
t = Tree()
for i in open('filename.txt'):
t.insert_val(i.strip('\n'))
print(t.flatten())
输出:
['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EAEUDNBNUW', 'EOEUDNBNUW', 'FGH']
答案 5 :(得分:1)
这将使您到达想要的位置:
with open('toy.txt' ,'r') as f:
lines = f.readlines()
data = set(lines)
print(sorted([i for i in lines if len([j for j in data if j.startswith(i)])==1]))
#['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EAEUDNBNUW', 'EOEUDNBNUW', 'FGH']
我添加了set
,以防多次出现相同的文本。
答案 6 :(得分:1)
一种简单的方法是一次处理输入文件的每一行,将每行与上一行进行比较,如果当前行中不包含 previous ,则将其保留。
代码可以很简单:
with open('toy.txt' ,'r') as f:
old = next(f).strip() # keep first line after stripping EOL
for pattern in f:
pattern = pattern.strip() # strip end of line...
if old not in pattern:
print old # keep old if it is not contained in current line
old = pattern # and store current line for next iteration
print old # do not forget last line
答案 7 :(得分:1)
与您的期望不完全匹配,但是,鉴于您声明的内容已经排序(并且不在EOEUDNBNUWD EAEUDNBNUW
附近),并且我不知道您为什么会错过EOEUDNBNUWD
不知道您的期望是正确的陈述还是我误解了您的问题。
(是的,我看到 overlap (重叠)的概念在sort
和startswith
方法中使用了扳手)。
对于OP重新声明该特定方面可能会很不错,我在未真正理解他的关注的情况下阅读@DSM评论。现在我知道了。
li = sorted([i.strip() for i in """
ABCDE
ABCDEFG
ABCDEFGH
ABCDEFGHIJKLMNO
CEST
DBTSFDE
DBTSFDEO
EOEUDNBNUW
EOEUDNBNUWD
EAEUDNBNUW
FEOEUDNBNUW
FG
FGH""".splitlines() if i.strip()])
def get_iter(li):
prev = ""
for i in li:
if not i.startswith(prev):
yield(prev)
prev = i
yield prev
for v in get_iter(li):
print(v)
输出:
ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EAEUDNBNUW
EOEUDNBNUWD
FEOEUDNBNUW
FGH
答案 8 :(得分:1)
代码
import collections as ct
def read_file(filepath):
"""Yield a generator of lines from a file."""
with open(filepath, "r") as f:
for line in f:
yield line.strip()
def find_longest_sequences(seqs):
"""Return a dict of the long common sequences."""
seqs = tuple(seqs)
dd = ct.defaultdict(list)
[dd[k].append(seq) for seq in seqs for k in seqs if k in seq]
return {max(v, key=len) for v in dd.values()}
data = read_file("test.txt")
find_longest_sequences(data)
输出
{'ABCDEFGHIJKLMNO',
'CEST',
'DBTSFDEO',
'EAEUDNBNUW',
'EOEUDNBNUWD',
'FEOEUDNBNUW'}
详细信息
我们使用read_file
产生文件的每一行。
find_longest_sequences
构建了一个defaultdict,它将相似的序列组合在一起。通过两个循环迭代数据:
由所得的dict组成一组值,并返回最长的序列。
请注意与预期输出的一些差异:
FGH
与ABCDEFGHIJKLMNO
重叠,因此不是有效的输出。FEOEUDNBNUWD
不是原始序列。重叠序列需要进行后处理。答案 9 :(得分:1)
肯尼, 您几乎明白了,但是@scharette指出了两个问题:
for
循环和删除列表项不应同时进行。解决方法是使用while
循环并显式增加索引。 while
循环效率较低,因为它多次调用len()
而不是一次,但这正是获得正确结果所需要的。IndexError
。这仅发生在最后一行。解决这个问题的方法是忽略错误。这样,我将您的代码修改为:
with open('toy.txt' ,'r') as f:
pattern = f.read().splitlines()
print pattern
try:
i = 0
while i < len(pattern):
if pattern[i] in pattern[i+1]:
pattern.remove(pattern[i])
print pattern
i += 1
except IndexError:
pass
答案 10 :(得分:0)
如其他答案所述,您的错误来自于在开始时计算输入的长度,然后在缩短列表时没有更新它。
这是可行的解决方案的另一种观点:
with open('toy.txt', 'r') as infile:
input_lines = reversed(map(lambda s: s.strip(), infile.readlines()))
output = []
for pattern in input_lines:
if len(output) == 0 or not output[-1].startswith(pattern):
output.append(pattern)
print('\n'.join(reversed(output)))