【问题标题】:Most efficient way to concatenate and rearrange files连接和重新排列文件的最有效方法
【发布时间】:2010-03-24 14:46:45
【问题描述】:

我正在读取几个文件,每个文件分为两部分,首先是几千行的标题部分,然后是几千行的正文。我的问题是我需要将这些文件连接到一个文件中,其中所有标题都在顶部,然后是正文。

目前我正在使用两个循环:一个是拉出所有标题并写入它们,第二个是写入每个文件的主体(我还包括一个 tmp_count 变量来限制要加载的行数转储到文件之前的内存)。

这非常慢 - 13gb 文件大约需要 6 分钟。谁能告诉我如何优化这一点,或者在 python 中是否有更快的方法来做到这一点?

谢谢!

这是我的代码:

def cat_files_sam(final_file_name,work_directory_master,file_count):

    final_file = open(final_file_name,"w")

    if len(file_count) > 1:
        file_count=sort_output_files(file_count)

    # only for @ headers    
    for bowtie_file in file_count:
        #print bowtie_file
        tmp_list = []

        tmp_count = 0
        for line in open(os.path.join(work_directory_master,bowtie_file)):
            if line.startswith("@"):

            if tmp_count == 1000000:
                final_file.writelines(tmp_list)
                tmp_list = []
                tmp_count = 0

            tmp_list.append(line)
            tmp_count += 1

        else:
            final_file.writelines(tmp_list)
            break

    for bowtie_file in file_count:
        #print bowtie_file
        tmp_list = []

        tmp_count = 0
        for line in open(os.path.join(work_directory_master,bowtie_file)):
            if line.startswith("@"):
            continue
        if tmp_count == 1000000:
            final_file.writelines(tmp_list)
            tmp_list = []
            tmp_count = 0

        tmp_list.append(line)
        tmp_count += 1
        final_file.writelines(tmp_list)

    final_file.close()

【问题讨论】:

  • 请修正你的代码块的格式;不可读
  • 文件的格式是什么?你如何确定标题和正文之间的边界?
  • 您的代码格式仍然错误。 if line.startswith("@"): 之后出现语法错误。
  • 可以帮助您的一点是,@header 之后的所有剩余部分都是@body,这表明您不需要逐行阅读此部分,但可以复制它作为一个整体。另外,我不确定 1M 行是否是 IO 写入和内存之间的适当平衡,因此您也可以尝试一下。
  • 嗨,我很抱歉格式不正确,但在文本框中所有内容都正确缩进,我已尝试重置标签 - 可能是我的浏览器有问题。将尝试尽快修复它。感谢大家的回复!

标签: python file concatenation


【解决方案1】:

您希望移动 13Gb 数据的速度有多快?这个问题是 I/O 限制的,而不是 Python 的问题。要使其更快,请减少 I/O。这意味着您要么 (a) 坚持现有的速度,要么 (b) 重新调整工具链的后续元素以就地处理文件,而不是需要一个 13 Gb 的巨大文件。

【讨论】:

  • “你期望多快”:每 en.wikipedia.org/wiki/Hard_disk_drive#Data_transfer_rate ,在典型驱动器上为 70 MB/秒(磁盘到内存,反之亦然),因此如果数据被写入不同的驱动器它正在被读取,考虑到一个好的操作系统会重叠操作,人们可能希望在 3 分钟多一点的时间内移动 13GB。 6 分钟几乎是这个时间的两倍,这表明程序执行的 I/O 量是所需的两倍(当然,或者文件系统的碎片化或操作系统很糟糕;-)。我建议的方法可能会减少 I/O(不读取文件的标题两次)。
  • 但如果它不是不同的磁盘(我敢打赌不是),那么这个特定的程序实际上运行得几乎是最佳状态! :)
【解决方案2】:

你可以节省第二次跳过标题所花费的时间,只要你有合理的空闲磁盘空间:除了最终文件,还打开(for 'w+')一个临时文件@ 987654321@,然后做:

import shutil

hdr_list = []
bod_list = []
dispatch = {True: (hdr_list, final_file), 
            False: (bod_list, temp_file)}

for bowtie_file in file_count:
    with open(os.path.join(work_directory_master,bowtie_file)) as f:
        for line in f:
            L, fou = dispatch[line[0]=='@']
            L.append(f)
            if len(L) == 1000000:
                fou.writelines(L)
                del L[:]

# write final parts, if any
for L, fou in dispatch.items():
    if L: fou.writelines(L)

temp_file.seek(0)
shutil.copyfileobj(temp_file, final_file)

这应该会提高您程序的性能。微调现在硬编码的1000000,甚至完全取消列表并将每一行直接写入适当的文件(最终或临时),是您应该进行基准测试的其他选项(但如果您有无限量的内存,那么我认为它们不会有太大影响——但是,关于性能的直觉往往会产生误导,所以最好尝试和衡量!-)。

【讨论】:

  • @Alex: 在循环之后也需要写最后一个块
  • @msw,不像家庭作业那样“闻起来”,所以显示优化的开始对我来说似乎没问题——它建议使用诸如通过 dict 调度之类的习语,以及诸如@987654324 之类的有用 stdlib 模块@,所以我希望它既能提供指导,又能快速有用。
  • @alex,完全同意,如果我的玩笑看起来很重要,我很抱歉。
  • @Alex 谢谢你的例子! @msw 我对如何优化这个过程很感兴趣,因为我每天都这样做(以及许多相关问题),虽然我仍然是一名初级程序员,但我很高兴有机会让我的代码尽可能高效地工作
【解决方案3】:

您要编写的代码有两个严重的低效率问题(这不是提供的代码):

  1. 您正在第一个主要的 for 块中建立大量标题行列表,而不是仅仅将它们写出来。
  2. 当您已经确定标题在 (1) 中的结尾位置时,您将在第二个主要 for 块中逐行跳过文件的标题。见file.seek and file.tell

【讨论】:

  • 感谢您的建议!昨天我与一位更资深的程序员讨论了这个问题,并在我的代码中实现了 seek() 和 tell()(我现在只看到这些帖子,所以我没有实现任何其他改进)但这总体上产生的影响很小这仍然需要大约 6 分钟。我不知道在内存中构建列表比单独编写每一行要慢。我认为这样做的 I/O 开销比一次写入整个块要高。我只是好奇为什么会这样。
  • 无论是先在内存中构建列表还是在阅读时逐行发出列表,最终都将不得不执行相同数量的 i/o。你不做的事情(在这种情况下是列表构建)花费零时间。该库正在(默默地)缓冲您的读取和写入,因此并非每个 readline(对于 f 中的行)或 writeline() 实际上都在进行磁盘访问。
  • 也就是说,Alex Martelli 的方法可能比我提到的较小的优化更有效,也更“Pythonic”。我说“可能是”,因为我没有衡量过它,而且经常——用任何语言——对效率的猜测往往是不正确的。
猜你喜欢
  • 1970-01-01
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-24
  • 2011-02-18
  • 2010-09-06
相关资源
最近更新 更多