如何比较两个文件来创建第三个文件,其中所有可能的值对都对应于第一个文件的一行



我有两个文件。第一个包含n列的文件file_A.txt如下所示:

SRK_00017   MRS10904.1
SRK_00017   MRS14430.1
SRK_00005   MRS13611.1
... 

第二个文件file_B.txt看起来像:

MRS10904.1  DMN02113.10
MRS10983.1  DMN07690.11
SRK_00011   DMN02311.14
MRS13611.1  DMN12833.2
MRS10981.1  DMN00149.23
SRK_00011   DMN02872.13
MRS14430.1  DMN12777.2
SRK_00005   DMN00659.13
SRK_04765   DMN12781.2
SRK_04765   DMN03028.10
MRS13611.1  DMN1234.10
SRK_00017   DMN03028.10
SRK_00017   DMN03029.10
SRK_14211   DMN13843.1
SRK_00017   DMN00069.20
MRS10904.1  DMN00659.13
....

我想创建一个新的第三个文件file_C.txt,如:

SRK_00017       DMN03028.10     DMN02113.10 MRS10904.1
SRK_00017       DMN03029.10     DMN02113.10 MRS10904.1
SRK_00017       DMN00069.20     DMN02113.10 MRS10904.1
SRK_00017       DMN03028.10     DMN02113.10 MRS10904.1
SRK_00017       DMN03029.10     DMN02113.10 MRS10904.1
SRK_00017       DMN00069.20     DMN02113.10 MRS10904.1
SRK_00017       DMN03028.10     DMN12777.2 MRS14430.1
SRK_00017       DMN03028.10     DMN12777.2 MRS14430.1
SRK_00017       DMN00069.20     DMN12777.2 MRS14430.1
SRK_00005       DMN00659.13     DMN12833.2  MRS13611.1
SRK_00005       DMN00659.13     DMN1234.10  MRS13611.1
....

第二个文件包含ID SRK_和MRS的值(如DMN..)。所有SRK_都在file_A的couln1中,所有MRS都在file_A的Couln2中。SRK_和MRS中的任何一个可以具有多个DMN值(在File_B中给出)。在我的File_C中,我真正想要的是SRK_和MRS的值的所有可能配对都属于File_A的一行。例如,File_A中的第一行是SRK_00017 MRS10904.1,如果我们看File_B,SRK_00011有三个DMN值(DMN03028.10、DMN03029.10、DMNO0069.20),MRS10904.也有两个DMN数值(DMN02113.10和DMN00659.13)。因此,对于File_A的第一行,将有3*2=6对可能的DMN。结果File_C.txt的前六行中有。我希望我能传达我的问题。

使用awk

awk 'NR==FNR{a[$1]++;b[$1 FS a[$1]]=$2;next}
{for (i=1;i<=a[$2];i++)
for (j=1;j<=a[$1];j++)
print $1,b[$1 FS j],b[$2 FS i],$2
}' File_B.txt File_A.txt
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1

在Gnu Awk 4.1版中,您可以使用类似的数组

gawk 'NR==FNR {
a[$1][$2]++
next
}
{
for (i in a[$1])
for (j in a[$2])
print $1, i, j, $2
}' File_B.txt File_A.txt 

输出:

SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1

这是一个Perl版本。

注意:为了清楚起见,我省略了任何错误检查(不确定这里的政策是什么)。

open my $file_a, '<', 'File_A.txt';
open my $file_b, '<', 'File_B.txt';
open my $file_c, '>', 'File_C.txt';
my %map;
foreach (<$file_b>) {
chomp;
my ($id,$dmn) = split;
push(@{ $map{$id} },$dmn);
}
foreach (<$file_a>) {
chomp;
my ($srk,$mrs) = split;
for my $dmn2 ( @{ $map{$mrs} } ) {
for my $dmn1 ( @{ $map{$srk} } ) {
print $file_c "$srk $dmn1 $dmn2 $mrsn";
}
}
}
close $file_a;
close $file_b;
close $file_c;

File_C.txt包含:

SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1

这是一个shell脚本版本,使用sortjoin。这适用于我在ubuntu 12.04和osx:

#!/bin/bash
sort $2 > $2.MRS.SRK
sort $1 > $1.SRK
join $1.SRK $2.MRS.SRK | sed 's/^([^ ]*) +/1:/' | sort > SRK.join
sort -k2 $1 > $1.MRS
join -o "1.1 1.2 2.2" -1 2 $1.MRS $2.MRS.SRK | sed 's/^([^ ]*) +/1:/' | sort > MRS.join
join SRK.join MRS.join | sed 's/([^ ]*):([^ ]*) ([^ ]* [^ ]*)/1 3 2/'

输出为:

ubuntu@ubuntu:~$/DMN.sh文件_A.txt文件_B.txtSRK_00005 DMN00659.13 DMN1234.10 MRS13611.1SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1SRK_0017 DMN00069.20 DMN00659.13 MRS10904.1SRK_0017 DMN00069.20 DMN02113.10 MRS10904.1SRK_0017 DMN03028.10 DMN00659.13 MRS10904.1SRK_0017 DMN03028.10 DMN02113.10 MRS10904.1SRK_0017 DMN03029.10 DMN00659.13 MRS10904.1SRK_0017 DMN03029.10 DMN02113.10 MRS10904.1SRK_0017 DMN00069.20 dmn2777.2 MRS14430.1SRK_0017 DMN03028.10 dmn2777.2 MRS14430.1SRK_0017 DMN03029.10 dmn2777.2 MRS14430.1ubuntu@ubuntu:~$

忽略排序,基本流程为:

  1. 两个文件的字段1上的join("SRK"字段)。确保字段输出顺序为"SRK MRS DMN">
  2. sed是结果输出中"SRK"one_answers"MRS"字段之间的冒号,因此这两个字段对于未来的joins显示为一个字段
  3. 文件A的字段2和文件B的字段1("MRS"字段)上的join。确保字段输出顺序为"SRK MRS DMN">
  4. sed是结果输出中"SRK"one_answers"MRS"字段之间的冒号,因此这两个字段对于未来的joins显示为一个字段
  5. CCD_ 9结果为2。和4。上面的"SRK:MRS"字段
  6. sed删除冒号并将生成的输出字段按顺序移动为"SRK DMN DMN MRS">

注意,join的输入必须在联接字段上排序,因此在这里的每个join之前实际上都需要排序。因此,不能保证最终输出的排序顺序。但我认为,对于任何类型的DB加入来说,通常都是这样。


这也可以全部重写为一个(长)命令,不需要中间文件,并使用bash基元而不是sed:

join <(
join <(
sort File_A.txt) <(
sort File_B.txt) |
while read a b; do echo "$a:$b"; done | sort) <(
join -o "1.1 1.2 2.2" -1 2 <(
sort -k2 File_A.txt) <(
sort File_B.txt) |
while read a b; do echo "$a:$b"; done | sort) |
while IFS=" :" read a b c d; do echo "$a $c $d $b"; done > File_C.txt

注意,join只能从stdin读取其一个输入文件,因此<()进程替换被广泛用于将输入管道传输到join

还要注意,两个输入文件都必须有效地读取两次。

相关内容

  • 没有找到相关文章

最新更新