この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Pandas を使って JSON をごにょごにょする機会があったので使った Tips をまとめておきます。
URLで取得するJSONのイメージ
下記の JSON が https://example.com/sample.json
で取れるとします。
{
"elements": [
{
"id": "001",
"name": "Sample Name 1",
"tags": [
"sample",
"test"
],
"enabled": true,
"registeredAt": "2022-04-01T00:00:00Z",
"updatedAt": "2022-04-03T09:04:10Z"
},
{
"id": "002",
"name": "Sample Name 2",
"tags": [
"sample",
"test"
],
"enabled": true,
"registeredAt": "2022-03-31T00:00:00Z",
"updatedAt": "2022-04-04T12:00:00Z"
},
{
"id": "003",
"name": "Sample Name 3",
"tags": [
"sample",
"test"
],
"enabled": true,
"registeredAt": "2022-04-05T00:00:00Z",
"updatedAt": "2022-04-08T08:15:21Z"
}
]
}
実装
この JSON を Python で読み込んで、登録日( registeredAt
)が実行日の月初( 本記事の執筆時点では 2022/04/01
)以降の要素だけ取り出すこと考えます。
環境には aurl
コマンドと jq
コマンドがインストール済みであるとします。
実装コードは次の通りです。
import pandas as pd
import subprocess
import datetime
from dateutil.relativedelta import relativedelta
# 月初の日付を取得
today = datetime.date.today()
beginning_of_the_month = today + relativedelta(day=1)
# JSON の elements の配列を取得
json = subprocess.getoutput("aurl 'https://example.com/sample.json' | jq -r '.elements'")
# Pandas で絞り込んで出力
df = pd.read_json(json)
df_filtered = df[["id", "name", "tags", "enabled", "registeredAt"]].copy()
df_filtered['registeredAtDatetime'] = pd.to_datetime(df['registeredAt'])
print(df_filtered[df_filtered['registeredAtDatetime'].dt.date >= beginning_of_the_month])
実行結果は次のようになります。
id name tags enabled registeredAt registeredAtDatetime
0 1 Sample Name 1 [sample, test] True 2022-04-01T00:00:00Z 2022-04-01 00:00:00+00:00
2 3 Sample Name 3 [sample, test] True 2022-04-05T00:00:00Z 2022-04-05 00:00:00+00:00
登録日が 2022/03/31
である Sample Name 2
の要素がフィルタされて消えているのがわかります。
このコードのポイントは次の通りです。
月初の日付の取得
月初の日付は today
を取得した上で、 dateutil.relativedelta
を使うことで簡単に取得できます。
today = datetime.date.today()
beginning_of_the_month = today + relativedelta(day=1)
コマンドの実行結果の取得
シェルコマンドの実行結果の取得は subprocess.getoutput( "command's string" )
を使うのが簡単です。
サンプルでは aurl で JSON を取得した結果をパイプして jq に渡し、 elements 要素だけ取り出しています。
json = subprocess.getoutput("aurl 'https://example.com/sample.json' | jq -r '.elements'")
Pandas で取得するフィールドの設定
df = pd.read_json(json)
によって、 JSON から DataFrame が作成できるので、取得した DataFrame に対して、取得したいフィールド名を与えます。
また、 copy()
メソッドで元の DataFrame とは別個の DataFrame を明示的に生成しています。
明示的に生成しておかないと、後続の操作で SettingWithCopyWarning
が発生します。
参考) DataFrameでSettingWithCopyWarningの意味と対処法 - Qiita
df_filtered = df[["id", "name", "tags", "enabled", "registeredAt"]].copy()
日付で結果を絞り込む
registeredAt
カラムの値を元に datetime64
型の値を生成し、フィールド registeredAtDatetime
にセットします。
さらに、フィルター条件で df_filtered['registeredAtDatetime'].dt.date
とすることで datetime.date
型に変換します。
これで、 datetime.date
型の月初の日付( beginning_of_the_month
) との比較が可能になり、目的の絞り込みが行えます。
df_filtered['registeredAtDatetime'] = pd.to_datetime(df['registeredAt'])
print(df_filtered[df_filtered['registeredAtDatetime'].dt.date >= beginning_of_the_month])
JSON 形式で結果を返す
最後の結果を JSON で返したい場合は下記のようにします。
print(df_filtered[df_filtered['registeredAtDatetime'].dt.date >= beginning_of_the_month].to_json(orient='records', indent=2))
indent
を指定すると結果を整形してくれます。
結果は次のようになります。
[
{
"id":1,
"name":"Sample Name 1",
"tags":[
"sample",
"test"
],
"enabled":true,
"registeredAt":"2022-04-01T00:00:00Z",
"registeredAtDatetime":1648771200000
},
{
"id":3,
"name":"Sample Name 3",
"tags":[
"sample",
"test"
],
"enabled":true,
"registeredAt":"2022-04-05T00:00:00Z",
"registeredAtDatetime":1649116800000
}
]