【问题标题】:Fast I/O when working with multiple files处理多个文件时的快速 I/O
【发布时间】:2020-11-17 15:11:48
【问题描述】:

我有两个输入文件,我想将它们混合并将结果输出到第三个文件中。在下文中,我将使用一个玩具示例来解释文件的格式和所需的输出。每个文件包含重复的 4 行模式(但包含不同的序列),我只包含单个 4 行:

输入文件1:

@readheader1
ACACACACACACACACACACACACACACACACACACACACACACACACACACAC
+
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
...

输入文件2:

@readheader2
AATTAATT
+
FFFFFFFF
...

想要的输出:

@readheader1_AATTAATT
ACACACACACACACACACACACACACACACACACACACACACACACACACACAC
+
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
...

所以我想在small sequence found in the second line of every four line from the second file 上使用下划线附加first line of every four line from the first file。我只是将第一行的每四行中的第 2n、第 3 和第 4 行按原样输出到输出中。

我正在寻找可以优化以下内容的任何脚本(linux bash、python、c++ 等):

我编写了这段代码来完成这项任务,但我发现它很慢(60 GB 和 15 GB 的输入需要一天多的时间);请注意,输入文件是fastq.gz 格式,所以我使用 gzip 打开它们:

    ...
    r1_file = gzip.open(r1_file_name, 'r') # input file 1
    i1_file = gzip.open(i1_file_name, 'r') # input file 2
    
    out_file_R1 = gzip.open('_R1_barcoded.fastq.gz', 'wb') # output file

    r1_header = ''
    r1_seq = ''
    r1_orient = ''
    r1_qual = ''
    i1_seq = ''
    cnt = 1 
    with gzip.open(r1_file_name, 'r') as r1_file:
        for r1_line in r1_file:
            if cnt==1:
                r1_header = str.encode(r1_line.decode("ascii").split(" ")[0])
                next(i1_file)
            if cnt==2:
                r1_seq = r1_line
                i1_seq = next(i1_file)
            if cnt==3:
                r1_orient = r1_line
                next(i1_file)
            if cnt==4:
                r1_qual = r1_line
                next(i1_file)
                out_4line = r1_header + b'_' + i1_seq + r1_seq + r1_orient + r1_qual
                out_file_R1.write(out_4line)
                cnt = 0
            cnt += 1
    i1_file.close()
    out_file_R1.close()

    

然后我有两个输出使用 2 个数据集,我希望交错输出文件:第一个文件的 4 行,第二个文件的 4 行,第一个文件的 4 行,依此类推...

【问题讨论】:

  • 您可能希望考虑使用 asyncio (docs.python.org/3/library/asyncio.html) 将代码拆分为协程。考虑到您的用例,它可能是最有效的工具。线程是您可以尝试的另一种选择,尽管如果您将线程用于这么多的 I/O,您会浪费相当多的 CPU 时间。您可能还想在处理之前解压缩文件。

标签: python linux bash performance io


【解决方案1】:

使用 paste 实用程序(来自 GNU coreutils)和 GNU sed

paste file1 file2 |
sed -E 'N; s/\t.*\n([^\t]*)\t(.*)/_\2\n\1/; N; N; s/\t[^\n]*//g' > file.out

如果文件被压缩然后使用:

paste <(gzip -dc file1.gz) <(gzip -dc file2.gz) |
sed -E 'N; s/\t.*\n([^\t]*)\t(.*)/_\2\n\1/; N; N; s/\t[^\n]*//g' > file.out

注意:这假定 file1 和 file2 中没有制表符


解释:假设 file1 和 file2 包含以下行:
文件1:

Header1
ACACACACAC
XX
FFFFFFFFFFFF

文件2:

Header2
AATTAATT
YY
GGGGGG

paste 命令之后,行被合并,由TABs 分隔:

Header1\tHeader2
ACACACACAC\tAATTAATT
XX\tYY
FFFFFFFFFFFF\tGGGGGG

上面的\t 表示一个制表符。这些行被馈送到sedsed读取第一行,模式空间变为

Header1\tHeader2

N 命令将换行符添加到模式空间,然后将输入的下一行 (ACACACACAC\tAATTAATT) 附加到模式空间。模式空间变成了

Header1\tHeader2\nACACACACAC\tAATTAATT

并与正则表达式 \t.*\n([^\t]*)\t(.*) 匹配,如下所示。

Header1\tHeader2\nACACACACAC\tAATTAATT
       ||^^^^^^^||^^^^^^^^^^||^^^^^^^^
       \t   .*  \n ([^\t]*) \t  (.*)
       ||       ||    \1    ||   \2
      

\n 表示换行符。然后通过s/\t.*\n([^\t]*)\t(.*)/_\2\n\1/ 命令将匹配部分替换为_\2\n\1。模式空间变成了

Header1_AATTAATT\nACACACACAC

两个N 命令读取接下来的两行。现在模式空间是

Header1_AATTAATT\nACACACACAC\nXX\tYY\nFFFFFFFFFFFF\tGGGGGG

s/\t[^\n]*//g 命令删除TAB(包括)和换行符(不包括)之间的所有部分。在这个操作之后,最终的模式空间是

Header1_AATTAATT\nACACACACAC\nXX\nFFFFFFFFFFFF

打印出来的就是

Header1_AATTAATT
ACACACACAC
XX
FFFFFFFFFFFF

【讨论】:

  • 非常感谢;你能添加一个关于它是如何工作的解释吗?喜欢你如何使用 regex 命令访问每条记录?
  • @ameerosein 添加了解释。
猜你喜欢
  • 2021-08-17
  • 2020-06-26
  • 2013-09-22
  • 2018-05-20
  • 1970-01-01
  • 1970-01-01
  • 2010-12-25
  • 2015-10-14
  • 1970-01-01
相关资源
最近更新 更多