見出し画像

【解析Tips】[Linux]コマンドラインで済ませよう

■ Linuxのコマンド

 Linuxは便利コマンドが数多くあるため、コマンドラインで驚くほどの作業ができます。そのためLinuxで作業をする時はマウスなしでキーボードのみで完結できる作業が多いですね。

 そしてLinuxのコマンドはオプションが非常に多く、今までできないと思っていた作業がオプションを入れるだけで簡単にできたりします。大体作業に慣れてくるとコマンドを調べることが少なくなり、いつの間にか「できない」と思ってしまっているケースが多いですが、たまに調べてみると、分かっていたつもりのコマンドも思いもよらぬオプションがあったりして面白いです。

■ コードを書くまでもない作業

 PythonでもFortranでもCでもJavaでも、コードを書くのには少々時間がかかります。今流行りのビッグデータ分析などをしようと思っても、データの前処理が大変で、実際にモデルを組むコードよりもデータの前処理に長い時間を要することはざらにあると思います。

 そこで、ここでは「コードを書かなくても結構な前処理ができる便利コマンド」を紹介していこうと思います。何回かに分けて書くと思うのでご了承ください。

■ データを見る

 だいたい分析用のデータを集めようと思った時は、DBからダウンロードするにしろ、先方からもらうにしろ、ログを引っ張るにしろ、おそらくCSV形式が主流だと思います。APIから引っ張ってきてXMLやJSON形式で集まるデータもあると思いますが、分析用に加工しようと思ったら、RDBに入れるにしろ、DataFrameに入れるにしろ、一度どこかでCSVを踏むと思います。

 そこで、ここでは分析用に使うまとまった量のCSVをもらったけれど中身をどうやって見ていこうか、という状況を想定して、前処理で行う内容をできるだけコマンドラインで済ませるためのTipsを紹介します。少しでも作業時間の短縮になれば幸いです。

■ 最強の便利コマンド2選 : wc -l , uniq -c

 そうは言ってもそれほど多くのコマンドを覚える必要はなく、使いまわしが効くのでご安心ください。個人的に最強のコマンドは、以下の2つです。

[何か色々なコマンド] | wc -l
[何か色々なコマンド] | sort | uniq -c

もうこれだけ覚えて頂ければ色々なことができるので、今日はこれだけ持って帰っていただければ大丈夫です。

 具体的な意味としては、wc -l は行数のカウントです。SQLで言うところのcount(何か)ですね。何かをして、ヒットした行数を数えるときに、パイプで繋いでwc -lを噛ますと良いですよ、という話です。

 2つ目は何か色々なコマンドを打って、sortで並び変えて、uniqで重複をカウント(-c)する、という処理です。処理の間はパイプ(|)で繋いでいるので、実質1行で書くことができます。SQLで言うところのcount(distinct 何か)ですね。

この2つを短歌の下の句のように色々なコマンドにつけることで、かなりの内容が分かります。それではどんなことができるか見ていきましょう。

(1)ファイル数を数える

 店舗別とか日付別にCSVが分かれていたり、ディレクトリの中に何ファイルあるかなどを調べたいときがありますよね。そういう時は、

# ディレクトリ内のファイル数を数える
ls | wc -l

でファイル数が分かります。いやいや階層が深いディレクトリに分かれてて数えにくいねん。という場合は、以下のようにfindと組み合わせるといいと思います。

# ディレクトリ以下にあるCSVファイルのファイル数を数える
find . -name *.csv -type f | wc -l

(2)ファイル毎の行数

 各CSVが何行あるかを数えたいときは、以下のコマンドで一発です。wcコマンドの本来の役割ですね。

# ディレクトリ内のCSVファイルの行数をカウント
wc -l *.csv

 これで0行とか1行のファイルが出てきたりすると、「○○日のデータが欠測みたいなんですけど・・・」のようなデータに関する質問を投げることができますね。

