3

わかりましたので、問題は、次のように、指定された N 行のリストがあることです。

4.96035894  2.94014535  9.71651378 On
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 On
6.40892867  9.44195654  8.83707549 On
4.26065784  3.74966832  7.89520829 On
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off
...

目的は、3 番目のフィールドで最も低い値を持つ X (X < N) 行を見つけ (任意のフィールドに簡単に拡張できますが、3 番目に焦点を当てましょう)、4 番目のフィールド (常に文字列) を変更することです。ユーザーが呼び出した引数に応じて On/Off に切り替わります。つまり、引数が On の場合は On に変更され、Off の場合は Off に変更されます。

上記の例で、たとえば、3 番目の値が最も低い 3 行をオフに変更したい場合、出力は次のようになります。

4.96035894  2.94014535  9.71651378 On
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 On
6.40892867  9.44195654  8.83707549 Off // this row is changed
4.26065784  3.74966832  7.89520829 Off // this row is changed
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off // this row is changed
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off
...

最小値の行である X=1 の特定のケースについてはできると思いますが、任意の X に拡張する方法がわかりません。リストを調べている間に X サイズの配列がいっぱいになって編集されているのではないでしょうか?

4

4 に答える 4

3

興味深い問題です。これには配列を賢く使う必要があります。

BEGIN {
    if (!x)                           # If x wasn't set using -v default is 3
        x=3
    if (!field)                       # If field wasn't set using -v default is 3
        field=3
}
{
    lines[NR]=$0                                    # Store each line in an array
    sort[NR]=$field                                 # Store the field in an array
    field_a[$field]=$0                              # Line lookup on field 
}
END{
    asort(sort)                                     # Sort the fields  

    for (j=1;j<=NR;j++) {                           # For every line in the file
        for(i=1;i<=x;i++) {                         # For the top x values
            if (lines[j] == field_a[sort[i]]) {     # If current line in top x
                sub(/On/,"Off",lines[j])            # Do the subsitution
                break                               # Grab the next line
            }
        }
        print lines[j]                              # print the line
    }
}

次のようなファイルに保存し、次のscript.awkように実行します。

$ awk -f script.awk file
4.96035894  2.94014535  9.71651378 On
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 On
6.40892867  9.44195654  8.83707549 Off
4.26065784  3.74966832  7.89520829 Off
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off

デフォルトでは、フィールド 3 の最低 3 つの値がオフになりますが、-vオプションを使用してフィールドと値の数の両方を指定できます。たとえば、フィールド 3 の最小 10 個の値をオフにして、最大値のみをオンにします。

$ awk -v x=10 -f script.awk file
4.96035894  2.94014535  9.71651378 Off
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 Off
6.40892867  9.44195654  8.83707549 Off
4.26065784  3.74966832  7.89520829 Off
8.89601431  9.84208918  9.63054539 Off
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off

フィールド2からの最大値はどうですか:

$ awk -v x=10 -v field=2 -f script.awk file
4.96035894  2.94014535  9.71651378 Off
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 Off
6.40892867  9.44195654  8.83707549 Off
4.26065784  3.74966832  7.89520829 Off
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 Off
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off

注:asort()関数の使用には が必要GNU awkです。

于 2013-05-04T12:56:38.107 に答える
2

次のようなものが機能します。

x=3
f=3
awk -v f="$f" '{print $f, NR, $0}' file |
sort -n |
awk -v x="$x" 'NR<=x{sub(/On/,"Off")} {print}' |
sort -k2n |
awk '{sub(/[^ ]+ +[^ ]+ +/,""); print}'

f はソートするフィールド、x はフラグを立てたい最小値の数です。

挿入ソートまたは gawks 組み込みソート関数 asort()/asorti() を使用して awk ですべて実行できますが、上記は単純で、私は怠け者です...

$ x=3; f=3; awk -v f="$f" '{print $f, NR, $0}' file | sort -n | awk -v x="$x" 'NR<=x{sub(/On/,"Off")} {print}' | sort -k2n | awk '{sub(/[^ ]+ +[^ ]+ +/,""); print}'
4.96035894  2.94014535  9.71651378 On
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 On
6.40892867  9.44195654  8.83707549 Off
4.26065784  3.74966832  7.89520829 Off
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off

$ x=4; f=2; awk -v f="$f" '{print $f, NR, $0}' file | sort -n | awk -v x="$x" 'NR<=x{sub(/On/,"Off")} {print}' | sort -k2n | awk '{sub(/[^ ]+ +[^ ]+ +/,""); print}'
4.96035894  2.94014535  9.71651378 Off
8.37470259  9.08139103 10.23145322 Off
5.73085411  4.21656546  9.98718707 Off
6.40892867  9.44195654  8.83707549 On
4.26065784  3.74966832  7.89520829 Off
8.89601431  9.84208918  9.63054539 On
9.10538764  8.58408119 10.87454882 On
6.21494725  4.61164407  9.08378204 Off
7.62256424  9.59449339 10.84506558 Off
6.49210768  4.03768151 10.75221925 Off
5.04079861  4.99362253 10.34349177 Off
于 2013-05-04T12:54:25.413 に答える
1

さらに別のアプローチとして、ファイルを 2 回読み取り、順番に並べます。

awk '
  NR==FNR{
    S[0]=$field
    # sort the value into place
    for(i=1;i<=n;i++){
      if(S[i-1]>S[i]){
        c=S[i-1]
        S[i-1]=S[i]
        S[i]=c
      }
    }
    # shift the highest value into oblivion
    if(NR>n) for(i=n; i>=1; i--) S[i]=S[i-1]
    next
  }
  # Create associative array entries for the values 
  FNR==1 {
    for(i=1;i<=n;i++){
      A[S[i]]
    }
  }
  # if $field is one of the values then change the last field (assuming there are no other fields with value of $NF)
  $field in A {
    sub($NF,"Off")
  }
  1
' n=3 field=3 file file
于 2013-05-04T16:35:53.143 に答える
1

そして別のアプローチ:

n=4
field=3
newval=FOO
# find the line numbers that need to be updated
set -- $(
    cat -n file |
    sort -nk $((++field)),$field |
    awk -v n=$n 'FNR <= n {print $1}'
)
# now, update the value for the specific lines
awk -v val="$newval" -v lines=" $* " 'lines ~ " "FNR" " {$NF = val} 1' file
于 2013-05-04T14:21:33.570 に答える