テキスト処理
はじめに
テキスト形式は、各種の測定データや統計データを表現するのによく用いられる。また、なんらかの統計解析や時系列変化などの解析結果はテキストデータとして処理される。さらに、膨大な数のデータファイルを効率的に処理するには、ファイル名の自動識別・自動変換などの処理が必要だが、ファイル名自体はテキストデータである。
したがって、テキストデータの処理は様々な解析処理において非常に重要である。
ワイルドカード
多くのファイル名を扱う場合に、特定の文字や文字列のグループを、ある記号で代用させることで、複数のファイル名を一括して指定できる。そのような記号を「ワイルドカード」と呼ぶ。
主なワイルドカードに、*と?がある。
例:
*.txt ... 拡張子が.txtであるようなすべてのファイル test.* ... 名前がtest.で始まる全てのファイル test?.txt ... testではじまり、.txtで終り、間に1文字が挿入されているファイル名。
課題: ここから、 "MODIS_work.tar.gzというファイルをダウンロードし(右クリックして「名前を付けてリンク先を保存」)、
$ tar zxvf MODIS_work.tar.gz
として解凍・展開せよ。
注: 拡張子.gzは、gzipというタイプの圧縮、.tarは複数のファイルをひとまとめにした「アーカイブ」を表す。.tar.gzというふうに2つを組み合わせて使う場合がとても多い。その解凍/展開法としてtar zxvfというコマンドは、必ず覚えること。
その後、ディレクトリMODIS_workに移動せよ。
課題: このなかで、.datという拡張子をもつファイルのみをリストアップせよ。
注: このMODIS_work.tar.gzは、MODISという衛星データで、本州南東部の2004年4月〜10月の半年間を解析するためにダウンロードし、解析したファイル群である(ここでは名前だけ。中身は空にしてある。)
grep
grepは、テキストを検索して、マッチングした行を表示させるコマンドである。対象がファイル名の場合は、ワイルドカードを使うことで、grep を使わなくともある程度の検索ができるが、grepには、「特定の文字列を含まない」というような否定的検索もできるので便利である。また、grepはテキストファイルの内容を対象にすることができるし、標準入力を対象にすることもできる(パイプやリダイレクトを使用できる)。
例:
$ grep hdf readme.txt ... readme.txtというファイルの中から、hdfという文字列を含む行を抜き出す。 $ cat readme.txt | grep hdf ... 上と同じ機能の別表現。 $ grep -v hdf readme.txt ... readme.txtというファイルの中から、hdfという文字列を含まない行を抜き出す。
課題: ここから、 MODIS_archive.gzというファイルをダウンロードし、
$ gunzip MODIS_archive.gz
によって解凍せよ。
注: 文字化けしてうまくいかないときは, 以下のコマンドでダウンロードせよ:
$ wget http://pen.agbi.tsukuba.ac.jp/~torarimon/data4download/MODIS_archive.gz
注: tarアーカイブのかかっていないgzip圧縮のみのファイルは、gunzipコマンドで解凍できる。これも覚えておくこと。
課題: MODIS_archiveの内容を表示せよ。これは、MODISという衛星データをNASAのデータセンターに注文した結果、NASAから返って来たメールであり、サーバーに準備されたファイルの一覧が載っている。
課題: このテキストファイルの中から、FILENAMEという文字列を含んだ行を抜き出せ(これがサーバーに準備されたファイルの名前である)。
課題: このテキストファイルの中から、FILENAMEという文字列を含むが、.metという文字列を含まない行を抜き出せ。(.metは説明ファイルなのでとりあえず邪魔なので消して表示したい)
wc
wcは、テキストデータ(テキストファイルまたは標準入力)の行数・語数・バイト数を表示するコマンドである。
例:
$ ls | wc ... カレントディレクトリ内のファイルとディレクトリの数を表示。
課題: 上のMODIS_archiveの内容で、FILENAMEという文字列を含むが.metという文字列を含まない行は何行あるか? (これが、実質的にダウンロードすべきファイルの個数になる)
awk
awkは、テキストデータの1行づつに対して処理をする、プログラミング言語(スクリプト言語)である。コンパイルは必要ない。UNIXのコマンドラインの出力に、パイプでつないで実行できる。
awkは列を抜き出す!
例: ファイルのリスト(ls -lの出力)をawkで加工してみよう。たとえばls -lの出力が下記のようであったとする:
$ ls -l -rw-r--r-- 1 nishida nishida 802 Dec 27 15:08 IJ_Prefs.txt -rw-r--r-- 1 nishida nishida 238197 Jan 6 15:22 ame_master.pdf -rw-r--r-- 1 nishida nishida 816064 Jan 6 15:47 wg3ts.pdf -rw-r--r-- 1 nishida nishida 2527691 Jan 6 15:21 tebiki.pdf -rw-r--r-- 1 nishida nishida 5019689 Jan 6 15:36 haichi.pdf
このとき、ファイルサイズとファイル名のみを抜き出して表示してみよう。
$ ls -l | awk '{print $5,$NF}' 802 IJ_Prefs.txt 238197 ame_master.pdf 816064 wg3ts.pdf 2527691 tebiki.pdf 5019689 haichi.pdf
ここで、$5は「5列目のデータ」の意味。NFは、各行のフィールド数(列の数)。$NFは、「NF列目のデータ」、つまり、その行の最後の列のデータ、ということ。
awkは計算もする!
例: 例1の出力で、ファイルサイズを1024で割る、つまりキロバイト単位で表示する。例1で打ったコマンドを、キーボードの「上向き矢印」のキーで読み出して、ちょっとだけ変更すればよい(/1024を書き加える):
$ ls -l | awk '{print $5/1024,$NF}' 0.783203 IJ_Prefs.txt 232.614 ame_master.pdf 796.938 wg3ts.pdf 2468.45 tebiki.pdf 4902.04 haichi.pdf
awkは条件分岐もできる!
例: 例1の出力で、1000バイト以上のファイルについてのみ処理する:
$ ls -l | awk '1000<=$5{print $5,$NF}' 238197 ame_master.pdf 816064 wg3ts.pdf 2527691 tebiki.pdf 5019689 haichi.pdf
例: 例1の出力で、 ファイル名の末尾が「txt」のものだけ処理する:
$ ls -l | awk '$NF ~ /txt$/{print $5,$NF}'
802 IJ_Prefs.txt
注:/と/で囲まれたところに指定した条件にマッチするものを処理せよという意味。条件の中では、$は列を示すのではなく、末尾を示す記号となっている。
これらの例を見れば、awkの文法は、なんとなく直観でわかるだろう。実際、awkは極めて直観的な言語である。これだけわかれば、もうawkのポテンシャルを発揮できる。以下、実際のデータをいじってみよう。
データ列のならべかえ
以下のデータは、5つの生態観測サイトの、経度・緯度・名前を順に並べたものである:
137.42 36.14 Takayama 141.51 42.73 Tomakomai 138.8 35.45 Fuji-Yoshida 130.71 33.13 Kahoku 140.10 36.10 TERC
これが、site.txtという名前のテキストファイルになっているとしよう。
これを、あるアプリケーションに渡すために、名前・緯度・経度の順に並べ替える必要があるなら、このようにする:
$ cat site.txt | awk '{print $3,$2,$1}' Takayama 36.14 137.42 Tomakomai 42.73 141.51 Fuji-Yoshida 35.45 138.8 Kahoku 33.13 130.71 TERC 36.10 140.10
極めて簡単である。「並べ替えくらいエクセルでもカットペーストでできる」と思う人、あなたは甘い。エクセルで扱うことができるのは、65536行までのデータである。しかし、awkなら、データが数万行にわたっても問題なく走る。もっと大切なことは、後述するが、awkの処理はシェルスクリプトに書き込むことができる、ということである。
この出力を、アルファベット順に並べたいとする。その場合、UNIXのsortコマンドと組み合わせて、
$ cat site.txt | awk '{print $3,$2,$1}' | sort Fuji-Yoshida 35.45 138.8 Kahoku 33.13 130.71 TERC 36.10 140.10 Takayama 36.14 137.42 Tomakomai 42.73 141.51
課題: 上のMODIS_archiveの内容を参照して、NASAのサーバーからダウンロードすべきデータの大きさは合計何バイトになるか計算しよう。
ヒント:
- まず、MODIS_archiveの内容をすべて表示せよ(catを使う)。
- それにパイプをつないで、grepを使ってFILESIZEという文字列の入っている行だけ抽出せよ。
- 求めたいのは、FILESIZEという文字の右にある数字(それが各ファイルのサイズ)の合計である。上のコマンドにさらにパイプをつないで、awkを使ってファイルサイズの合計を求めよ。
解答例: cat MODIS_archive | grep FILESIZE | awk '{a=a+$2}END{print a}'
awkは極めて強力な解析ツールである。awkを使いこなすことができれば、Excelのような表計算ソフトに頼らないで、高速・大量・複雑な解析処理を行うことができる。GRASSのさまざまな解析ツールの出力も、awkで加工・二次処理することで、スムーズな処理の流れ(つまり自動化可能な処理)にすることができる。
なんでもそうだが、何かのデータの解析の仕上げはグラフ化である。gnuplotという、テキストデータ対応型・スクリプト型のグラフツールを使うことで、解析ソフト → awk → gnuplotという処理の流れがきれいにできあがる。その間をつなぐのが、シェルおよびシェルスクリプトである。
追記
このようなファイル操作ができるよう、データファイルに名前をつけるときには工夫するべきである。例えば、2008_0623_Tsukuba_Temp.txtなどのように、データをとった時刻・場所・種類・拡張子がはっきりするように名前をつけると、grepやawkなどで処理しやすくなる。(筑波大、本岡)
参考
- シェルスクリプトとawkによるデータ解析
- 正規表現
<Unix/Linux入門に戻る>
Keyword(s):
References:[2018_実用解析] [Unix/Linux入門]