如何优化打开和读取多次相同文件的python脚本?

Posted

技术标签:

【中文标题】如何优化打开和读取多次相同文件的python脚本?【英文标题】:How to optimize a python script which opens and reads multiple times the same files? 【发布时间】:2014-03-01 17:34:39 【问题描述】:

我有一个运行良好的代码,但它占用了太多内存。

本质上,这段代码采用一个输入文件(我们称之为索引,即 2 列制表符分隔)在第二个输入文件(我们称其为数据,即 4 列制表符分隔)中搜索相应的术语在第一列中,然后将其替换为索引文件中的信息。

索引的一个例子是:

amphibian   anm|art|art|art|art
anaconda    anm
aardvark    anm

数据的一个例子是:

amphibian-n is  green   10
anaconda-n  is  green   2
anaconda-n  eats    mice    1
aardvark-n  eats    plants  1

因此,将数据的Col 1中的值替换为Index中对应的信息时,结果如下:

anm-n   is  green
art-n   is  green
anm-n   eats    mice
anm-n   eats    plants

我将代码分成多个步骤,因为这个想法是计算给定数据文件中第 2 列和第 3 列的替换项(数据中的第 4 列)的值的平均值。此代码获取数据文件中插槽填充器的总数,并对步骤 3 中使用的值求和。

期望的结果如下:

anm second  hello   1.0
anm eats    plants  1.0
anm first   heador  0.333333333333
art first   heador  0.666666666667

我在步骤 1、2 和 3 中多次打开同一个输入文件(即 3 次),因为我需要创建几个需要按特定顺序创建的字典。 有没有办法优化我打开同一个输入文件的次数?

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import division
from collections import defaultdict

import datetime

print "starting:",
print datetime.datetime.now()

mapping = dict()

with open('input-map', "rb") as oSenseFile:
    for line in oSenseFile:
        uLine = unicode(line, "utf8")
        concept, conceptClass = uLine.split()
        if len(concept) > 2:  
                mapping[concept + '-n'] = conceptClass


print "- step 1:",
print datetime.datetime.now()

lemmas = set()

with open('input-data', "rb") as oIndexFile:
    for line in oIndexFile:
        uLine = unicode(line, "latin1")
        lemma = uLine.split()[0]
        if mapping.has_key(lemma):
            lemmas.add(lemma)

print "- step 2:",
print datetime.datetime.now()


featFreqs = defaultdict(lambda: defaultdict(float))

with open('input-data', "rb") as oIndexFile:            
    for line in oIndexFile:
        uLine = unicode(line, "latin1")
        lemmaTAR, slot, filler, freq = uLine.split()
        featFreqs[slot][filler] += int(freq)


print "- step 3:",
print datetime.datetime.now()

classFreqs = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))

with open('input-data', "rb") as oIndexFile:            
    for line in oIndexFile:
        uLine = unicode(line, "latin1")
        lemmaTAR, slot, filler, freq = uLine.split()
        if lemmaTAR in lemmas:
            senses = mapping[lemmaTAR].split(u'|')
            for sense in senses:
                classFreqs[sense][slot][filler] += (int(freq) / len(senses)) / featFreqs[slot][filler]
        else:
            pass

print "- step 4:",
print datetime.datetime.now()

with open('output', 'wb') as oOutFile:
    for sense in sorted(classFreqs):
                for slot in classFreqs[sense]:
                        for fill in classFreqs[sense][slot]:
                                outstring = '\t'.join([sense, slot, fill,\
                                                       str(classFreqs[sense][slot][fill])])
                                oOutFile.write(outstring.encode("utf8") + '\n')

有关如何优化此代码以处理大型文本文件(例如 >4GB)的任何建议?

【问题讨论】:

优化标准是什么? 我正在尝试处理一个 4 GB 的文件。它在步骤 2 中某处的 16GB 服务器上的内存不足。我希望能够使用服务器中的 16GB 内存来处理整个文件。你有什么建议? 这对您来说可能是不可能的,但是对于这类事情来说,实际的数据库不是更好的选择吗? 【参考方案1】:

如果我正确理解代码,则不需要引理集。您可以删除第 1 步并替换第 3 步中的检查

    if lemmaTAR in lemmas:

直接用

    if mapping.has_key(lemmaTAR):

关于内存问题 - 您是否尝试过减少保存在内存中的数据的开销?目前您正在使用嵌套字典。也许平面数据结构会使用更少的内存,例如一维featFreqs,它采用由“%slot-%filler”构造的单个键。

【讨论】:

谢谢,这真的解决了我的问题。除了按照您的建议删除第 1 步并替换第 3 步中的检查外,我还删除了所有 unicode 变量并将 featFreq 中的 float 更改为 int。通过这些更改和您的建议,我能够在大约 16GB 的内存中处理整个文件(4GB)。 30 分钟。

以上是关于如何优化打开和读取多次相同文件的python脚本?的主要内容,如果未能解决你的问题,请参考以下文章

如何读取刚刚由python中的函数创建的文件

在具有不同输入的 1x exe 中同时多次运行 python 脚本

如何在 C# .NET 中保持 TCP 连接打开并执行多次写入/读取?

include 和 require 的区别是什么? 如何避免多次包含同一文件?

如何使用Python脚本从PDF中读取阿拉伯语文本

如何在不修改脚本的情况下计算 PHP 脚本的文件读取和写入?