根据所有单独组条目之间的配对值列表分配组

根据所有单独组条目之间的配对值列表分配组

我有一个制表符分隔的文件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 行bgroup01,thenb也出现在第 2 行上,因此也应该是,group01这意味着c在 中group01,然后在第 3 行上c已经在group01第 2 行中,因此整个第 3 行(也如此d)与 关联group01

相关内容