Python - 检查两个巨大文本文件之间的一致性

时间:2015-11-18 07:50:28

标签: python python-2.7 parsing bigdata fastq

所以,这个给了我一个艰难的时刻! 我正在使用 HUGE 文本文件,而且我的意思是100Gb +。具体来说,它们位于fastq format。这种格式用于DNA测序数据,由四行记录组成,如下所示:

@REC1
GATTTGGGGTTCAAAGCAGTATCGATCAAATAGTAAATCCATTTGTTCAACTCACAGTTT  
+  
!''*((((***+))%%%++)(%%%%).1***-+*''))*55CCF>>>>>>CCCCCCC65  
@REC2
GATTTGGGGTTCAAAGCAGTATCGATCAAATAGTAAATCCATTTGTTCAACTCACAGTTT  
+  
!''*((((***+))%%%++)(%%%%).1***-+*''))**55CCF>>>>>>CCCCCCC65  
.  
.  
.

为了这个问题,只需关注标题行,以'@'开头。

因此,出于QA目的,我需要比较两个这样的文件。这些文件应该有匹配的标题,因此另一个文件中的第一个记录也应该有标题'@ REC1',下一个应该是'@ REC2',依此类推。在进行大量下游分析之前,我想确保情况确实如此 由于文件太大,字符串比较的天真迭代需要很长时间,但是这个QA步骤将运行多次,我不能等待那么久。所以我认为更好的方法是从文件中的几个点采样记录,例如每10%的记录。如果记录的顺序搞砸了,我很可能会发现它 到目前为止,我已经能够通过估计文件大小来处理这些文件,而不是使用python的file.seek()来访问文件中间的记录。例如,要访问大约中间的一行,我会这样做:

file_size = os.stat(fastq_file).st_size
start_point = int(file_size/2)
with open(fastq_file) as f:
    f.seek(start_point)
    # look for the next beginning of record, never mind how

但现在问题更复杂,因为我不知道如何在两个文件之间进行协调,因为字节位置不是文件中行索引的指示符。换句话说,如何访问两个文件中的10,567,311行,以确保它们是相同的,而不会遍历整个文件?

会欣赏任何想法\提示。也许并行迭代?但究竟如何呢? 谢谢!

5 个答案:

答案 0 :(得分:3)

抽样是一种方法,但你依靠运气。此外,Python是这项工作的错误工具。您可以使用标准的Unix命令行工具以不同的方式做事并以合理有效的方式计算确切的答案:

  1. 线性化您的FASTQ记录:使用标签替换前三行中的换行符。
  2. 在一对线性化文件上运行diff。如果存在差异,diff会报告。
  3. 要进行线性化,您可以通过awk

    运行FASTQ文件
    $ awk '\
        BEGIN { \
          n = 0; \
        } \
        { \
          a[n % 4] = $0; \
          if ((n+1) % 4 == 0) { \
            print a[0]"\t"a[1]"\t"a[2]"\t"a[3]; \
          } \
          n++; \
        }' example.fq > example.fq.linear 
    

    比较一对文件:

    $ diff example_1.fq.linear example_2.fq.linear
    

    如果存在任何差异,diff会找到它并告诉您哪个FASTQ记录有所不同。

    您可以直接在这两个文件上运行diff,而无需进行线性化的额外工作,但如果您首先进行线性化,则更容易看出哪个读取有问题。

    所以这些都是大文件。编写新文件的时间和磁盘空间都很昂贵。有一种方法可以使用streams改善这一点。

    如果您将awk脚本放入文件(例如linearize_fq.awk),您可以这样运行:

    $ awk -f linearize_fq.awk example.fq > example.fq.linear
    

    这可能对您的100多个Gb文件有用,因为您现在可以通过bash process substitutions设置两个Unix文件流,并直接在这些流上运行diff:< / p>

    $ diff <(awk -f linearize_fq.awk example_1.fq) <(awk -f linearize_fq.awk example_2.fq)
    

    或者您可以使用named pipes

    $ mkfifo example_1.fq.linear
    $ mkfifo example_2.fq.linear
    $ awk -f linearize_fq.awk example_1.fq > example_1.fq.linear &
    $ awk -f linearize_fq.awk example_2.fq > example_2.fq.linear &
    $ diff example_1.fq.linear example_2.fq.linear
    $ rm example_1.fq.linear example_2.fq.linear
    

    命名管道和进程替换都避免了创建额外(常规)文件的步骤,这可能是您的输入问题。将100+ Gb文件的线性化副本写入磁盘可能需要一段时间才能完成,而这些副本也可能使用您可能没有太多的磁盘空间。

    使用流可以解决这两个问题,这使它们对于以有效的方式处理生物信息学数据集非常有用。

    你可以用Python重现这些方法,但它几乎肯定会运行得慢得多,因为Python在这类I / O繁重的任务上非常慢。

