모든 개별 그룹 항목 간의 쌍을 이루는 값 목록을 기반으로 그룹을 할당합니다.

모든 개별 그룹 항목 간의 쌍을 이루는 값 목록을 기반으로 그룹을 할당합니다.

쌍을 이루는 항목을 표시하는 두 개의 열이 있는 탭으로 구분된 파일이 있습니다 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나는 또한 awk 버전과 같이 해시(연관 배열)를 사용하는 Perl 버전을 작성했습니다.그리고@pairs각 그룹의 쌍을 유지 하려면 배열의 배열 배열(AoA - 즉, 각 요소가 다른 배열의 배열임)을 호출합니다 . 결과를 읽은 대로 인쇄하는 대신, 읽은 후 모든 입력을 인쇄합니다.

#!/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 시스템의 모든 쉘에서 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에서 group01then b은 2행에도 나타나므로 마찬가지로 나타나야 합니다. group01즉 , 3행에서는 c이미 2행 에 있으므로 3행 전체( )도 와 연관되어 있습니다 .group01cgroup01dgroup01

관련 정보