前回 は、JSON ファイルを扱う方法をまとめました。
今回は、JSONファイルを扱うCUIツール「jq」について紹介します。
はじめに
前回の記事です。
daisuke20240310.hatenablog.com
e-Stat からダウンロードした「令和2年国勢調査の男女別人口の JSONファイル」を使います。
jqのインストール
「jq」のインストール方法として、Ubuntu 22.04 の場合と、Windows10 の Git BASH で使う場合の2通りを紹介します。
Ubuntu 22.04
とても簡単にインストールできます。
$ sudo apt install jq
Windows10のGit BASHでjqを使えるようにする
まず、Windows10 用の「jq」をダウンロードします。
「jq」の GitHub です。
github.com
リリースページ から、最新の Windows 用の exe(jq-win64.exe)をダウンロードします。
隠れてるかもしれないので、「Show all 29 assets」をクリックして、全て表示して、ダウンロードします。
適当な場所に置いて、コマンドプロンプトからも使えると思いますが、今回は、Git BASH で使えるように、ファイルを配置します。
Git BASH は、Git for Windows をインストールすると一緒に入るコンソールです。
Git for Windows をインストールしたフォルダ「C:\Program Files\Git」の下に「usr/bin」ディレクトリがあるので、そこに jq-win64.exe をコピーして、jq.exe にリネームしておきます。
Git BASH から使ってみます。
無事使えるようになりました。
jqの使い方
公式のドキュメントです。
jqlang.github.io
「jq」の基本的な使い方は、以下の2通りで、通常のコマンドのように、「jq フィルタ JSONファイル」の形で実行するか、パイプでつないでフィルタとして使うかです。
後者は、「.」を省略することが出来るようです。ここでは、以降は、後者の使い方とします。
$ jq . getStatsData.json
$ cat getStatsData.json | jq .
ちなみに、「.」は、ドキュメントによると、何もしないフィルタという感じで、受け取ったものを全て出力するという感じです。
大きいファイルを扱う場合は、less コマンドなどを入れておいた方がいいですね。
$ cat getStatsData.json | jq | less
jqの基本的な使い方
e-Stat のデータは、最初にしては難しいので、簡単なデータを用意しました。
{
"ka0": {
"ka1": "va1",
"kb1": "vb1"
},
"kb0": [
{ "kc1": "vc1" },
{ "kd1": "vd1" }
]
}
sample1.json として、保存しておきます。
まずは、何もしないフィルタを使います。
$ cat sample1.json | jq .
{
"ka0": {
"ka1": "va1",
"kb1": "vb1"
},
"kb0": [
{
"kc1": "vc1"
},
{
"kd1": "vd1"
}
]
}
少し整形されて出力されました。
JSON ファイルは、基本的に、キーと値のオブジェクト(ハッシュとも言う)の形式と、配列の形式しかありません。
この sample1.json は、最上位階層がオブジェクト形式になっています。
オブジェクトのキーを全て表示する方法です。配列になって出力されました。
$ cat sample1.json | jq keys
[
"ka0",
"kb0"
]
先頭のキーだけを選択し、その後、その中のキーを選択してみます。「.」に続いて、キーを続けます。値を取り出すことが出来ています。
$ cat sample1.json | jq .ka0
{
"ka1": "va1",
"kb1": "vb1"
}
$ cat sample1.json | jq .ka0.ka1
"va1"
続いて、配列について扱い方です。まず、2番目のキーを指定して配列を取り出し、その後、配列の中身を取り出します。さらに、配列の要素の2番目を指定してみます。
$ cat sample1.json | jq .kb0
[
{
"kc1": "vc1"
},
{
"kd1": "vd1"
}
]
$ cat sample1.json | jq .kb0[]
{
"kc1": "vc1"
}
{
"kd1": "vd1"
}
$ cat sample1.json | jq .kb0[1]
{
"kd1": "vd1"
}
基本的な扱い方は以上になります。
jqで複雑なデータを扱う
では、e-Stat のデータを扱っていきます。
まずは、オブジェクト形式と想定して、キー一覧を出します。キーが1つだったので、次の階層のキー一覧を出します。
さらに、GUI ツールで見て "STATISTICAL_DATA" が一番大きいデータと分かっているので、そこのキー一覧を出します。
GUI ツールと見え方が変わるとややこしいので、ソートしないキー一覧も使っていきます。
$ cat getStatsData.json | jq keys
[
"GET_STATS_DATA"
]
$ cat getStatsData.json | jq .GET_STATS_DATA | jq keys_unsorted
[
"RESULT",
"PARAMETER",
"STATISTICAL_DATA"
]
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq keys_unsorted
[
"RESULT_INF",
"TABLE_INF",
"CLASS_INF",
"DATA_INF"
]
大きい配列(CLASS)を見てみます。配列やオブジェクトの個数を調べるには、length を使います。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .CLASS_INF.CLASS_OBJ[2] | jq .CLASS | jq length
4086
もう1つの大きい配列(DATA)を見てみます。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE | jq length
12258
CLASS の方のデータの先頭の3つです。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .CLASS_INF.CLASS_OBJ[2] | jq .CLASS[:3]
[
{
"@code": "00000",
"@name": "全国",
"@level": "1"
},
{
"@code": "01000",
"@name": "北海道",
"@level": "2",
"@parentCode": "00000"
},
{
"@code": "01100",
"@name": "札幌市",
"@level": "4",
"@parentCode": "01000"
}
]
jqの組み込み関数を使う
map(f) 関数を使ってみます。f は任意のフィルタです。入力のオブジェクト、または、配列に対して、まとめてフィルタを適用することが出来ます。
@name
だけ抜き出して、見てみます。最後の「.CLASS」の出力は、4086個の配列です。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .CLASS_INF.CLASS_OBJ[2] | jq .CLASS | jq 'map(."@name")'
[
"全国",
"北海道",
"札幌市",
"札幌市中央区",
"札幌市北区",
"札幌市東区",
"札幌市白石区",
"札幌市豊平区",
"札幌市南区",
"札幌市西区",
"札幌市厚別区",
"札幌市手稲区",
"札幌市清田区",
"函館市",
(省略)
次は、uniqe 関数を使ってみます。
DATA の方の先頭3つです。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE[:3]
[
{
"@tab": "2020_01",
"@cat01": "0",
"@area": "00000",
"@time": "2020000000",
"@unit": "人",
"$": "126146099"
},
{
"@tab": "2020_01",
"@cat01": "0",
"@area": "01000",
"@time": "2020000000",
"@unit": "人",
"$": "5224614"
},
{
"@tab": "2020_01",
"@cat01": "0",
"@area": "01100",
"@time": "2020000000",
"@unit": "人",
"$": "1973395"
}
]
"@tab" が全て同じなのかを確認したいときなどに、unique 関数を使います。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE | jq 'map(."@tab")' | jq unique
[
"2020_01"
]
全て同じ値だったようです。
次は、select 関数を使ってみます。条件にマッチする項目だけが表示されます。
人口が1億以上のものを求めます。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE | jq 'map(."$")' | jq .[] | jq 'tonumber' | jq 'select(. > 100000000)'
jq: error (at <stdin>:725): Invalid numeric literal at EOF at line 1, column 1 (while parsing '-')
jq: error (at <stdin>:4811): Invalid numeric literal at EOF at line 1, column 1 (while parsing '-')
jq: error (at <stdin>:8897): Invalid numeric literal at EOF at line 1, column 1 (while parsing '-')
126146099
いくつかエラーが出ています。調べてみると、人口のところが -
になってるところが3か所ありました。ちょっと強引ですが、-
を取り除いておきます。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE | jq 'map(."$")' | jq .[] | jq 'select(. != "-")' | jq 'tonumber' | jq 'select(. > 100000000)'
126146099
1つしかなかったので、1千万人以上に変更します。
$ cat getStatsData.json | jq .GET_STATS_DATA | jq .STATISTICAL_DATA | jq .DATA_INF.VALUE | jq 'map(."$")' | jq .[] | jq 'select(. != "-")' | jq 'tonumber' | jq 'select(. > 10000000)'
126146099
14047594
61349581
64796518
4つになりました。
おわりに
今回は、CUI で JSON ファイルを扱う方法として、「jq」コマンドを紹介しました。
ちょっと難しいコマンドなので、もう少し使い込んでいきたいと思います。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。