在两个大型排序文本文件中查找匹配键并比较值(VCF 文件)
Find matching key in two large sorted text files and compare values (VCF-files)
我正在寻找一种有效的解决方案来过滤两个数据集。基本上,我只想保留一个文件中没有丢失的行作为它们的键列,并且在两个文件中都没有值“0/0”。
输入数据(对于感兴趣的人,我为这个问题简化的基因组 VCF 文件)具有以下特征:
- 第 1 列和第 2 列一起按数字排序,唯一标识符
- 第 3 列以值 0/0、0/1 或 1/1 开头
脚本最好执行以下操作:
- 遍历 sample1.dat 中的每一行并在 sample2.dat
中查找相同的标识符
- 如果 sample1.dat 中的标识符未在 sample2.dat 中找到,则不执行任何操作
- 如果两行都包含“0/0”,则什么也不做
- 如果一行或两行不包含“0/0”,则将两行写入各自的输出
.
输入
sample1.dat
1 1001 0/0:8:8:99:PASS
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 1234 0/0:8:8:99:PASS # not present in sample2
2 2345 1/1:8:8:99:PASS
sample2.dat
1 1001 0/0:8:8:99:PASS
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
2 3456 0/1:8:8:99:PASS # not present in sample1
输出
sample1_out.dat
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 2345 1/1:8:8:99:PASS
sample2_out.dat
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
在这种情况下,不会打印 1-1001,因为它们都有值“0/0”,并且不会打印 2-1234 和 2-3456,因为它们不存在于两个文件中。
一些注意事项:
- 这些文件大约有 260GB,但我可以很容易地将它们分成多个最大 18GB 的文件(我基本上将它们分成染色体)
- 我机器上的可用内存约为 128GB
- 第 1 列和第 2 列已按数字顺序排列在一起
非常感谢任何帮助!
awk
救援!可能你需要先拆分文件,对于每个块你可以这样做
$ function f { awk -v OFS='\t' '{print "~",[=10=]}' ; };
join <(f file1) <(f file2) |
awk -v OFS='\t' '!~/0\/0/ || !~/0\/0/
{print ,, > "file1.out";
print ,, > "file2.out"}'
说明让join
完成匹配相应记录的工作,但需要通过合并前两个字段来创建合成键。输出包含我们需要的所有信息,将结果的相应字段和输出部分中的“0/0”逻辑应用到相应的输出文件。
$ head file{1,2}.out
==> file1.out <==
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 2345 1/1:8:8:99:PASS
==> file2.out <==
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
您可能最好对文件名进行参数化,包括输入和输出。
我正在寻找一种有效的解决方案来过滤两个数据集。基本上,我只想保留一个文件中没有丢失的行作为它们的键列,并且在两个文件中都没有值“0/0”。
输入数据(对于感兴趣的人,我为这个问题简化的基因组 VCF 文件)具有以下特征:
- 第 1 列和第 2 列一起按数字排序,唯一标识符
- 第 3 列以值 0/0、0/1 或 1/1 开头
脚本最好执行以下操作:
- 遍历 sample1.dat 中的每一行并在 sample2.dat 中查找相同的标识符
- 如果 sample1.dat 中的标识符未在 sample2.dat 中找到,则不执行任何操作
- 如果两行都包含“0/0”,则什么也不做
- 如果一行或两行不包含“0/0”,则将两行写入各自的输出 .
输入
sample1.dat
1 1001 0/0:8:8:99:PASS
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 1234 0/0:8:8:99:PASS # not present in sample2
2 2345 1/1:8:8:99:PASS
sample2.dat
1 1001 0/0:8:8:99:PASS
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
2 3456 0/1:8:8:99:PASS # not present in sample1
输出
sample1_out.dat
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 2345 1/1:8:8:99:PASS
sample2_out.dat
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
在这种情况下,不会打印 1-1001,因为它们都有值“0/0”,并且不会打印 2-1234 和 2-3456,因为它们不存在于两个文件中。
一些注意事项:
- 这些文件大约有 260GB,但我可以很容易地将它们分成多个最大 18GB 的文件(我基本上将它们分成染色体)
- 我机器上的可用内存约为 128GB
- 第 1 列和第 2 列已按数字顺序排列在一起
非常感谢任何帮助!
awk
救援!可能你需要先拆分文件,对于每个块你可以这样做
$ function f { awk -v OFS='\t' '{print "~",[=10=]}' ; };
join <(f file1) <(f file2) |
awk -v OFS='\t' '!~/0\/0/ || !~/0\/0/
{print ,, > "file1.out";
print ,, > "file2.out"}'
说明让join
完成匹配相应记录的工作,但需要通过合并前两个字段来创建合成键。输出包含我们需要的所有信息,将结果的相应字段和输出部分中的“0/0”逻辑应用到相应的输出文件。
$ head file{1,2}.out
==> file1.out <==
1 1002 0/0:8:8:99:PASS
1 1003 0/1:5,3:8:99:PASS,PASS
2 2345 1/1:8:8:99:PASS
==> file2.out <==
1 1002 0/1:5,3:8:99:PASS,PASS
1 1003 0/0:8:8:99:PASS
2 2345 1/1:8:8:99:PASS
您可能最好对文件名进行参数化,包括输入和输出。