jqコマンドでは、JSONファイルでの集計処理(キーの数を数えたり、合計や平均、最大値や最小値を出したり)をすることが出来る。
なお、今回は以下のようなJSONファイルをサンプルに進めていく。
● sample1.json
[
{
"id": "0001",
"name": "test001",
"value": 112,
"group1": {
"subg01": [
{
"id": "1001",
"type": "type001"
},
{
"id": "1004",
"type": "type002"
}
]
},
"group2": [
{
"id": "5001",
"type": "None"
},
{
"id": "5003",
"type": "type053"
},
{
"id": "5004",
"type": "type054"
}
]
},
{
"id": "0002",
"name": "test002",
"value": 954,
"group1": {
"subg01": [
{
"id": "1021",
"type": "type101"
},
{
"id": "1054",
"type": "type052"
}
]
},
"group2": [
{
"id": "5001",
"type": "None"
},
{
"id": "5004",
"type": "type054"
}
]
}
]
● sample3.json
[
{
"id": 1,
"value": 1243,
"type": "A"
},
{
"id": 2,
"value": 24365,
"type": "B"
},
{
"id": 3,
"value": 201,
"type": "B"
},
{
"id": 4,
"value": 5465,
"type": "C"
},
{
"id": 5,
"value": 24,
"type": "A"
},
{
"id": 6,
"value": 54750,
"type": "B"
},
{
"id": 7,
"value": 3547,
"type": "B"
}
]
1.キーの数を求める
'length'を用いる事で、配列内のキー数の数をカウントすることが出来る。
[root@BS-PUB-CENT7-02 ~]# # 指定した配列内のキー数を取得する
[root@BS-PUB-CENT7-02 ~]# jq -c '.[] | select(.id == "0002") | .group1.subg01[1]' /tmp/sample1.json
{"id":"1054","type":"type052"}
[root@BS-PUB-CENT7-02 ~]# jq -c '.[] | select(.id == "0002") | .group1.subg01[1] | length' /tmp/sample1.json
2
[root@BS-PUB-CENT7-02 ~]#
[root@BS-PUB-CENT7-02 ~]# # 配列でないキーの数をカウントしたい場合は、length前の抽出文を[]で囲って配列として扱わせる
[root@BS-PUB-CENT7-02 ~]# jq -c '.[] | select((.value >= 1000 ) and (.type == "B") | not) | .id' /tmp/sample3.json
1
3
4
5
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | select((.value >= 1000 ) and (.type == "B") | not) | .id]' /tmp/sample3.json
[1,3,4,5]
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | select((.value >= 1000 ) and (.type == "B") | not) | .id] | length' /tmp/sample3.json
4
この時、group_byを利用することで要素ごとの数を求める事も可能だ。
[root@BS-PUB-CENT7-02 ~]# # 「type」のそれぞれの数を求めたいとする
[root@BS-PUB-CENT7-02 ~]# jq -c '.[] | .type' /tmp/sample3.json
"A"
"B"
"B"
"C"
"A"
"B"
"B"
[root@BS-PUB-CENT7-02 ~]# # 一度配列にしてやる必要がある
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .type]' /tmp/sample3.json
["A","B","B","C","A","B","B"]
[root@BS-PUB-CENT7-02 ~]# # 「group_by」で値ごとに配列を分ける
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .type] | group_by(.)' /tmp/sample3.json
[["A","A"],["B","B","B","B"],["C"]]
[root@BS-PUB-CENT7-02 ~]# # mapで配列ごとにカウントする
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .type] | group_by(.) | map({(.[0]): length})' /tmp/sample3.json
[{"A":2},{"B":4},{"C":1}]
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .type] | group_by(.) | map({(.[0]): length}) | add' /tmp/sample3.json
{"A":2,"B":4,"C":1}
2.キーの値の合計を求める
'add'で、配列内の値を合計することが出来る。
[root@BS-PUB-CENT7-02 ~]# # 集計したい条件を指定
[root@BS-PUB-CENT7-02 ~]# jq -c '.[] | select((.value >= 1000 ) and (.type == "B")) | .value' /tmp/sample3.json
24365
54750
3547
[root@BS-PUB-CENT7-02 ~]# # 1回配列にする
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | select((.value >= 1000 ) and (.type == "B")) | .value]' /tmp/sample3.json
[24365,54750,3547]
[root@BS-PUB-CENT7-02 ~]# # 合計値を取得
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | select((.value >= 1000 ) and (.type == "B")) | .value] | add' /tmp/sample3.json
82662
グループごとに合計を算出することもできる。
以下の例では、「type」の値に応じて合計値を算出する。
[root@BS-PUB-CENT7-02 ~]# # 最初に、group_byでtypeごとに配列を分ける
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)' /tmp/sample3.json
[[{"id":1,"value":1243,"type":"A"},{"id":5,"value":24,"type":"A"}],[{"id":2,"value":24365,"type":"B"},{"id":3,"value":201,"type":"B"},{"id":6,"value":54750,"type":"B"},{"id":7,"value":3547,"type":"B"}],[{"id":4,"value":5465,"type":"C"}]]
[root@BS-PUB-CENT7-02 ~]# # mapで必要な情報だけを抽出する(この段階だと配列の数がvalueの数の2乗になる)
``` [root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type) | map( {(.[].type): (.[].value)} )' /tmp/sample3.json
\[{"A":1243},{"A":24},{"A":1243},{"A":24},{"B":24365},{"B":201},{"B":54750},{"B":3547},{"B":24365},{"B":201},{"B":54750},{"B":3547},{"B":24365},{"B":201},{"B":54750},{"B":3547},{"B":24365},{"B":201},{"B":54750},{"B":3547},{"C":5465}\] \[root@BS-PUB-CENT7-02 ~\]# # reduceで認識した順に足す処理を追記する(配列の数だけ重複した集計が出力される)
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): (reduce .[].value as $value (0; . + $value))})' /tmp/sample3.json
\[{"A":1267},{"A":1267},{"B":82863},{"B":82863},{"B":82863},{"B":82863},{"C":5465}\] \[root@BS-PUB-CENT7-02 ~\]# # ユニークな値のみを抽出する
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): (reduce .[].value as $value (0; . + $value))}) | unique' /tmp/sample3.json
\[{"A":1267},{"B":82863},{"C":5465}\] \[root@BS-PUB-CENT7-02 ~\]# jq -c 'group_by(.type)| map({(.[].type): (reduce .[].value as $value (0; . + $value))}) | unique | add' /tmp/sample3.json
{"A":1267,"B":82863,"C":5465}
[root@BS-PUB-CENT7-02 ~]#
[root@BS-PUB-CENT7-02 ~]# # 配列に突っ込む方式もある(こっちの方がキレイ)
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): ([.[].value]|add)}) | unique' /tmp/sample3.json
[{"A":1267},{"B":82863},{"C":5465}]
# 3.平均を求める
上の合計・キーの数を求めるやり方を利用することで、平均を求める事が出来る。
```shell
[root@BS-PUB-CENT7-02 ~]# # 合計(add),個数(length)を求める
[root@BS-PUB-CENT7-02 ~]# jq '[.[] | select((.value >= 1000 ) and (.type == "B")) | .value] | add,length' /tmp/sample3.json
82662
3
[root@BS-PUB-CENT7-02 ~]#
[root@BS-PUB-CENT7-02 ~]# # 合計(add)/個数(length)で平均を求める
[root@BS-PUB-CENT7-02 ~]# jq '[.[] | select((.value >= 1000 ) and (.type == "B")) | .value] | add/length' /tmp/sample3.json
27554</pre>
もちろん、グループごとの平均値を求める事も可能だ。
```shell
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): (reduce .[].value as $value (0; . + $value)/length)}) | unique' /tmp/sample3.json
[{"A":633.5},{"B":20715.75},{"C":5465}]
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): ([.[].value]|add/length)}) | unique' /tmp/sample3.json
[{"A":633.5},{"B":20715.75},{"C":5465}]
4.最大値・最小値を求める
最大値を求める場合は、「max」「max_by」を、最小値を求める場合は「min」「min_by」を用いる。
[root@BS-PUB-CENT7-02 ~]# # 配列内の最大・最小値を求める
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .value]' /tmp/sample3.json
[1243,24365,201,5465,24,54750,3547]
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .value] | max' /tmp/sample3.json
54750
[root@BS-PUB-CENT7-02 ~]# jq -c '[.[] | .value] | min' /tmp/sample3.json
24
[root@BS-PUB-CENT7-02 ~]#
[root@BS-PUB-CENT7-02 ~]# # 最大・最小値を持つ配列を抽出する
[root@BS-PUB-CENT7-02 ~]# jq -c 'max_by(.value)' /tmp/sample3.json
{"id":6,"value":54750,"type":"B"}
[root@BS-PUB-CENT7-02 ~]# jq -c 'min_by(.value)' /tmp/sample3.json
{"id":5,"value":24,"type":"A"}
グループごとでの最大値・最小値を求める
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): ([.[].value]|max)}) | unique' /tmp/sample3.json
[{"A":1243},{"B":54750},{"C":5465}]
[root@BS-PUB-CENT7-02 ~]# jq -c 'group_by(.type)| map({(.[].type): ([.[].value]|min)}) | unique' /tmp/sample3.json
[{"A":24},{"B":201},{"C":5465}]