【问题标题】:how to create an index to parse big text file如何创建索引来解析大文本文件
【发布时间】:2014-01-13 23:25:51
【问题描述】:

我有两个 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,并且两个文件都包含数亿行。

有人建议我应该为文件 B 创建一个索引;我在谷歌上四处搜索但没有成功,如果有人能指出如何做到这一点或让我知道一个教程以便我可以学习,我将不胜感激。谢谢。

==编辑== 理论上,每组 4 行应该在每个文件中只存在一次。如果在每次比赛后中断解析,是否会足够提高速度,还是我需要完全不同的算法?

【问题讨论】:

    标签: python sqlite file indexing fastq


    【解决方案1】:

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

    由于本例中的“值”是 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')
    

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

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

    【讨论】:

    • > 非常感谢。对于像我这样的初学者来说,这是一些东西,但是看代码对我的学习很有好处。我将 afile 和 bfile 添加为 sys.argv[1]sys.argv[2] 以便将其作为命令行运行(通常不会遇到问题)。但是,当我尝试运行它时,它会显示 > /some/path/code.py(92)<module>() -> index = build_index(afile) (Pdb) 并停留在那里。它没有给出错误,它只是卡住了......
    • 对不起!我在代码中留下了对调试器的调用。带有“import pdb; pdb.set_trace()”的行会导致代码停止并调用调试器。注释掉或删除该行,它应该贯穿。 (或者,使用“n”表示下一步,“s”表示步入,“p expr”表示打印 expr,以便在代码运行时观察代码。)
    • 现在我收到以下警告,但没有输出...很抱歉打扰您:Traceback (most recent call last):File "py_fetch_pair.py", line 94, in <module>index = build_index(afile)index = build_index(afile)File "py_fetch_pair.py", line 12, in build_indexfor key, pos, data in parse_fastq(path):@987654332 @f = open(path)TypeError: coercing to Unicode: need string or buffer, file found
    • 没关系,它正在工作!那是我的错,脚本需要一个字符串,但我在尝试实现sys.argv[] 时将文件放在那里。非常感谢!
    • 有人能解释一下 [parse_fastq] 中 [pos and data] 的用途吗?这些在脚本的其余部分中在哪里以及如何使用?可能是我错过了一些东西。谢谢!
    【解决方案2】:

    这些家伙声称在使用专用库时解析了一些 gigs 文件,请参阅 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),然后在那里运行查询

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-11
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 2014-03-22
      • 2011-04-29
      相关资源
      最近更新 更多