[Tips] Python で URL で指定した JSON を読み込んで日付型で絞り込む ( Pandas 使用 )
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 } ]