答案 1 :(得分:2)

并行迭代可能是在Python中执行此操作的最佳方法。我不知道它的运行速度有多快(快速的SSD可能是提高速度的最好方法),但是既然你不得不计算两个文件中的换行符,我就不会看到一种方法围绕这个:

with open(file1) as f1, open(file2) as f2:
    for l1, l2 in zip(f1,f2):
        if l1.startswith("@REC"):
            if l1 != l2:
                print("Difference at record", l1)
                break
    else:
        print("No differences")

这是为Python 3编写的,其中zip返回迭代器;在Python 2中,您需要使用itertools.izip()代替。

答案 2 :(得分:1)

您是否考虑过使用rdiff命令 rdiff的好处是:

  • 使用相同的4.5GB文件,rdiff只吃了大约66MB的RAM并且缩放得非常好。它从未坠毁到目前为止。
  • 它比diff快得多。
  • rdiff本身结合了diff和patch功能,因此您可以创建增量并使用相同的程序应用它们

rdiff的缺点是:

  • 它不是标准Linux / UNIX发行版的一部分 - 你必须这样做 安装librsync包。
  • delta文件rdiff生成的格式与diff不同。
  • delta文件略大(但不足以照顾)。
  • 在使用rdiff生成delta时使用稍微不同的方法,这既是好的也是坏的 - 需要2个步骤。该 第一个产生一个特殊的签名文件。在第二步,a delta是使用另一个rdiff调用创建的(全部如下所示)。而 两步过程可能看起来很烦人,它有好处 提供比使用diff时更快的增量。

请参阅:http://beerpla.net/2008/05/12/a-better-diff-or-what-to-do-when-gnu-diff-runs-out-of-memory-diff-memory-exhausted/

答案 3 :(得分:0)

import sys
import re

""" To find of the difference record in two HUGE files. This is expected to  
use of minimal memory. """

def get_rec_num(fd):
    """ Look for the record number. If not found return -1"""
    while True:
        line = fd.readline()
        if len(line) == 0: break
        match =  re.search('^@REC(\d+)', line)
        if match:
            num = int(match.group(1))
            return(num)
    return(-1)

f1 = open('hugefile1', 'r')
f2 = open('hugefile2', 'r')

hf1 = dict()
hf2 = dict()
while f1 or f2:
    if f1:
        r = get_rec_num(f1)
        if r < 0:
            f1.close()
            f1 = None
        else:
            # if r is found in f2 hash, no need to store in f1 hash  
            if not r in hf2:
                hf1[r] = 1
            else:
                del(hf2[r])
        pass
    pass
    if f2:
        r = get_rec_num(f2)
        if r < 0:
            f2.close()
            f2 = None
        else:
            # if r is found in f1 hash, no need to store in f2 hash  
            if not r in hf1:
                hf2[r] = 1
            else:
                del(hf1[r])
        pass
    pass

print('Records found only in f1:')
for r in hf1:
    print('{}, '.format(r));
print('Records found only in f2:')
for r in hf2:
    print('{}, '.format(r));

答案 4 :(得分:0)

从我的角度来看,来自@AlexReynolds和@TimPietzcker的答案都非常出色,但我想把我的两分钱放进去。你也可能想要加速你的硬件:

  • 带SSD的Raplace硬盘
  • 选择n SSD并创建RAID 0.在完美的世界中,您的磁盘IO速度将提高n倍。
  • 调整从SSD / HDD读取的块的大小。我希望,例如,一个16 MB的读取执行速度比十六个1 MB的读取速度快。 (这适用于单个SSD,对于RAID 0优化,必须查看RAID控制器选项和功能)。

最后一个选项与NOR SSD特别相关。不要追求最低的RAM利用率,但要尽可能多地阅读以保持磁盘读取速度。例如,从两个文件并行读取单行可能会降低读取速度 - 想象一下两个文件中的两行始终位于同一磁盘的同一侧的HDD。