jqコマンドでJSONのキーの集計(キーの数・合計・平均・最大・最小等を求める)を行う
Pocket

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}
Sponsored Links

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.平均を求める

上の合計・キーの数を求めるやり方を利用することで、平均を求める事が出来る。

[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

 

もちろん、グループごとの平均値を求める事も可能だ。

[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}]

 

Pocket

Written by blacknon

インフラ系のSE。一時期はプログラマ。 仮想化とオープンソースに興味あり。一日中寝てたい今日このごろ。 スペインとかで働きたいなぁ…(シエスタがあるので)

Leave a Comment

メールアドレスが公開されることはありません。