3

単一のユニット 1、2、および 3 で構成される複数列ファイルがあります。各列にはユニットの繰り返しがたくさんあり、時々、別のユニットに切り替わります。この切り替えがすべての列で何回発生するかを数えたいと思います。たとえば、列 1 ではスイッチが 1 から 2 から 3 から 1 に変化するため、3 つのスイッチがあり、出力は 3 になるはずです。 .

私の入力ファイルには 4000 列あるため、手動で行うことはできません。ファイルはスペースで区切られています。

例えば:

入力:

1 2 3 1 2 
1 2 2 1 3
1 2 3 1 2
2 2 2 1 2
2 2 2 1 2    ......
3 2 2 1 2 
3 2 2 1 1
1 2 2 1 1
1 2 2 1 2
1 2 2 1 1

望ましい出力:

3    ## column 1 switch times
0    ## column 2 switch times
3    .....
0    
5    

私が使用していた:

awk '{print $1}' <inputfile> | uniq | wc -l
awk '{print $2}' <inputfile> | uniq | wc -l
awk '{print $3}' <inputfile> | uniq | wc -l
....

これにより、一度に 1 つの列が実行されます。最初の列の出力「4」が得られます。後で、4-1 = 3 を計算して、目的の出力を取得します。しかし、この awk コマンドをループに書き込んで各列で実行し、1 つのファイルに出力する方法はありますか?

ありがとう!

4

3 に答える 3

4

awkは、変数 NF の特定の行にあるフィールドの数を示しているため、必要な情報を追跡するために 2 つの配列を作成できます。1 つの配列は、指定された列の最後の行の値を保持します。もう 1 つは、特定の列のスイッチの数をカウントします。また、列の最大数を追跡します (また、新しい列のカウントをゼロに設定して、その列のスイッチの数が 0 の場合に最後に出力に適切に出力されるようにします)。また、空の文字列から空でない文字列への遷移 (列が初めて検出されたときに発生する) をカウントしないようにします。

実際、ファイルが一様に同じ列数である場合、それはデータの最初の行にのみ影響します。後続の行に実際に最初の行よりも多くの列がある場合は、それらを追加します。列が少し表示されなくなった場合、中断したところから再開する必要があると思いました (欠落している列が以前と同じ値であるかのように)。さまざまなアルゴリズムを決定できます。これは 2 つの遷移としてカウントされる可能性があります (数値から空白へ、および空白から数値へも。その場合は、カウント コードを変更する必要があります。または、より賢明なことに、不規則な列数は単に許可されないと判断することもできます。その場合、現在の行の列数が前の行と同じでない場合は、早期に救済できます (空白行に注意してください。それとも禁止されていますか?)。

また、プログラム全体を 1 行で書き込もうとはしません。それはわかりにくく、実際には必要ないからです。

awk '{   if (NF > maxNF)
         {
             for (i = maxNF + 1; i <= NF; i++)
                 count[i] = 0;
             maxNF = NF;
         }
         for (i = 1; i <= NF; i++)
         {
             if (col[i] != "" && $i != col[i])
                 count[i]++;
             col[i] = $i;
         }
     }
     END {
         for (i = 1; i <= maxNF; i++)
             print count[i];
     }' data-file-with-4000-columns

サンプル データ (ドットを削除) を指定すると、スクリプトからの出力は要求どおりになります。

3
0
3
0
5

ぎざぎざの行を含むこの代替データ ファイル:

1 2 3 1 2
1 2 2 1 3
1 2 3 1 2
2 2 2 1 2
2 2 2 1 2 1 1 1
3 2 2 1 2 2 1
3 2 2 1 1
1 2 2 1 1 2 2 1
1 2 2 1
1 2 2 1 1 3

出力を生成します:

3
0
3
0
3
2
1
0

私が定式化したルールによればどちらが正しいですが、別のルールでデータをカバーすることを決定した場合、別の答えになる可能性があります。

最終ループで使用した場合printf("%d\n", count[i]);、ループでカウント値をゼロに設定する必要はありません。あなたはあなたのお金を払い、あなたの選択を取ります。

于 2013-02-06T01:27:47.930 に答える
2

ループを使用して、各列の現在の値の配列と、対応するcountの別の配列を保持します。

awk '{for(i=0;i<5;i++) if(c[i]!=$(i+1)) {c[i]=$(i+1); t[i]++}} END{for(i=0;i<5;i++)print t[i]-1}' filename

これは、列の値がゼロではないことを前提としていることに注意してください。値がゼロの場合cは、ファイルに存在しない一意の値に配列を初期化してください。

于 2013-02-05T22:27:01.393 に答える
0

見やすくするためにコーディングされていますが、SaveColx、CountColx は配列にする必要があります。少なくともチェックのために、列番号自体を結果に出力します:-)

BEGIN { 
    SaveCol1 = " "
    CountCol1 = 0
    CountCol2 = 0
    CountCol3 = 0
    CountCol4 = 0
    CountCol5 = 0
}
{
if ( SaveCol1 == " " ) {
    SaveCol1 = $1
    SaveCol2 = $2
    SaveCol3 = $3
    SaveCol4 = $4
    SaveCol5 = $5
    next
    }
if ( $1 != SaveCol1 ) {
    CountCol1++
    SaveCol1 = $1
    }
if ( $2 != SaveCol2 ) {
    CountCol2++
    SaveCol2 = $2
    }
if ( $3 != SaveCol3 ) {
    CountCol3++
    SaveCol3 = $3
    }
if ( $4 != SaveCol4 ) {
    CountCol4++
    SaveCol4 = $4
    }
if ( $5 != SaveCol5 ) {
    CountCol5++
    SaveCol5 = $5
    }
}
END {
    print CountCol1
    print CountCol2 
    print CountCol3
    print CountCol4
    print CountCol5
}
于 2013-02-06T01:45:36.073 に答える