如何根据列表中的值从文本文件中删除行?

时间:2018-12-06 01:26:45

标签: python

我有一个很大的文本文件(coverage.txt)> 2G,看起来像这样:

#RefName    Pos Coverage
BGC0000001_59320bp  0   0
BGC0000001_59320bp  1   0
BGC0000002_59320bp  2   0
BGC0000002_59320bp  3   0
BGC0000002_59320bp  4   0
BGC0000003_59320bp  5   0
BGC0000003_59320bp  6   0
BGC0000003_59320bp  7   0
BGC0000004_59320bp  8   0
BGC0000004_59320bp  7   0
BGC0000004_59320bp  8   0
BGC0000005_59320bp  7   0
BGC0000005_59320bp  8   0
BGC0000005_59320bp  7   0
BGC0000006_59320bp  8   0
BGC0000006_59320bp  7   0
BGC0000006_59320bp  8   0
BGC0000007_59320bp  7   0
BGC0000007_59320bp  8   0
BGC0000007_59320bp  7   0
BGC0000008_59320bp  8   0
BGC0000008_59320bp  7   0
BGC0000008_59320bp  8   0
BGC0000009_59320bp  7   0
BGC0000009_59320bp  8   0

我还有另一个文本文件(rmList.txt),如下所示:

BGC0000002
BGC0000004
BGC0000006
BGC0000008

如果这些行包含rmList.txt中的ID,我想从我的coverage.txt文件中删除这些行。

这是我尝试过的:

wanted = [line.strip() for line in open('rmList.txt')]
files = 'coverage.txt'

def rmUnwanted(file):
    with open(file) as f, open('out.txt', 'w') as s:
        for line in f:
            pos = line.split()[0].split('_')[0]
            if pos not in wanted:
                s.write(line)

rmUnwanted(files)

但这对于我的大文件来说是永远的。有一个更好的方法吗?我的代码有什么问题吗?

非常感谢您!

3 个答案:

答案 0 :(得分:0)

使用set而不是list来检查重复的元素。

wanted = { line.strip() for line in open('rmList.txt') }

....

答案 1 :(得分:0)

在我看来,代码没有错,它可以满足您的要求。但是,大文件将需要时间。 您仍然可以提高效率。

如果您确定两个文件都已排序(如您的示例所示),则此代码应更快:

def rmUnwanted(file):
    with open(file) as f, open('out.txt', 'w') as s:
        i = 0
        lastwanted = ""
        for line in f:
            pos = line.split()[0].split('_')[0]
            try:
                if pos not in [wanted[i], lastwanted]:
                    s.write(line)
                else:
                    if pos == wanted[i]:
                        lastwanted = wanted[i]
                        i = i+1
            except IndexError:
                s.write(line)

使用您提供的示例文件可以得到相同的结果,但是速度更快(我没有测量,但是应该)。 我在这里要做的是避免在每次迭代时都在整个pos列表中寻找wanted,如果您的实际rmList.txt也很大,那会很费时间。

答案 2 :(得分:0)

您可以执行以下操作:

with open("rmLst.txt") as f:
    rmLst = set(f.readlines())

with open("out.txt", "w") as outf, open("coverage.txt") as inf:
    # write header
    outf.write(next(inf))
    # write lines that do not start with a banned ID
    outf.writelines(line for line in inf if line[:line.index("_")] not in rmList)

首先,将要删除的所有ID存储在一组中以进行快速查找。然后,遍历行并检查每行是否以错误的ID开头。请注意,我们可以运行line.split()来检查访问每一行的ID部分,而不是运行line[:line.index['_']]。这样可以避免创建每行的副本,并且应比split更快。如果所有ID的长度都是固定的,则可以用数字替换line.index['_']