特定の列から重複フィールドを削除する

特定の列から重複フィールドを削除する

特定の列(例では$ 2)から重複フィールド(カンマ区切り)を削除したいと思います。

入力ファイル:

A    1,2,3,4   
B    4,5,6,3
C    2,15

予想出力:

A    1,2,3,4
B    5,6
C    15

答え1

perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e'

上記のコードは次のように実行できます。

$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile 
A    1,2,3,4
B    5,6
C    15

どのように動作しますか?

を初めて呼び出すperlと、-lpe次の3つのことが行われます。

  • -l[octal] 行末処理を有効にして行終端を指定します。
  • -p -n のような繰り返しを仮定し、sed のようなライン印刷も仮定します。
  • -e program 1行プログラム(複数の-eを許可、プログラムファイルを省略)

これは本質的にファイルをインポートし、改行文字を削除し、1行で動作し、操作が完了すると改行文字を追加します。したがって、ファイルを繰り返しながら、各ファイルに対してPerlコードを順番に実行します。

実際のPerlコードは次のとおりです。

  • \s 空白文字(たとえば、最新バージョンでは[ \f\n\r\t]5文字)を表します。\vperl[[:space:]]
  • \K 内容を\Kの左側に保ち、$&に含めないでください。
  • \S+ セットにない 1 つ以上の文字 [\f\n\r\t\v]

結果が収集され、join ",",各フィールドがコンマで区切られるように再結合されます。

split ",", $&で見つかった一致を取得し、カンマ\S+なしでフィールドに分割します。

各フィールドの番号を取得し、grep {!$seen{$_}++}ハッシュに追加します。$seen{}ここで、各フィールドの番号は、$_各フィールドを繰り返すときの番号です。フィールド番号が「表示」されるたびに++演算子で計算されます$seen{$_}++

grep{!$seen{$_}++}フィールド値が一度だけ発生すると、フィールド値が返されます。

編集して何が起こるかを確認してください

この修正された嫌悪機能を使用すると、Perlコードの1行がファイルから複数行に移動したときに何が起こるかを確認できます。

$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile 
keys: 4 1 3 2 | vals: 1 1 1 1
A    1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B    5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C    15

$seen{}これは、処理されたファイルの行末に何があるかを示しています。ファイルの2行目を見てみましょう。

B    4,5,6,3

以下は、次のように行を表示する修正版です。

keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1

つまり、フィールド#6(1回)、フィールド#4(2回)など、フィールド#5(1回)を確認したという意味です。したがって、grep{...}結果が返されたときにその配列の結果がこの行(4、5、6、3)に表示され、1回(6,1,15,5)のみが表示される場合、その配列の結果のみが返されます。これら2つのリストの交差は(5,6)であり、これが返されますgrep

引用する

答え2

{ 
    tr -cs '0-9ABC' '[\n*]' | 
    nl -w1 -s: |
    sort -t: -uk2,2 | sort -t: -k1,1n |
    sed 's/[^:]*://;/^[ABC]/!{H;$!d
        };x;y/\n/,/;s/,/\t/'

} <<\FILE
A       1,2,3,4
B       4,5,6,3
C       2,15
FILE

基本的には、データが実行されsort -u転送されたときと比較的同じ形式で返されることを確認するためにいくつかの手順を実行します。

First は、tr数値ではなく入力のすべてのバイトをABC改行に変換し、すべての反復を圧縮します。したがって、各フィールドには独自の行があります。

nl次に、入力の各行に番号を付けます。出力は次のとおりです。

1:A
2:1
3:2

...など。

次に、データをsort2回編集します。最初はsortすべての重複フィールドを削除します。各行の2番目のデータフィールドでのみ機能し、それをに置き換えます。 2番目は、:今回最初のフィールド(番号付きシーケンス)を指定してsort元の順序を復元します。nlsort

最後に、sedすべてをもう一度合わせてください。入力を収集し、すべてのnl挿入を削除し、xABC$最後の行の保留およびモードバッファを変更します。各行のyすべてのewlineをコンマ\nに変換し、各行の最初の行を最後のaに置き換えます。,<tab>

結果はどうですか?

出力

A       1,2,3,4
B       5,6
C       15

友達の皆さん、これがまさにUnixの美しさです。

そして気になるので、個々のsedアイテムは次のようになります。

sed 's/\t\(.*\)/,\1,/;H;$!d;x;:t
     s/,\([^,]*,\)\(.*,\)\1/,\1\2/;tt
     s/,\(\n.\),/\1\t/g;s/,\(.*\),/\t\1/'
' <<\FILE
A       1,2,3,4
B       4,5,6,3
C       2,15
FILE

1行目と3行目は準備/整理と同じです。まず、ファイル全体をパターン空間にインポートし、各フィールドがコンマで区切られていることを確認します。たとえば、最初の行は次のようになります。

A,1,2,3,4,

通り過ぎるとこんな姿です。

2行目はすべての作業が完了する場所です。これは実際に再帰交換機能です。フィールドが見つからなくなるまで、他の場所でのみ一致できるコンマ区切りフィールドを引き続き置き換えます。これがestコマンドが実行する操作ですt

私が言ったように、3行目はすべてを整理します。カンマやタブなどを元の位置に戻し、最終結果は次のとおりです。

出力

A       1,2,3,4
B       5,6
C       15

面白いですが、かなりの量のデータに対してこれを行うのはコンピュータ殺人です。これは再帰正規表現で、おそらく最悪の再帰型である可能性があります。したがって、わずか数行のコードでこの種の操作を実行することは可能ですが、このようにスプレッドシートを作成するのはおそらく賢明ではありません。

答え3

それでawk使いやすいarraysplit、と同様、loop:

{
    split($2, elements, ",")
    out = ""
    for (i in elements) {
        el = elements[i]
        if (!(el in used)) {
            out = out el ","
        }
        used[el] = 1
    }
    sub(/,$/, "", out)
    $2 = out
}
1

各行に対して、2番目の列をコンマで区切り、ビットを配列に格納しますelements。次に、ループを使用してその列の新しい値を作成し、以前にその値を見たことがないかどうかを確認します。 (関連)配列から既に見た値のセットを保持しますused。以前に見たことがある場合は、el in used出力に入れないでください。それ以外の場合は新しいものであるため、out再使用しないように接続してこの値のセットに追加します。最後に設定されたリストをもう一度2番目の列に入れます。これは本質的に他の言語で取られるアプローチです。

上記のコードをファイルに入れるか、awk -f単一引用符を使用してコマンドラインから引数としてすべて実行します。

関連情報