如何创建索引来解析大文本文件

时间:2014-01-13 23:25:51

标签: python sqlite file indexing fastq

我有两个FASTQ格式的文件A和B,它们基本上是以从@开头的4行组成的几亿行文本,如下所示:

@120412_SN549_0058_BD0UMKACXX:5:1101:1156:2031#0/1
GCCAATGGCATGGTTTCATGGATGTTAGCAGAAGACATGAGACTTCTGGGACAGGAGCAAAACACTTCATGATGGCAAAAGATCGGAAGAGCACACGTCTGAACTCN
+120412_SN549_0058_BD0UMKACXX:5:1101:1156:2031#0/1
bbbeee_[_ccdccegeeghhiiehghifhfhhhiiihhfhghigbeffeefddd]aegggdffhfhhihbghhdfffgdb^beeabcccabbcb`ccacacbbccB

我需要比较

5:1101:1156:2031#0/

文件A和B之间的部分,并在文件B中写入与新文件匹配的4行组。我在python中得到了一段代码,但只适用于小文件,因为它解析了文件A中每个@ -line的文件B的整个@ -lines,并且这两个文件都包含数亿行。 / p>

有人建议我应该为文件B创建一个索引;我已经google了一下没有成功,如果有人能指出如何做到这一点或让我知道一个教程,我会非常感激,所以我可以学习。感谢。

== EDIT == 理论上,每组4行应该只在每个文件中存在一次。如果在每场比赛后打破解析,它会提高速度吗?还是我需要一个不同的算法?

2 个答案:

答案 0 :(得分:1)

这些人声称在使用专用库时解析了几个演出文件,请参阅http://www.biostars.org/p/15113/

fastq_parser = SeqIO.parse(fastq_filename, "fastq") 
wanted = (rec for rec in fastq_parser if ...)
SeqIO.write(wanted, output_file, "fastq")

更好的方法IMO将解析它一次并将数据加载到某个数据库而不是output_file(即mysql),后者在那里运行查询

答案 1 :(得分:1)

索引只是您正在使用的信息的缩短版本。在这种情况下,您将需要“键” - @ -line上的第一个冒号(':')和末尾附近的最后斜杠('/')之间的文本 - 以及某种值。< / p>

由于这种情况下的“值”是4行块的全部内容,并且因为我们的索引将为每个块存储一个单独的条目,所以如果我们使用了它,我们将把整个文件存储在内存中索引中的实际值。

相反,让我们使用4行块开头的文件位置。这样,您可以移动到该文件位置,打印4行,然后停止。总成本是存储整数文件位置所需的4或8个或多个字节,而不是实际基因组数据的多个字节。

这是一些完成工作的代码,但也进行了大量的验证和检查。你可能想扔掉你不使用的东西。

import sys

def build_index(path):
    index = {}
    for key, pos, data in parse_fastq(path):
        if key not in index:
            # Don't overwrite duplicates- use first occurrence.
            index[key] = pos

    return index

def error(s):
    sys.stderr.write(s + "\n")

def extract_key(s):
    # This much is fairly constant:
    assert(s.startswith('@'))
    (machine_name, rest) = s.split(':', 1)
    # Per wikipedia, this changes in different variants of FASTQ format:
    (key, rest) = rest.split('/', 1)
    return key

def parse_fastq(path):
    """
    Parse the 4-line FASTQ groups in path.
    Validate the contents, somewhat.
    """
    f = open(path)
    i = 0
    # Note: iterating a file is incompatible with fh.tell(). Fake it.
    pos = offset = 0
    for line in f:
        offset += len(line)
        lx = i % 4
        i += 1
        if lx == 0:     # @machine: key
            key = extract_key(line)
            len1 = len2 = 0
            data = [ line ]
        elif lx == 1:
            data.append(line)
            len1 = len(line)
        elif lx == 2:   # +machine: key or something
            assert(line.startswith('+'))
            data.append(line)
        else:           # lx == 3 : quality data
            data.append(line)
            len2 = len(line)

            if len2 != len1:
                error("Data length mismatch at line "
                        + str(i-2)
                        + " (len: " + str(len1) + ") and line "
                        + str(i)
                        + " (len: " + str(len2) + ")\n")
            #print "Yielding @%i: %s" % (pos, key)
            yield key, pos, data
            pos = offset

    if i % 4 != 0:
        error("EOF encountered in mid-record at line " + str(i));

def match_records(path, index):
    results = []
    for key, pos, d in parse_fastq(path):
        if key in index:
            # found a match!
            results.append(key)

    return results

def write_matches(inpath, matches, outpath):
    rf = open(inpath)
    wf = open(outpath, 'w')

    for m in matches:
        rf.seek(m)
        wf.write(rf.readline())
        wf.write(rf.readline())
        wf.write(rf.readline())
        wf.write(rf.readline())

    rf.close()
    wf.close()

#import pdb; pdb.set_trace()
index = build_index('afile.fastq')
matches = match_records('bfile.fastq', index)
posns = [ index[k] for k in matches ]
write_matches('afile.fastq', posns, 'outfile.fastq')

请注意,此代码返回到第一个文件以获取数据块。如果文件之间的数据相同,则可以在匹配发生时从第二个文件中复制块。

另请注意,根据您要提取的内容,您可能希望更改输出块的顺序,并且您可能希望确保键是唯一的,或者可能确保键不是唯一的但是按照它们匹配的顺序重复。这取决于你 - 我不确定你在做什么数据。