合并超过10个文件的有效方法,每个文件在一个列上的行数超过一百万

时间:2019-07-25 10:35:30

标签: python memory merge

我需要在其第一列上合并83个文件,并且第一列中的id在文件之间并不总是相同的。由于这些文件每个都包含几百万行,因此我需要一种有效的计算方法来将它们合并成宽格式。

因此,我对this question by Stephen Rauch中的python答案做了一些修改。它将以正确的模式在目录中一个接一个地读取每个文件,创建一个包含id-value匹配的sampleID的字典,然后从所有词典中将每个id作为行调用。

from os import listdir
import fnmatch
import re


vcfs= fnmatch.filter(listdir("."), "pivotted_vaf_out*")

columns = []
data = {}
ids = set()

for filename in vcfs:
    with open(filename, 'rU') as f:
        key=re.findall(r"[0-9]+",filename)[0]
        columns.append(key)
        data[key] = {}
        for line in f:
            if line.strip():
                id, value = line.strip().split()
                try:
                    data[key][(id)] = value
                except ValueError as exc:
                    raise ValueError(
                        "Problem in line: '{}' '{}' '{}'".format(
                            id, value, line.rstrip()))

                ids.add((id))

print('\t'.join(['ID'] + columns))

for id in sorted(ids):
    line = []
    for column in columns:
        line.append(data[column].get(id, './.'))
    print('\t'.join([str(id)] + line))

我的输入和输出如下所示。

File1.tsv
chr1_13868_A_G  0/1
chr1_13896_C_A  0/1
chr1_14464_A_T  1/1
chr1_14653_C_T  0/1

File2.tsv
chr1_13868_A_G  0/1
chr1_14464_A_T  1/1
chr1_14654_G_T  1/1

File3.tsv
chr1_13868_A_G  0/1
chr1_13896_C_A  0/1
chr1_14464_A_T  1/1
chr1_14653_C_T  0/1

Together.tsv
ID     1     2     3
chr1_13868_A_G  0/1     0/1     0/1
chr1_13896_C_A  0/1     ./.     0/1
chr1_14464_A_T  1/1     1/1     1/1
chr1_14653_C_T  0/1     0/1     0/1
chr1_14654_G_T  1/1     ./.     ./.

如果我分配了足够的内存,此代码可以完美地工作。我想知道有什么替代和有效的解决方案来合并/合并大量行数很大的文件。

1 个答案:

答案 0 :(得分:0)

使用POSIX中已经可用的工具:sortjoin。 Python通常会浪费内存(对象开销),并且在POSIX工具中已经为您完成了所有工作之后,您可能需要编写代码。 (我不知道是否存在现成的东西。)

此外,POSIX join不需要内存中的文件即可发挥作用(它每次只能从每个文件读取一行),并且可以按顺序进行排序(因此,您只需要一个文件即可)一次)。

分别排序每个文件:

sort -t$'\t' -k1,1 File1.tsv > File1.sorted.tsv
sort -t$'\t' -k1,1 File2.tsv > File2.sorted.tsv
sort -t$'\t' -k1,1 File3.tsv > File3.sorted.tsv

然后将他们一一加入:

join -t$'\t' -a1 -a2 -e./. -o0,1.2,2.2 File1.sorted.tsv File2.sorted.tsv > join_2.tsv
join -t$'\t' -a1 -a2 -e./. -o0,1.2,1.3,2.2 join_2.tsv File3.sorted.tsv > join_3.tsv

不幸的是,您将需要列出join中的所有字段(我不知道如何解决);您要连接的第一个文件中的每个数据列都需要-o0,,然后是1.x,从2开始(即join_2.tsv有两个数据列,因此您需要{{1 }})后接1.2,1.3。到第83个文件时,2.2列表将变得很胖。 :(

很明显,编写一个循环(在bash中完全可能,尽管有点混乱),或者编写一个程序来生成所需的全部165条命令真是太棒了。您不是想要手动执行此操作:D

无论如何,语法解释:-o要求对文件进行排序,并且排序顺序必须与join使用的文件相同。因此,join选项(对于-tsort)都将制表符作为定界符,而join选项确保-k1,1仅考虑联接列,并且不像默认情况下那样将列作为主要排序标准(与其他列的关联消除歧义)加入。 sort正在产生完整的外部联接,并且-a1 -a2指定-e./.应该用于空值。最后,./.不会输出任何空字段(即使我们指定了join),除非您用-e(sheesh)列出输出字段。