シミュレーション値を使用して、2 つの列の順序を同時に 1000 回ランダムに変更するにはどうすればよいですか?

シミュレーション値を使用して、2 つの列の順序を同時に 1000 回ランダムに変更するにはどうすればよいですか?

次のようなデータがあります:

    ID    pheno  

    231   0.12     
    432   0.32     
    11    0.0003   
    134   0.33     
    2334  0.553    
    12    0.33     
    100   0.331    
    1008  1.6      
    223  -0.81     
    998  -3.001    

以下のように、ランダムな数字を含む「simulation1」と「simulation2」という名前の 2 つの列を作成する必要があります。

 ID    pheno  simulation1 simulation2

134   0.33     4.11   0.004
2334  0.553    83     0.44
12    0.33     87     0.55
100   0.331    88     12 
231   0.12     0.1    2 
432   0.32     3      30
11    0.0003   3.5    11
1008  1.6      89     23
223  -0.81     113    0.001
998  -3.001    220    982

その後、列「ID」と「pheno」を列「simulation1」の値に従って並べ替え、上位40%の行の「pheno」の平均を計算します。これは0.193 = (0.12+0.32+0.0003+0.33)/4になります。

 ID    pheno  simulation1 simulation2

231   0.12     0.1    2 
432   0.32     3      30
11    0.0003   3.5    11
134   0.33     4.11   0.004
2334  0.553    83     0.44
12    0.33     87     0.55
100   0.331    88     12
1008  1.6      89     23
223  -0.81     113    0.001
998  -3.001    220    982 

次に、列「ID」と「pheno」を列「simulation2」の値に従って並べ替え、上位40%の行の「pheno」の平均を計算します。これは0.1 = (-0.81+0.33+0.553+0.33)/4になります。

ID    pheno  simulation1 simulation2

223  -0.81     113    0.001
134   0.33     4.11   0.004
2334  0.553    83     0.44
12    0.33     87     0.55
231   0.12     0.1    2
11    0.0003   3.5    11
100   0.331    88     12
1008  1.6      89     23
432   0.32     3      30
998  -3.001    220    980

そうすると、異なる並べ替えを使用した上位 40% の平均の差は -0.093 = 0.1 - 0.193 になります。

2 回目のラウンドでは、最初の 2 つの列 (ID と pheno) は一定のままですが、列 "simulation1" と "simulation2" に新しい値を作成する必要があります。Excel で関数 RAND()* を使用して "simulation1" と "simulation2" に値を作成します。ただし、これを 1000 回繰り返すことは不可能です。Linux で必要なループを作成する方法を知りたいです。

最終的な出力は次のようになります。

row_name top40%average_simulate1  top40%average_simulate2  difference 

   1          0.193                    0.1                    -0.093
   2          
   .
   .
   .
   1000   

答え1

いずれの場合も、以下は| column -t出力を視覚的に揃えるために追加されるだけです。

1) 「simulation1」と「simulation2」という名前の2つの列を作成し、その中にランダムな数字を入れる必要があります。

$ cat tst.awk
BEGIN { srand(seed) }
{ print $0, r(), r() }
function r() { return rand() * 100001 / 1000 }

$ awk -f tst.awk file | column -t
231   0.12    85.5574  23.7444
432   0.32    23.558   65.5853
11    0.0003  59.2486  50.3799
134   0.33    27.8248  45.7872
2334  0.553   45.7947  13.1887
12    0.33    51.6042  99.55
100   0.331   88.0281  17.4515
1008  1.6     1.37974  65.5945
223   -0.81   14.6773  97.6476
998   -3.001  87.888   31.97

2) その後、「ID」列と「pheno」列を「simulation1」列の値に従って並べ替えます。

$ awk -f tst.awk file | sort -k3,3n | column -t
1008  1.6     1.37974  65.5945
223   -0.81   14.6773  97.6476
432   0.32    23.558   65.5853
134   0.33    27.8248  45.7872
2334  0.553   45.7947  13.1887
12    0.33    51.6042  99.55
11    0.0003  59.2486  50.3799
231   0.12    85.5574  23.7444
998   -3.001  87.888   31.97
100   0.331   88.0281  17.4515

3) 次に、上位40%の行の「表現型」の平均を計算します。

$ cat tst2.awk
{ vals[NR] = $2 }
END {
    max = NR * 40 / 100
    for (i=1; i<=max; i++) {
        sum += vals[i]
    }
    print sum / max
}

$ awk -f tst.awk file | sort -k3,3n | awk -f tst2.awk
0.36

残りはおわかりいただけると思います。上記では、計算の各段階を追跡しやすくするために、出力が一定になるように、awk にすべての呼び出しで同じシードを与えました。呼び出しごとに異なる乱数を生成するには、 のtst.awk呼び出しを に変更します。awk -v seed="$RANDOM" -f tst.awk

答え2

更新されたスクリプトは、bc数字の先頭の記号とうまく連携しなかったため、 で に変更されましawkdomath

また、shuf固定配列の方が簡単なので、各反復で配列インデックスをシャッフルするために を使用するように変更しました。

#!/bin/bash

function domath {
    #do the math using the 4 indices into the pheno array
    awk '{print ($1+$2+$3+$4)/4}' <<<"${ph[$1]} ${ph[$2]} ${ph[$3]} ${ph[$4]}"
}

function iterate {
    #randomise the indices and get the first 4
    shuf -e 0 1 2 3 4 5 6 7 8 9 | head -n 4
}

#number of iterations
nits=100

#read the pheno values into an array
ph=($(tail -n +3 data | awk '{print $2}'))


echo -e row'\t'sim1'\t'sim2'\t'diff
for (( row=1; row<=$nits; row++ )); do
    #calculate simulation1 
    first=$(printf "%+.3f" $(domath $(iterate)))
    #calculate simulation 2
    second=$(printf "%+.3f" $(domath $(iterate)))
    #calculate the difference
    diff=$(printf "%+.3f" $(awk '{print $2-$1}' <<<"$first $second"))
    #and print
    echo -e $row'\t'$first'\t'$second'\t'$diff
done

関連情報