前書き
Qiita初投稿です。よろしくお願いします。
シェルスクリプトを使って面倒な仕事をささっと片付けよう!
シェルスクリプトは主にちょっとした仕事を短時間で実装し、結果を得るのに向いています。
今回はよく行う「ユニーク数を数える処理」を早く実装し、早く結果を出そうと思います。
使用したマシンのスペック等
SSD 512GB
CentOS7
シェル: bash
CPU: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
サンプルデータ
テキストファイルからMeCab+NEologdで抽出した地名。(MeCabが地名と判断したもの)
4億行のtextファイル!
$ wc -l extracted_geo.txt # 行数 (= 抽出された地名の数)
488139214 extracted_geo.txt
$ head -n 30 extracted_geo.txt # ファイルの中身(先頭30行)
カレー
中東
岩井
日光
横浜
ベネチア
静岡
大学前
玉取
ケンタッキー
沖縄
函館
広橋
三橋
一日
東京
大阪
バッキンガム
三上
石川
8号
秋葉原
新台
サンセイ
東京
ムリ
福岡
関東
和歌山
関西
$ du -m extracted_geo.txt # データの大きさ(MB)
4037 extracted_geo.txt
余談ですが、MeCab( http://taku910.github.io/mecab/ )とは日本語の形態素解析器であり、日本語の文章を与えると、品詞を解析してくれたり、分かち書きをしてくれたりする優れものです。
NEologdは、多数のWeb上の言語資源から得た新語を追加することでカスタマイズした MeCab 用のシステム辞書です。詳しくは下記URLを参照してください。
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md
地名が何種類含まれているか知りたい
サンプルファイルには地名が重複して入っています。
このファイル中に何種類の地名(ユニーク数)が含まれているか、調べてみましょう。
何も考えずにスクリプトを組むと、
$ time cat extracted_geo.txt | sort | uniq | wc -l
^C
real 108m14.580s
user 108m5.770s
sys 0m1.845s
のようになりますが、これは100分経っても終わりませんでした。ちーん
高速化
2バイト文字に対応したために、sortコマンドが遅くなってしまっているらしいので、英語環境で実行しました。
コードの意味を日本語で表すなら、「(英語環境で)extracted_geo.txtの中身を標準出力し、行方向にソートして、同じものが続くなら一つにしたあと、行数を数える」という具合でしょうか。
単純に英語環境にしてみた結果、爆速になりました!
$ time cat extracted_geo.txt | LANG=C sort | uniq | wc -l
554332
real 3m36.962s
user 3m47.305s
sys 0m10.531s
引数にファイル名を与えるとさらに高速化!
user timeが増えていることから、並列化が有効になったと推測されます。
$ time LANG=C sort extracted_geo.txt | uniq | wc -l
554332
real 3m5.450s
user 8m43.576s
sys 0m6.934s
上記のコマンドはstable sortが無効になっていたので有効にしてみました。
最終形態!
$ time LANG=C sort -s extracted_geo.txt | uniq | wc -l
554332
real 2m59.063s
user 8m35.422s
sys 0m6.600s
ちょっと高速化!
標準入力で、データを与えるのではなく、引数にファイル名を与えてsortするときに、top -H で実行の様子を見たら、並列化されていることがわかりました。
のべ4.8億件の地名があったのに、55万程度しか種類がありませんでした。
sortは一般的に計算量がO(nlogn)だが、約4.8億行のソートに3分程度しかかかりませんでした。
結論
sortコマンドを使う時は、特に必要がなければLANG=Cを先頭につけましょう!
余談等
今回の処理ではわざわざsortをする必要はないですが、実装が楽(数秒で済む)なので上記のようなスクリプトを書きました。データの統計を短い実装時間で求めたいときにはシェルスクリプトをつかうことを考えてもいいとおもいます。
データ量がさらに多い場合や、何度も繰り返しする処理であるならば、シェルスクリプトを書かずに高速な言語でプログラミングをした方がいいと思います。
シェルスクリプト(シェル芸)に限った話ではありませんが、使い所を考えることは大事ですね。
間違い等あればご指摘よろしくお願いします。