我有一个制表符分隔的文件pairs.tsv
,其中有两列显示配对条目。根据两列中的两个值,我想创建第三列,将组分配给条目。
然而,在某些情况下,两个以上的条目属于同一组。因此,脚本应从第 1 行开始并分配group01
给第一对,然后检查其余行是否出现第 1 列或第 2 列中的两个值中的任何一个,如果为 true,也分配group01
给该行。
应重复此步骤,直到通过迭代检测对附属于的文件中的所有条目都group01
被分配给group01
。
然后脚本应继续执行下一行,如果尚未分配组,则将其分配给group02
,然后再次检查剩余文件,第 2 行中第 1 或第 2 列的条目是否出现在下面的任何行中,如果是也分配group02
给该行。等等。
pairs.tsv
:
a b
c d
e f
e g
h i
h j
k l
f g
m n
i j
输出文件应如下所示:
a b group01
c d group02
e f group03
e g group03
h i group04
h j group04
k l group05
f g group03
m n group06
i j group04
答案1
您只需要通过输入文件一次即可完成此操作:
awk -F'\t' '{
# "groups" is an associative array containing the group numbers
# for the values in fields $1 and $2.
if (! ($1 in groups)) {
# "gc" stands for "group counter"
groups[$1] = ++gc;
}
groups[$2] = groups[$1]
printf "%s\t%s\tgroup%02i\n", $1, $2, groups[$1];
}' pairs.tsv
a b group01
c d group02
e f group03
e g group03
h i group04
h j group04
k l group05
f g group03
m n group06
i j group04
%groups
我还编写了一个 Perl 版本,它使用像 awk 版本一样的哈希(关联数组) ,和调用一个数组的数组(AoA - 即每个元素都是另一个数组的数组)来@pairs
保存每个组的对。它不是在读入结果时打印出结果,而是在读取所有输入后将它们全部打印出来:
#!/usr/bin/perl
use strict;
my $gc = 1; # group counter
my %groups; # hash containing group numbers for each element
my @pairs; # array of arrays containing pairs
while(<>) {
chomp;
my ($a,$b) = split /\t/;
$groups{$a} = $gc++ unless (defined($groups{$a}));
$groups{$b} = $groups{$a};
push @{ $pairs[$groups{$a}] }, [ $a, $b ];
};
END {
for my $g (keys @pairs) {
for my $p (@{ $pairs[$g] }) {
printf "%s\t%s\tgroup%02i\n", @$p[0], @$p[1], $g;
}
};
}
因为它迭代 @pairs 数组,所以输出按组号排序:
$ ./group.pl pairs.tsv
a b group01
c d group02
e f group03
e g group03
f g group03
h i group04
h j group04
i j group04
k l group05
m n group06
除了排序之外,两个版本的输出是相同的。
答案2
基于对您的需求的一种可能的解释,并在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
grp = ""
for ( i=1; i<=NF; i++ ) {
if ( $i in val2grp) {
grp = val2grp[$i]
break
}
}
if ( grp == "" ) {
grp = ++grps
}
for ( i=1; i<=NF; i++ ) {
if ( !($i in val2grp) ) {
val2grp[$i] = grp
}
}
printf "%s%sgroup%02d\n", $0, OFS, grp
}
$ awk -f tst.awk pairs.tsv
a b group01
c d group02
e f group03
e g group03
h i group04
h j group04
k l group05
f g group03
m n group06
i j group04
不过,考虑一下这个输入:
$ cat pairs2.tsv
a b
c b
d c
以下是否是预期输出:
$ awk -f tst.awk pairs2.tsv
a b group01
c b group01
d c group01
如果没有,为什么不呢?在第 1 行b
上group01
,thenb
也出现在第 2 行上,因此也应该是,group01
这意味着c
在 中group01
,然后在第 3 行上c
已经在group01
第 2 行中,因此整个第 3 行(也如此d
)与 关联group01
。