CSVやTSVのデータを扱っていると、大容量になるとcatなんかでは抽出するのは辛くなってくる。
かと言って、grepで行抽出、awkで列指定しての行抽出となると毎回はちょっとつらいし、複数人にそれをやらせるとブーイングが来そうだ。DBにインポートすればよいのだろうが、それも面倒くさい。。。jsonであればjqコマンドを使えば良いのだけど、そうも言ってられないし、わざわざjson形式に切り替えるのも面倒くさい。

そんな時に使えそうなのが、csvファイルに対してSELECT文などを利用できるコマンド「q」コマンドだ。
これならSQL使えれば簡単に扱えるし、DBも必要なさそう。

1.インストール

インストールは簡単で、こちらを参考に各OSに合わせてバイナリをインストールするだけだ。
今回はCentOS 7でインストールしてみる。

curl -s http://harelba.github.io/q/install.html | grep rpm_download_link | awk -F\" '{print $4}' | xargs rpm -ihv
[root@BS-PUB-CENT7-01 ~]# curl -s http://harelba.github.io/q/install.html | grep rpm_download_link | awk -F\" '{print $4}' | xargs rpm -ihv
https://github.com/harelba/packages-for-q/raw/master/rpms/q-text-as-data-1.5.0-1.noarch.rpm を 取得中
準備しています...              ################################# [100%]
更新中 / インストール中...
   1:q-text-as-data-1.5.0-1           ################################# [100%]

これでインストールができた。

2.実際に使ってみる

さて、それでは実際に使ってみよう。

2-1.普通にselect文を実行する

基本的には、以下のようにコマンドを実行してやれば良い。(カラムは、c○で表現する)

q -d"区切り文字" "SELECT ~ FROM ファイルPATH WHERE ~"

例として、以下のようなCSVファイルを使ってみる。

test1.csv

1,aaa,1,123
2,bbb,2,544
3,ccc,1,65745
4,ddd,1,122
5,eee,2,222
6,fff,1,444
7,ggg,3,566
8,hhh,4,111
9,iii,3,12
10,bbb,2,1243
11,ddd,1,4423
[root@BS-PUB-CENT7-01 ~]# cat /tmp/test1.csv
1,aaa,1,123
2,bbb,2,544
3,ccc,1,65745
4,ddd,1,122
5,eee,2,222
6,fff,1,444
7,ggg,3,566
8,hhh,4,111
9,iii,3,12
10,bbb,2,1243
11,ddd,1,4423
[root@BS-PUB-CENT7-01 ~]# q -d"," "select * from /tmp/test1.csv where c3='2'"
2,bbb,2,544
5,eee,2,222
10,bbb,2,1243
[root@BS-PUB-CENT7-01 ~]# q -d"," "select * from /tmp/test1.csv where c3='2' or c3='4'"
2,bbb,2,544
5,eee,2,222
8,hhh,4,111
10,bbb,2,1243
[root@BS-PUB-CENT7-01 ~]# q -d"," "select count(*) from /tmp/test1.csv where c3='2'"
3

2-2.ヘッダーについてるカラム名を利用する

csvのヘッダーにカラム名がついてる場合、「-H」オプションをつけることでそれをSQL内で利用することができる。

[root@BS-PUB-CENT7-01 ~]# cat /tmp/test1.csv
id,name,prefecture,value
1,aaa,1,123
2,bbb,2,544
3,ccc,1,65745
4,ddd,1,122
5,eee,2,222
6,fff,1,444
7,ggg,3,566
8,hhh,4,111
9,iii,3,12
10,bbb,2,1243
11,ddd,1,4423
[root@BS-PUB-CENT7-01 ~]# q -H -d"," "select * from /tmp/test1.csv where prefecture='2'"
2,bbb,2,544
5,eee,2,222
10,bbb,2,1243

2-3.グループ化して集計を行う

「group by」でsumやavgで集計することも可能だ。

[root@BS-PUB-CENT7-01 ~]# cat /tmp/test1.csv
id,name,prefecture,value
1,aaa,1,123
2,bbb,2,544
3,ccc,1,65745
4,ddd,1,122
5,eee,2,222
6,fff,1,444
7,ggg,3,566
8,hhh,4,111
9,iii,3,12
10,bbb,2,1243
11,ddd,1,4423
[root@BS-PUB-CENT7-01 ~]# q -H -d"," "select prefecture,sum(value) from /tmp/test1.csv group by prefecture"
1,70857
2,2009
3,578
4,111
[root@BS-PUB-CENT7-01 ~]# q -H -d"," "select prefecture,avg(value) from /tmp/test1.csv group by
 prefecture"
1,14171.4
2,669.666666667
3,289.0
4,111.0
[root@BS-PUB-CENT7-01 ~]# q -H -d"," "select prefecture,sum(value),avg(value) from /tmp/test1.c
sv group by prefecture"
1,70857,14171.4
2,2009,669.666666667
3,578,289.0
4,111,111.0
[root@BS-PUB-CENT7-01 ~]# q -H -d"," "select prefecture,count(prefecture),sum(value),avg(value)
 from /tmp/test1.csv group by prefecture"
1,5,70857,14171.4
2,3,2009,669.666666667
3,2,578,289.0
4,1,111,111.0

2-4.csvの結合を行う

「join」も使えるので、CSVを結合して利用することも可能だ。

[root@BS-PUB-CENT7-01 ~]# cat /tmp/test1.csv
id,name,prefecture,value
1,aaa,1,123
2,bbb,2,544
3,ccc,1,65745
4,ddd,1,122
5,eee,2,222
6,fff,1,444
7,ggg,3,566
8,hhh,4,111
9,iii,3,12
10,bbb,2,1243
11,ddd,1,4423
[root@BS-PUB-CENT7-01 ~]# cat /tmp/test2.csv
prefecture,ja_prefecture
1,tokyo
2,kanagawa
3,kyoto
4,osaka
[root@BS-PUB-CENT7-01 ~]# q -H -d "," "
> SELECT  test1.name
>        ,test2.ja_prefecture
>        ,test1.value
> FROM    /tmp/test1.csv as test1 left join /tmp/test2.csv as test2 on test1.prefecture=test2.prefecture"
aaa,tokyo,123
bbb,kanagawa,544
ccc,tokyo,65745
ddd,tokyo,122
eee,kanagawa,222
fff,tokyo,444
ggg,kyoto,566
hhh,osaka,111
iii,kyoto,12
bbb,kanagawa,1243
ddd,tokyo,4423

これだけ使えれば、CSVでのデータ処理もだいぶ楽になるのではなかろうか。
ただまぁ、あまり大きいCSVファイルだと処理に時間がかかるようなので、その場合は素直にDBに入れるなどしてやろう。