python初学者-在大文件中查找和替换的更快方法?

python初学者-在大文件中查找和替换的更快方法?,python,replace,Python,Replace,我有一个大约1亿行的文件,其中我想用存储在制表符分隔文件中的替换文本替换文本。我拥有的代码可以工作,但要处理前70K行大约需要一个小时。为了逐步提高我的python技能,我想知道是否有更快的方法来完成这项工作。谢谢 输入文件如下所示: linecounter = 0 for line in infile2: for key, value in udict.items(): matches = line.count(key) if matches >

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

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
染色体IV ncRNA基因5723085 572305。ID=基因:WBGene00045518 染色体IV ncRNA ncRNA 5723085 572305。父代=基因:WBGene00045518

具有替换值的文件如下所示:

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
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()

这不是特定于Python的,但是您可以稍微展开double for循环,以便文件写入不会在循环的每次迭代中发生。可能每1000或10000行写入一次文件。

Python中最明显的一种方法是-这是一种更快(更可读)的方法:

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

mult10K = [x*10000 for x in range(100)]
同样,如果您有:

udict = {}
for line in infile1:
    line = line.strip()
    linelist = line.split('\t')
    udict1 = {linelist[0]:linelist[1]} 
    udict.update(udict1)
我们可以使用
dict
理解(带有生成器表达式):

这里还值得注意的是,您似乎正在使用以制表符分隔的文件。在这种情况下,可能是比使用
split()
更好的选择

还请注意,使用将提高可读性,并确保关闭文件(即使在异常情况下)

如果在每个循环上执行Print语句,它们也会大大降低速度-它们对调试很有用,但在主数据块上运行时,可能值得删除它们

您可以做的另一件“更具pythonic”的事情是使用,而不是每次向变量添加一个。例如:

linecounter = 0
for line in infile2:
   ...
   linecouter += 1
可替换为:

for linecounter, line in enumerate(infile2):
    ...
在计算键的出现次数时,更好的解决方案是在中使用

if key in line:
因为这会在找到实例后短路

所有这些加起来,让我们看看我们有什么:

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

with open('f1.txt', 'r') as infile1:
    reader = csv.reader(delimiter='\t')
    udict = dict(reader)

with open('f2.txt', 'r') as infile2, open('out.txt', 'w') as outfile:
    for line in infile2:
        for key, value in udict.items():
            if key in line: 
                line = line.replace(key, value)
        outfile.write(line + '\n')
编辑:根据注释中的要求,列出补偿与正常循环:

python -m timeit "[i*10000 for i in range(10000)]"
1000 loops, best of 3: 909 usec per loop

python -m timeit "a = []" "for i in range(10000):" "  a.append(i)"
1000 loops, best of 3: 1.01 msec per loop

注意usec与msec。它不是很大,但很重要。

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

    udict[linelist[0]] = linelist[1]
>>关于findall(r“\w+”,“染色体IV ncRNA基因5723085 5723015.-.ID=基因:WBGene00045518染色体IV ncRNA 5723085 5723015.-.父母=基因:WBGene00045518”)
[‘染色体四’、‘ncRNA’、‘基因’、‘5723085’、‘572305’、‘ID’、‘基因’、‘WBGene00045518’、‘染色体四’、‘ncRNA’、‘5723085’、‘572305’、‘父母’、‘基因’、‘WBGene00045518’]
这将消除字典中每行的循环

以下是完整的代码:

重新导入
以open(“f1.txt”、“r”)作为填充1:
udict=dict(line.strip().split(“\t”,1)用于填充1中的行)
将open(“f2.txt”、“r”)作为infile2,将open(“out.txt”、“w”)作为outfile:
对于内嵌2中的线:
对于re.findall(r“\w+”,第行)中的单词:
如果单词在udict中:
行=行。替换(word,udict[word])
输出文件。写入(行)
编辑:另一种方法是从字典中构建单个mega正则表达式:

    udict[linelist[0]] = linelist[1]
打开(“f1.txt”,“r”)作为填充1:
udict=dict(line.strip().split(“\t”,1)用于填充1中的行)
regex=re.compile(“|”)
将open(“f2.txt”、“r”)作为infile2,将open(“out.txt”、“w”)作为outfile:
对于内嵌2中的线:
write(regex.sub(lambda m:udict[m.group()],第行))

我在考虑你对DICIARY键的循环,以及一个wqya来优化它,让我们稍后对你的代码进行其他注释

但后来我偶然发现了这一部分:

if linecounter in mult10K:
    print linecounter   
    print (datetime.now()-startTime)
这个看起来不起眼的片段实际上让Python按顺序查看并比较文件中每一行的“linecounter”列表中的10000项

将此部分替换为:

if linecounter % 10000 == 0:
    print linecounter   
    print (datetime.now()-startTime)
(忘记所有的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”方式处理行计数)

此代码充满了线性搜索。难怪它跑得很慢。在不了解更多输入信息的情况下,我无法就如何解决这些问题向您提供建议,但我至少可以指出问题所在。我会注意到主要问题,还有一些次要问题

udict = {}
for line in infile1:
    line = line.strip()
    linelist = line.split('\t')
    udict1 = {linelist[0]:linelist[1]} 
    udict.update(udict1)
不要在此处使用
update
;只需将该项添加到字典:

    udict[linelist[0]] = linelist[1]
这比为每个条目创建字典要快。(事实上,使用基于生成器的方法来创建这本词典更好。)不过这是相当小的

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)

这是另一个问题。您正在使用线性搜索查找匹配项。然后执行
替换
,这将执行相同的线性搜索!你不需要检查是否匹配
replace
仅返回相同的字符串(如果没有)。这也不会有太大的区别,但会给你带来一些好处

        line = line.replace(key, value)
继续执行这些替换,然后仅在完成所有替换后写入行:

    outfile.write(line + '\n')
最后

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