質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.39%
JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

1回答

254閲覧

Pythonでnp.nanを含むデータをjsonにうまくエンコードできない

ttuucch

総合スコア1

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2024/08/15 02:19

編集2024/08/15 04:49

実現したいこと

DataFrameで保有しているデータをjsonにencodeしたいと考えています。
ただし、実際には少し複雑なデータ構造になるため、pandasのto_jsonは使わないという前提になります。

発生している問題・分からないこと

test用のデータとして次を用意します。

・データ

python

1df=pd.DataFrame({'a':[0,1,2,3,4],'b':[None,6,7,8,np.nan], 2 'c':[10,11,np.nan,np.nan,None],'d':[15,16,17,None,19], 3 'e':[np.nan,np.nan,np.nan,np.nan,np.nan]})

これを次のfor文で回しながらdictにして、最後にjson.dumpsでencodeするというプロセスです。ただし、このやり方だとnp.nanやほかのnp.int64などのデータ型もencodeのじゃまになるというところまではわかったので、エンコーダを用意することにしました。なお、pythonのバージョンは3.11.4です。

これのテスト用コードがこちらです。

python

1class NumpyEncoder(json.JSONEncoder): 2 def default(self, obj): 3 4 if isinstance(obj, np.int64): 5 return int(obj) 6 elif isinstance(obj, np.float64): 7 if np.isnan(obj): 8 return None 9 else: 10 return float(obj) 11 elif isinstance(obj, np.ndarray): 12 return obj.tolist() 13 14 return super().default(obj) 15 16dic=dict() 17for i,row in enumerate(df.index): 18 for j,col in enumerate(df.columns): 19 dic[f'{col}-{row}']=df.iloc[i,j] 20print(json.dumps(dic,indent=4,cls=NumpyEncoder))

printされて出てきたものが下です。

python

1{ 2 "a-0": 0, 3 "b-0": NaN, 4 "c-0": 10.0, 5 "d-0": 15.0, 6 "e-0": NaN, 7 "a-1": 1, 8 "b-1": 6.0, 9 "c-1": 11.0, 10 "d-1": 16.0, 11 "e-1": NaN, 12 "a-2": 2, 13 "b-2": 7.0, 14 "c-2": NaN, 15 "d-2": 17.0, 16 "e-2": NaN, 17 "a-3": 3, 18 "b-3": 8.0, 19 "c-3": NaN, 20 "d-3": NaN, 21 "e-3": NaN, 22 "a-4": 4, 23 "b-4": NaN, 24 "c-4": NaN, 25 "d-4": 19.0, 26 "e-4": NaN 27}

一応、プログラムは回るのですが、本来nullであるべきところがNaNのままになっており、試しにこれと同じ方法で出力したファイルをjupyter labで開こうとするとこれが原因で開けません。エラーメッセージは、

Unexpected token 'N', ..."文字列": NaN, "... is not valid JSON

(なお、これは上記のテストコードで出力したファイルではなく本番用のコードなのでキーが少し異なります。)

色々変えながらNaNをnullにして出力しようとしていますがうまくいきません。

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

色々調べて作成したコードなのですが、うまく行きません。jsonのencodeは正直慣れていないので初歩的なミスがある可能性が高いと思うのですが、ご助言いただけるとありがたいです。

以下のページを特に参考にしました。
カスタムオブジェクトの JSON エンコード/デコード
numpyをjson.dumpするときに気をつけたいこと

追記

最初にご回答いただいた方にも返答で書かせていただきましたが、今回はせっかくなので勉強のためエンコーダを利用する方法でやりたいと考えております。どうしても方法が見つからなさそうであれば、dict作成の段階でのデータの型変換をやりたいと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

辞書を作成する段階で型変換を行ってみてはどうでしょうか。(この場合はエンコーダは不要になります)

python

1for i,row in enumerate(df.index): 2 for j,col in enumerate(df.columns): 3 v = df.iloc[i,j] 4 if isinstance(v , np.int64): 5 v = int(v) 6 elif np.isnan(v): 7 v = None 8 dic[f'{col}-{row}'] = v 9 10print(json.dumps(dic,indent=4)) 11 12# { 13# "a-0": 0, 14# "b-0": null, 15# "c-0": 10.0, 16# "d-0": 15.0, 17# "e-0": null, 18# "a-1": 1, 19# "b-1": 6.0, 20# "c-1": 11.0, 21# "d-1": 16.0, 22# "e-1": null, 23# "a-2": 2, 24# "b-2": 7.0, 25# "c-2": null, 26# "d-2": 17.0, 27# "e-2": null, 28# "a-3": 3, 29# "b-3": 8.0, 30# "c-3": null, 31# "d-3": null, 32# "e-3": null, 33# "a-4": 4, 34# "b-4": null, 35# "c-4": null, 36# "d-4": 19.0, 37# "e-4": null 38# }

追記

pandas.DataFrame.to_dict() と辞書内包表記(dictionary comprehension) を使用して以下の様にも書くことができます。

python

1import pandas as pd 2import numpy as np 3import json 4 5df=pd.DataFrame({'a':[0,1,2,3,4],'b':[None,6,7,8,np.nan], 6 'c':[10,11,np.nan,np.nan,None],'d':[15,16,17,None,19], 7 'e':[np.nan,np.nan,np.nan,np.nan,np.nan]}) 8 9dic = {f'{k}-{i}': v 10 for i, d in enumerate(df.replace({np.nan: None}).to_dict(orient='records')) 11 for k, v in d.items()} 12print(json.dumps(dic,indent=4))

投稿2024/08/15 04:02

編集2024/08/15 11:11
melian

総合スコア20530

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttuucch

2024/08/15 04:45

ありがとうございます! その方法も頭にはありつつ、私のエンコーダへの理解が足りてないなら、勉強になるので指摘がもらえるといいかなと思っております。 エンコーダを使用する方法がどうにもならなければ仰るとおり辞書に入れるところでデータ型を変換しようと思います。
melian

2024/08/15 04:57

NumpyEncoder で NaN が変換されないのは、np.float64型がfloat型の継承型であるためです。一方、np.int64型はint型の継承型ではありません。そのため、NumpyEncoder による変換が行われるのはnp.int64型のインスタンスのみになります。 >>> isinstance(np.float64(1.0), float) True >>> isinstance(np.int64(1), int) False
ttuucch

2024/08/15 08:17

仕組みを教えていただきありがとうございます。確かに色々np.nanのデータ型を見ていると複雑だなあと思っていました。Encoderでの解決は、できない、ということになるのでしょうか?
melian

2024/08/15 08:45

Encoder はシリアライズできないオブジェクトにのみ作用します。具体的には、以下のページの表(「デフォルトでは以下のオブジェクトと型をサポートします」)に記載のない型だけが、NumpyEncoder.default() メソッドによる変換の対象になります。 class json.JSONEncoder https://docs.python.org/ja/3/library/json.html#json.JSONEncoder int型の派生ではない np.int64型はどれにも当てはまりませんので NumpyEncoder.default() で変換されますが、np.float64型(NaNを含む)は「int、float と int や float の派生列挙型」に当てはまりますので、NumpyEncoder.default() は呼び出されず、デフォルトの JSON エンコーダによる変換が行われることになります。
ttuucch

2024/08/15 23:50

なるほど、ありがとうございます。理解しました。では、やっぱり辞書にいれる段階での型変換になるのですね。丁寧にご解説ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.39%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問