列の各部分の中央値を計算する方法を学びます。

列の各部分の中央値を計算する方法を学びます。

次のデータがあります。

111 5
111 6
111 1
222 8
222 9
222 1
222 3
555 9
555 7
555 6

の各値について$1可能であれば、AWKを使用して$2その値のすべての値の中央値を取得したいと思います。$1

希望の出力:

111 5 5
111 6 5
111 1 5
222 8 5.5
222 9 5.5
222 1 5.5
222 3 5.5
555 9 7
555 7 7
555 6 7

$1ここで、5は5、6、1(==値)の中央値111、5.5は8、9、1、3の中央値です。

答え1

すべてのUNIXシステム上のすべてのシェルでsort + awkを使用してください。

$ cat tst.awk
$1 != prev { if (NR>1) prt(); prev=$1 }
{ vals[++cnt] = $2 }
END { prt() }

function prt(   i,med) {
    med = (vals[int((cnt+1)/2)] + vals[int((cnt/2)+1)]) / 2
    for (i=1; i<=cnt; i++) {
        print prev, vals[i], med
    }
    cnt = 0
}

$ sort -k1,1n -k2,2n file | awk -f tst.awk
111 1 5
111 5 5
111 6 5
222 1 5.5
222 3 5.5
222 8 5.5
222 9 5.5
555 6 7
555 7 7
555 9 7

$2上記のコードは、すべての現在の値を名前付き配列$1に保存し、値が変更されたかファイルの終わりに達したときに呼び出してその配列の中央値を計算し、名前付き変数に保存してループに印刷します。 sとのすべての関連追加。vals[]$1prt()med$1$2med

出力行は上記で並べ替えられました。これが問題の場合は、最初に行を飾って元の順序を維持し、上でsort + awkを実行してから元の順序で並べ替え、最後に装飾を取り消すことができます。

GNU awkがあり、キー値がすでにソートされている場合は、関数asort()内で呼び出すことができるので、以前にprt()呼び出す必要はありません。ソートがない場合は、すべてを配列に保存してからEND部分でソートできます。しかし、図に示すように、最初に呼び出すのが最も明確でシンプルで効率的で、移植性に優れています。sortawksort

答え2

データがすでに最初の列でアルファベット順に並んでおり、プロセスの置き換えを理解するシェルを使用しているとします<(...)

$ join file <( datamash -W groupby 1 median 2 <file )
111 5 5
111 6 5
111 1 5
222 8 5.5
222 9 5.5
222 1 5.5
222 3 5.5
555 9 7
555 7 7
555 6 7

コマンド結果を使用して、ファイルの最初のフィールドでリレーショナルJOIN操作を実行します。

datamash -W groupby 1 median 2 <file

このコマンドは、最初のフィールドの値にグループ化された2番目のフィールドの各値セットの中央値を計算します。このオプションを使用すると、GNUに入力をスペースで区切られたものとして扱うことができます-Wdatamash

このタスクの結果は

111     5
222     5.5
555     7

必要な結果を得るには、最初のフィールドの元のデータに関連付けます。

データが最初のフィールドでまだソートされていない場合:

join <( sort -k 1,1n file ) <( datamash -s -W groupby 1 median 2 <file )

最初のフィールドで同じ行が互いに比較的並べ替えられないようにするには、コマンドで次のものを使用していることをsort file確認してください。安定ソートアルゴリズム。ほとんどの実装では、これを行うためにsort非標準オプションを使用できます。-s


プロセス交換のないシェルの場合:

  • データは次のようにソートされます。

    datamash -W groupby 1 median 2 <file | join file -
    
  • データを並べ替える必要があります。

    sort -o file.sorted -k 1,1n file
    datamash -W groupby 1 median 2 <file.sorted | join file.sorted -
    rm -f file.sorted
    

答え3

GNU awk を使用して asort() 関数を実行する場合は、照合するかどうかにかかわらず、入力ファイルを最初の処理で 2 回処理する必要があり、各 Id 値を 1 つにグループ化し、それぞれの中央値を計算します。グループ、2番目にファイルを処理するときに計算された中央値を最後の列として印刷します。idバルク:

awk -v sep=, 'NR==FNR{ id[$1]=($1 in id ? id[$1] sep : "") $2; next }
     FNR==1 {
                for(x in id){
                    ln=split(id[x], tmp, sep); asort(tmp)
                    id[x]=(ln%2? tmp[int(ln/2)+1]: (tmp[ln/2]+ tmp[ln/2+1])/2 )
                }
            }
{ print $0, id[$1] }' infile infile

関連情報