根据同一列表中的下一个项目从列表中删除项目

时间:2018-07-13 14:47:30

标签: python list bioinformatics

我刚刚开始学习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

11 个答案:

答案 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 (重叠)的概念在sortstartswith方法中使用了扳手)。

对于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,它将相似的序列组合在一起。通过两个循环迭代数据:

  1. 第一个循环用唯一序列作为键构建一个空列表的字典。
  2. 第二个循环将与键相似的所有字符串附加为值。

由所得的dict组成一组值,并返回最长的序列。

请注意与预期输出的一些差异:

  1. FGHABCDEFGHIJKLMNO重叠,因此不是有效的输出。
  2. FEOEUDNBNUWD不是原始序列。重叠序列需要进行后处理。

答案 9 :(得分:1)

肯尼, 您几乎明白了,但是@scharette指出了两个问题:

  1. for循环和删除列表项不应同时进行。解决方法是使用while循环并显式增加索引。 while循环效率较低,因为它多次调用len()而不是一次,但这正是获得正确结果所需要的。
  2. 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)))