web-dev-qa-db-ja.com

同じ列の値に基づいて2つの行をマージする

以下のようなファイルがあります。

47196436 47723284 name1 1.77273

42672249 52856963 name2 1.06061
52856963 430695 name2 1.16667

55094959 380983 name3 1.55613

17926380 55584836 name4 1.02461
3213456 34211 name4 1.11
54321 34211 name4 1.23

最初の2列は、テーブルの主キーに対応しています。同じ名前の場合、すべてのキーが同じ行にあるように行をマージしようとしています。

私は出力を取得しようとしています、

47196436 47723284 name1
42672249 52856963 430695 name2
55094959 380983 name3
17926380 55584836 3213456 34211 54321 name4

以下のコマンドを使用して、部分的にそれを達成することができました。

awk '{ x[$3]=x[$3] " " $2; } 
END { 
   for (k in x) print k,x[k] >"OUTPUT1";  
}' ccc.txt

ただし、出力が正しく表示されません。上記のコマンドをさらに変更するには、いくつかの支援が必要です。

5
Ramesh

Perlソリューション:

$ Perl -ane '$h{$F[2]} .= " ".$F[0]." ".$F[1];
    END {
        for $k (sort keys %h) {
            print $_," " for grep {!$seen{$_}++} split(" ",$h{$k});
            print "$k\n";
        }
    }' file

47196436 47723284 name1
42672249 52856963 430695 name2
55094959 380983 name3
17926380 55584836 3213456 34211 54321 name4
3
cuonglm

ひどく、しかし仕事をするようです

awk '$3 != prev {if (NR != 1) print prev; prev=$3; delete a};
!($1 in a){a[$1]++; printf "%s ", $1};
!($2 in a){a[$2]++; printf "%s ", $2}; 
END {print prev}' ccc.txt
47196436 47723284 name1
42672249 52856963 430695 name2
55094959 380983 name3
17926380 55584836 3213456 34211 54321 name4
3
iruvar

別のPerlアプローチは次のとおりです。

$ Perl -ane 'foreach(@F[0..1]){$k{$F[2]}{$_}++}
           END{
                foreach $v (sort keys(%k)){
                    print "$_ " foreach(keys(%{$k{$v}})); 
                    print "$v\n"
                }; 
            } ' file

これにより、以下が生成されます。

47723284 47196436 name1
42672249 430695 52856963 name2
380983 55094959 name3
34211 55584836 17926380 54321 3213456 name4

説明

確かに、上記のPerlスクリプトは理解しやすいPerlの例ではありません。私は多くのトリックを使用していて、それらはコードを難読化します。私はここで同じソリューションを提示していますが、スクリプトとしてフォーマットされ、より詳細なアプローチを使用しています。

#!/usr/bin/Perl 

## This is the hash that will store our values. 
my %k;

## Read through the input file line by line
## saving each line as $line. This is what the -n
## switch to Perl means, only there each line is saved
## in the special variable $_.
while (my $line=<>) {
    ## Split the line into the @F array. This is
    ## what the -a switch does.
    #chomp($line);
    my @F=split(/\s+/,$line);


    ## Populate the %k hash that we defined at the beginning.
    ## This is a hash of hashes, it looks like this:
    ##   $hash{key1}{key2}=value
    ## In this case, we are saying:
    ##   $hash{3rd field}{1st field}=1 
    ##   $hash{3rd field}{2nd field}=1 
    ## This just serves to add the 1st and 2nd fields
    ## to the list of fields for this $F[2] (the 3rd field, the name).
    ## A side effect of this is that hash keys are unique so duplicates
## are automatically removed.
    $k{$F[2]}{$F[0]}=1;
    $k{$F[2]}{$F[1]}=1;

}

## We have now finished processing the file
## (this is the END{} block above), so let's print.

## This saves the keys of the hash %k in the @names array
## sorted alphabetically.
my @names=(sort keys(%k));


## Go through each of the names, saving
## them as $name
foreach my $name (@names) {
    ## Now, iterate through the values associated 
    ## with the current $name. These are saved as the
    ## keys of the hash %k{$name}
    foreach my $value ( (keys(%{$k{$name}})) ){
      print "$value ";
    } 
    ## Now print the name as well
    print "$name\n";

}

上記のスクリプトは、私が投稿した1つのライナーとまったく同じことを行いますが、より明確な構文を使用するように拡張されています。

2
terdon

gawk >= 4.0を使用してもかまわない場合、これ( terdon's とほぼ同じ)は、オプションの名前とキーの順序で目的の出力を生成します。

NF {
    Names[$3][$1] = 1;
    Names[$3][$2] = 1;
} 
END {
    PROCINFO["sorted_in"] = "@ind_str_asc"; # if you want `Name` ordered
    for (Name in Names) { 
        PROCINFO["sorted_in"] = "@ind_num_asc"; # if you want `Key` ordered
        for (Key in Names[Name]) {
            printf("%s ", Key);
        }
        print Name;
    }
}

与える:

47196436 47723284 name1
430695 42672249 52856963 name2
380983 55094959 name3
34211 54321 3213456 17926380 55584836 name4
0
user61786