JSONファイルが読み込めない場合
import pandas as pd
import numpy as np
import json
pd.read_json("file.json")
でもJSONファイルが読み込めない場合の対策。原因としては色々考えられるが、一番としては、そもそもファイルがJSON形式を満たしていない場合が多いと考えられる。keyがシングルクォーテーションで括られていたり、バックスラッシュが含まれていたり、エラーとなる原因は多岐にわたる。特にファイルに
\\n
\\t
で改行やタブが含まれている場合、エラーが多い印象だ。
JSONを読み込むための対策
時間はかかってしまうが、1行ごとに読み出す。
with open("file.json", encoding = "utf-8") as f:
data = f.readlines()
ここでポイントは、encodingを指定することと、”readlines”(”readline”とすると1行だけ読み込まれてしまう)を用いること。こうやって生成された”data”は1行ごとにJSON形式の配列が読み込まれているので、
data[0]
などとアクセスできるようになる。ここから
new_data = data
for i in range(len(data)):
new_data[i] = data[i].replace("\\r", " ").replace("\\n", " ").replace("null", "0").replace("(", "").replace(")", "").replace("NumberLong", " ").rstrip()
とすれば各行からエラーの原因となる要素を削除できる。
再度DataFrameに戻すとき
こうやって生成された”new_data”を再度pandasのDataFrameやエクセルのcsv形式に戻したいときは、以下のようにする。
df_list = [pd.DataFrame()]*len(data)
for i in range(len(data)):
temp = json.loads(new_data[i])
df_list[i] = pd.json_normalize(temp)
df_new = pd.concat(df_list, axis = 0)
df_new.to_csv("df_new.csv")
順にcodeを解説する。まず、
df_list = [pd.DataFrame()]*len(data)
で”data”の長さだけカラのDataFrameをもつリストを作る。そのリストに対して、
for i in range(len(data)):
temp = json.loads(new_data[i])
df_list[i] = pd.json_normalize(temp)
で余分な情報を削除した”new_data”を代入していく。代入する時に、json.loads(ここでも”json.load”としてはいけない)で一度JSON形式にしてから、pandasのjson_normalizeでデータフレームに渡している。ネストされていないJSONファイルなら、ここはpd.read_jsonでも読み込めると考えられる。最後に、
df_new = pd.concat(df_list, axis = 0)
で一つのDataFrameにまとめている。
不完全なJSONファイル
なぜこのようなことをしているかというと、例えば
{
"id": 1,
"name": "tanaka",
"attribute": {
"gender": "male",
"phone_number": "xxxxxxxxxxx",
"birth": "1991/01/01"
}
}
{
"id": 2,
"name": "yamamoto",
"attribute": {
"gender": "female",
"phone_number": "xxxxxxxxxxx"
}
}
のように、id:1では”birth”情報があるのに、id:2ではない、などといった形式(MongDBなど安全でない言語でファイル出力するとしばしばこのようなことが起こる。”null”や””だけでも吐いてくれれば話はもっと簡単になる)で出力された場合、普通にpd.read_jsonで読み込もうとすると、エラーが吐き出されてしまうためだ。pandasに対してfor loopを回すと、果てしなく時間がかかり、DataFrameを用いる利点がほとんどなくなってしまうが、それでも不完全なJSONファイルを読み込まなければいけない場合、この方法が最も簡単ではある(もっと早い方法はいくらでもある気がする)。
まとめ
今回の記事のきっかけとなったのは、MongoDBベースで作成されたアプリから吐き出されたJSONファイルを、csvにしなくてはいけないという事情があったからだ。Don’t use MongoDBでも触れられているが、MongDBの問題もあるかもしれないが、慣れない形式のファイルを扱うのは大変ということなんだろう。
コメント