前回に続いてAWKでデータの処理を行います。そして表の体裁にして出力します。
今回もこれまで同様にサンプルで利用するファイル・ディレクトリはデスクトップのsampleディレクトリとしています。デスクトップにsampleディレクトリがない場合は作成しておいてください。(コマンド入力ならmkdir ~/Desktop/sampleとして作成することができます)
また、カレントディレクトリも上記の場所になります。cd ~/Desktop/sampleのようにコマンドを入力してカレントディレクトリを変更しておけばよいでしょう。
○欠損データの処理(1)
まず処理するデータを準備します。これまでは正常に記録されたデータを扱いました。しかし、IoTデバイスなどで計測したデータはデータそのものが記録されていない場合があります。欠損データというやつですが、AWKなどでデータ処理する前に、これらの欠損データをどうにかする必要があります。
ここでは以下の3つのファイルを処理してみます。まず、データは記録されている場合とそうでない場合です。以下のように記録されていない場合は空行だとします。ここで空行だったら無視するか他の値に置き換える方法があります。
ここでは以下のデータが温度だとして空行を他の値に置き換えてみることにします。都合のよいことに温度は絶対零度(-273.15度)より下がることはありません。そこで絶対零度よりも小さい値である-999に空行を置き換えることにします。
err1.txt
6
8
9
6
5
3
-3
-8
AWKでは空行かどうかは処理している行の列の総数でチェックします。列の総数は変数NFに入っています。NF == 0だった場合は空行なので直後の{}の中に処理を書きます。今回の場合は-999なのでprint "-999"としています。数値でなく文字にしたい場合は"~"の中の文字を変更してください。
空行の場合は-999を出力しますが、空行でなくデータがある場合は現状の値を出力する必要があります。今回の場合はデータが1つなので$1としています。$0なら行全体を出力することになります。
まとめると以下のようにすれば空行の場合、-999が出力されデータがある場合は、そのまま出力されることになります。
awk 'NF == 0 { print "-999" } NF > 0 { print $1 }' err1.txt
それぞれ条件を書くのではなくプログラミング言語でよく見られるif...elseを使う方法もあります。
awk '{ if (NF == 0) { print "-999" } else { print $1 } }' err1.txt
○欠損データの処理(2)
次に空行ではなく中途半端に記号だけ記録されてしまった場合や数値ではなくERRなどの文字が記録されている場合の処理です。まず、以下のようにデータが記録されている場合です。
・err2.txt
-1
2
-
10
7
5
3
-2
記号だけでなく空行もあります。このような場合、整数値かどうかで判断すると簡単です。整数値の場合は以下のようにして判定できます。
awk '{ if (NF > 0 && $1 ~ /^-?[0-9]+$/) { print $1 } else { print "-999" } }' err2.txt
文字が記録されている場合もあります。例えば以下のようなデータです。エラーで記録できなかった部分はERRもしくは-ERRとなっています。また、空行も含まれています。
err3.txt
-5
2
-ERR
4
10
ERR
8
-
6
このような場合でも先ほどのコマンドで解決できます。
awk '{ if (NF > 0 && $1 ~ /^-?[0-9]+$/) { print $1 } else { print "-999" } }' err3.txt
ちなみに上記のコマンドのような正規表現についてはChat GPTなどのAIを活用した方が便利です。
欠損データの処理はここまでにして、前回の処理の続きに戻りましょう。
○縦列の平均値
温度の場合、平均値を求めたい場合があります。そこで前回も使用したpi1.txtのデータの平均値を求めてみましょう。pi1.txtのデータは以下のようになっています。
pi1.txt
6
8
10
9
6
5
3
0
-3
-8
ここで必要なのは行数です。データの値を合計して行数で除算すれば平均値を求めることができます。AWKでは処理している行番号は変数NRに入っています。最終行数まで合計した後にNRで除算します。この場合、以下のようになります。
awk '{sum+=$1} END {print sum/NR}' pi1.txt
平均値だけでなく合計値も同時に表示する場合は以下のようになります。
awk '{sum+=$1} END {print sum,":",sum/NR}' pi1.txt
列が1つの場合は簡単ですが、前回のdata.txtのように列数が3つある場合は少し長くなります。列数が固定されているので汎用性に乏しいコマンドです。実行するとそれぞれの列の平均値が表示されます。
data.txt
6 -1 -5
8 2 2
10 7 0
9 10 4
6 9 8
5 10 10
3 7 15
0 5 8
-3 3 3
-8 -2 6
awk '{sum1+=$1;sum2+=$2;sum3+=$3}END{print sum1/NR,":",sum2/NR,":",sum3/NR}' data.txt
実際には平均値だけでなくデータの最後に平均値を追加する形にしたい場合もあります。>> data.txtとして元データに追記する方法もありますが、基本的に元データを変更するのはよくないでしょう。この場合、以下のようにprintでそれぞれのデータを出力してしまえば簡単です。
awk '{sum1+=$1;sum2+=$2;sum3+=$3;print $1,$2,$3}END{print sum1/NR,":",sum2/NR,":",sum3/NR}' data.txt
○CSV形式の処理
ここまではタブ区切りのデータを扱いました。ただ、世の中のデータではCSV形式(カンマ区切り形式)の方がメジャーではないでしょうか。そこで次にデータがCSV形式だった場合の処理を行ってみましょう。なお、CSV形式は単純にカンマ区切りで済むという話にはならないので、ここでは整数値データが、それぞれカンマで区切られているということにします。
AWKには区切り文字を変更することができます。これは変数OFSに区切り文字を指定します。
data.txt内のタブ区切りデータをカンマ区切りにするには以下のように指定します。
awk 'BEGIN { OFS = "," } { print $1, $2, $3 }' data.txt
data.csvとして保存するにはリダイレクトを使います。
awk 'BEGIN { OFS = "," } { print $1, $2, $3 }' data.txt > data.csv
data.csvファイルの内容は以下のようになります。
data.csv
6,-1,-5
8,2,2
10,7,0
9,10,4
6,9,8
5,10,10
3,7,15
0,5,8
-3,3,3
-8,-2,6
それでは先ほどと同様に、このデータを処理してみます。データと平均値を表示するスクリプトは以下のようになっていました。
awk '{sum1+=$1;sum2+=$2;sum3+=$3}END{print sum1/NR,":",sum2/NR,":",sum3/NR}' data.txt
これを以下のようにファイル名だけ変更しても期待通りにはなりません。エラーにもならないので注意しないと正しい結果を得られたと思ってしまう可能性もあります。
awk '{sum1+=$1;sum2+=$2;sum3+=$3}END{print sum1/NR,":",sum2/NR,":",sum3/NR}' data.csv
データの区切り文字を指定する場合はFオプションを使います。-Fの後に区切り文字を指定します。これで期待通りの結果になります。
awk -F , '{sum1+=$1;sum2+=$2;sum3+=$3}END{print sum1/NR,":",sum2/NR,":",sum3/NR}' data.csv
○表にする(HTMLのTable)
最後にデータを表にしてみましょう。1970~1980年代ならテキスト整形した表でも十分でしたが、現在ではさすがにそれは見た目にもイマイチだと思われます。
HTMLのTableタグに関しては説明すると面倒なので、すでに知っているものとして進めます。HTMLに関しては数多くの学習ページ/サイト/書籍がありますので、そちらを参考にしてください。
AWKでTableタグ部分だけを出力するには以下のようになります。他のHTMLデータ(内容)に組み込むような場合は、これで十分でしょう。なお、スタイルはCSSなどで指定すれば、よりよい見た目にすることができます。
awk -F , 'BEGIN { print "" }' data.csv
.