(3)ファイル毎の列数

 列数を数えたい場合は、AWKを使いましょう。だいたいCSVファイルって同じファイル内で列数は揃っているはずなのですが、異常値が入ってたり、途中から変な値が入ってたり、CSVと書かれていながらスペース区切りだったり、”商品名とか地点名にカンマ(,)が入っていたり”することが多々あり、きれいなデータであることは稀です。そういう時は、以下のようにすることであぶり出せます。

# file_name.csvの列数をカウントする。
awk -F"," '{print NF}' file_name.csv | sort | uniq -c

 このコマンドのリターンは、「行数 列数」で返ってくるため、例えば17列で100行のCSVがあれば「100   17」のように1行だけ返ってきます。

 その中に異常な行があると、uniqの結果が分かれて、以下のような感じで複数行で返ってきます。これにより17列の行が97行あって、何故か18列ある行が2行あって、1列しか入っていない行が1行ある、という情報が分かります。

#行数 列数
97  17
 2  18
 1   1

 だいたい同じフォーマットで送られてきていれば、たとえファイルが分かれていようが同じ列数なハズなので、1ファイルの中で列数が複数あるファイルは何か異常があると捉えることができます。すると、そもそも先ほどのリターンが1行でなければおかしいので、最後に行数をカウントするwcを噛ませば、以下のような形で全体の中からおかしなファイルを探すことができます。

for file in `ls *.csv` # すべてのCSVファイル
do
    cnt=`awk -F"," '{print NF}' $file | sort | uniq -c | wc -l` 
    echo $file, $cnt  # ファイル名 , 1ファイル内の列数の種類
done

 こうすると、「ファイル名, 1」みたいな結果がずらーっと並んで、何か中身が変なファイルがあると「ファイル名, 3」みたいな表示をされるので一目瞭然で特定できます。

(4)文字コード

 だいたい同じフォーマットで集められたファイルというのは同じ文字コードで入っているはずなんですが、実際はそうもいかない事例もあります。そういう場合は、nkfとawkを組み合わせて炙り出しましょう。

# 文字コードを調べて、文字コード部分だけを抜いて、重複をカウントする
nkf -g *.csv | awk -F":" '{print $2}' | sort | uniq -c

 ちょっと中盤のawkが分かりづらいかもしれませんが、nkf -g では、以下のようなリターンが来ます。これをコロン区切りと捉えて2フィールド目を取り出すことで、文字コードの部分のみを抜き出すことができますね。

file_1.csv : UTF-8
file_2.csv : UTF-8
  :
file_N.csv : Shift_JIS

 それをsort | uniq -cで重複カウントすれば、以下のようなリターンになって、「2ファイルだけShift_JISが混ざっとるやんけごるぁ!」というブチ切れポイントを作り出すことができます。(^-^)

 98  UTF-8
  2  Shift_JIS

(5)異常値

 ここまでのフィルターを潜り抜けてきたファイルは優等生です。もう後はRDBなりpandasなりで不自由なく分析できることでしょう。その前に念には念を入れて、異常値のチェックをしておきましょうか。大体異常値は-9999とかNaNとかで入っている場合が多いので、そいつらを引っかけてカウントするのが一番手っ取り早いですね。grepを使いましょう。

# 9999が入った行数をカウント
grep 9999 file_name.csv | wc -l

 これで0だったらめでたしめでたし。超優良ファイルですね。いや~助かる。逆にこれで先ほど(2)で求めた行数と一致しちゃったりすると、ファイルの中身が全欠測かもしれないし、どこか1列が全部欠測になっているかもしれないです。

・・・こんな感じで、データをもらってから分析にかけるまで、ざっと確認しておきたい事項は結構コマンドラインでサクッとできます。今回はuniq -cとwc -lが主役でしたが、その他も便利なコマンドが沢山あるので、また後日紹介していこうと思います。では、快適なLinuxライフを☆


この記事が気に入ったらサポートをしてみませんか?