datamashを使用してすべての列で作業する方法は?

datamashを使用してすべての列で作業する方法は?

次のデータファイルがあるとしましょう。

111 222 333
444 555 666
777 888 999

GNU Datamash を使用して、次のように各列の合計を計算できます。

cat foo | datamash -t\  sum 1 sum 2 sum 3
1332 1665 1998

データファイルの列数がわからない場合は、datamashを使用してこれをどのように実行しますか?

たとえば、フィールドセレクタcutなどの範囲終了記号をサポートしているので、この質問をします。-

答え1

不明な範囲を指定するオプションは表示されません。データ混合ハンドブック

このperl単一のライナーをお試しください

$ perl -lane '$s[$_]+=$F[$_] for 0..$#F; END{print join " ", @s}' ip.txt
1332 1665 1998
  • -aオプションは自動的に入力行をスペースに分割し、結果を@F配列に保存します。
  • for 0..$#F配列を繰り返して$#F最後の要素のインデックスを提供する
  • $s[$_]+=$F[$_]合計を@s配列に保存すると、デフォルトでは初期値は0数値コンテキストにあります。$_各反復にはインデックス値があります。
  • END{print join " ", @s}すべての入力行が処理されたら、@sスペースを区切り文字として使用して配列の内容を印刷します。

答え2

cols=$( awk '{print NF; exit}' foo); cat foo | datamash -t\  sum 1-$cols

または

cat foo | datamash -t\  sum 1-$( awk '{print NF; exit}' foo)

datamash列範囲を指定する機能があるため、列数を計算し、その結果を範囲指定の一部として使用します。私のサンプルソリューションでは、awkファイルの最初の行を確認して終了しましたが、自分に適した他の項目を使用できます。出力に列番号を含むdatamash関数自体がありますが、-checkその形式ではまだ関心のある特定の数値を解析する必要があります。

答え3

よくわかりませんが、解決策datamashは次のとおりですawk

$ awk '{ for( col=1; col<=NF; col++ ) { totals[col]+=$col } } END { for( col=0; col<length(totals); col++ ) {printf "%s ", totals[col]}; printf "\n" } ' input
1332 1665 1998

awkこのスクリプトを読みやすくするには、次の手順を実行します。

{      // execute on all records
  for( col=1; col<=NF; col++ ) { 
    totals[col]+=$col 
  }; 
} 
END {  // execute after all records processed
  for( col=0; col<length(totals); col++ ) {
    printf "%s ", totals[col]
  }; 
  printf "\n";
} 

答え4

使用datamashbash:

n=($(datamash -W check < foo)); datamash -W sum 1-${n[2]} < foo

出力:

1332    1665    1998

仕組み:

  1. datamash -W check < foo出力文字列「3つの行、3つのフィールド」

  2. n=($(datamash -W check < foo))文字列を配列としてロードします$n。私たちはフィールド数、すなわち${n[2]}

  3. datamash -W sum 1-${n[2]} < foo残りは完了しました。


これは次の方法で行うこともできます。POSIXシェルprintfは配列の代わりに複雑な型の文字列を使用しますが、より粗雑です。

datamash -W sum 1-$(printf '%0.0s%0.0s%s%0.0s' $(datamash -W check < foo)) < foo

これはシェルツールを使用して行うこともできます。

datamash -W sum 1-$(head -1 foo | wc -w) < foo

関連情報