python初学者快速查找和替换大文件的方法?

2024-09-28 20:47:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个大约1亿行的文件,我想用存储在制表符分隔的文件中的替代文本替换文本。我的代码可以工作,但是要花一个小时来处理第一个70K行。输入为了逐步提高我的python技能,我想知道是否有更快的方法来实现这一点。谢谢! 输入文件如下所示:

CHROMOSOME_IV ncRNA gene 5723085 5723105 . - . ID=Gene:WBGene00045518 CHROMOSOME_IV ncRNA ncRNA 5723085 5723105 . - . Parent=Gene:WBGene00045518

包含替换值的文件如下所示:

WBGene00045518 21ur-5153

这是我的代码:

infile1 = open('f1.txt', 'r')
infile2 = open('f2.txt', 'r')
outfile = open('out.txt', 'w')

import re
from datetime import datetime
startTime = datetime.now()

udict = {}
for line in infile1:
    line = line.strip()
    linelist = line.split('\t')
    udict1 = {linelist[0]:linelist[1]} 
    udict.update(udict1)

mult10K = []
for x in range(100):
    mult10K.append(x * 10000)   
linecounter = 0
for line in infile2:
    for key, value in udict.items():
        matches = line.count(key)
        if matches > 0: 
            print key, value
            line = line.replace(key, value)
            outfile.write(line + '\n')
        else:
            outfile.write(line + '\n')
    linecounter += 1
    if linecounter in mult10K:
        print linecounter   
        print (datetime.now()-startTime)
infile1.close()
infile2.close()
outfile.close()

Tags: 文件keyintxtfordatetimelineopen
3条回答

这段代码是完全的线性搜索。难怪它跑得很慢。如果不了解更多关于输入的信息,我无法就如何解决这些问题向您提供建议,但我至少可以指出问题所在。我会记下主要的问题,还有几个小问题。在

udict = {}
for line in infile1:
    line = line.strip()
    linelist = line.split('\t')
    udict1 = {linelist[0]:linelist[1]} 
    udict.update(udict1)

此处不要使用update;只需将该项添加到字典中:

^{pr2}$

这比为每个条目创建字典要快。(实际上,Sven Marnach基于生成器的方法来创建这个字典更好。)这是相当小的。在

mult10K = []
for x in range(100):
    mult10K.append(x * 10000)

这完全没有必要。删除这个;我将向您展示一种不使用这个间隔打印的方法。在

linecounter = 0
for line in infile2:
    for key, value in udict.items():

这是你的第一个大问题。你在字典中对每一行的键进行线性搜索。如果字典非常大,这将需要大量的操作:100000000*len(udict)。在

        matches = line.count(key)

这是另一个问题。您正在使用线性搜索查找匹配项。然后执行replace,它执行相同的线性搜索!不需要检查匹配项;replace如果没有匹配项,则返回相同的字符串。这也不会有什么大的不同,但它会给你带来一些东西。在

        line = line.replace(key, value)

继续进行这些替换,然后只在所有替换完成后才写下该行:

    outfile.write(line + '\n')

最后

    linecounter += 1
    if linecounter in mult10K:

原谅我,但这样做太荒谬了!您正在通过linecounter进行线性搜索,以确定何时打印一行。在这里,这又增加了将近100000000*100个操作。你应该至少在一个集合中搜索;但是最好的方法(如果你真的必须这样做的话)是做一个模运算并测试它。在

    if not linecounter % 10000: 
        print linecounter   
        print (datetime.now()-startTime)

为了使这段代码高效,您需要去掉这些线性搜索。Sven Marnach的回答建议了一种可能有效的方法,但我认为这取决于文件中的数据,因为替换键可能不符合明显的单词边界。(不过,regex方法添加了地址。)

我在考虑你对dictionary键的循环,以及一个wqya来优化这一点,并允许在以后对你的代码进行其他注释。在

但后来我无意中发现了这一点:

if linecounter in mult10K:
    print linecounter   
    print (datetime.now()-startTime)

这个看起来像inocent的片段实际上让Python按顺序查看并比较文件中每行的“linecounter”列表中的10000个项。在

将此部件替换为:

^{pr2}$

(忘记所有的mult10k部分)-你应该得到一个显著的加速。在

而且,似乎您正在为每个输入行记录多个输出行- 你的主循环是这样的:

linecounter = 0
for line in infile2:
    for key, value in udict.items():
        matches = line.count(key)
        if matches > 0: 
            print key, value
            line = line.replace(key, value)
            outfile.write(line + '\n')
        else:
            outfile.write(line + '\n')
    linecounter += 1

将其替换为:

for linecounter, line in enumerate(infile2):
    for key, value in udict.items():
        matches = line.count(key)
        if matches > 0: 
            print key, value
            line = line.replace(key, value)
    outfile.write(line + '\n')

它只为每个输入行正确地写入一个输出行(除了消除代码重复,并以“pythonic”方式处理行计数之外)

你应该把你的行拆分成“单词”,只在字典中查找这些单词:

>>> re.findall(r"\w+", "CHROMOSOME_IV ncRNA gene 5723085 5723105 . - . ID=Gene:WBGene00045518 CHROMOSOME_IV ncRNA ncRNA 5723085 5723105 . - . Parent=Gene:WBGene00045518")
['CHROMOSOME_IV', 'ncRNA', 'gene', '5723085', '5723105', 'ID', 'Gene', 'WBGene00045518', 'CHROMOSOME_IV', 'ncRNA', 'ncRNA', '5723085', '5723105', 'Parent', 'Gene', 'WBGene00045518']

这将消除您对每一行所做的字典循环。在

以下是完整的代码:

^{pr2}$

编辑:另一种方法是从字典中构建单个mega regex:

with open("f1.txt", "r") as infile1:
    udict = dict(line.strip().split("\t", 1) for line in infile1)
regex = re.compile("|".join(map(re.escape, udict)))
with open("f2.txt", "r") as infile2, open("out.txt", "w") as outfile:
    for line in infile2:
        outfile.write(regex.sub(lambda m: udict[m.group()], line))

相关问题 更多 >