PDEP-12: コンパクトで可逆的なJSONインターフェース

概要

要約

問題の説明

現在のJSONインターフェースでは、dtypeと「Python型」は明示的に考慮されていません。

そのため、JSONインターフェースは常に可逆的ではなく、dtypeの考慮に関して不整合があります。

もう1つの結果として、orient="table"オプションにおけるTable Schema仕様の部分的な適用があります(定義されている24のTable Schemaデータ型のうち6つが考慮されています)。

JSONインターフェースに関するいくつかの問題は、リンクされたノートブックで詳しく説明されています。

機能の説明

シンプルでコンパクト、かつ可逆的なソリューションを実現するために、型の概念を統合したJSON-NTV形式(名前付きおよび型付き値)とその表形式データのJSON-TABバリエーションを使用することを提案します(JSON-NTV形式はIETFインターネットドラフト(まだRFCではありません!!)で定義されています)。

このソリューションにより、多数の型(必ずしもpandas dtypeである必要はありません)を含めることができ、以下のことが可能になります。

グローバルJSONインターフェースの例

以下の例では、複数のデータ型を持つDataFrameがJSONに変換されます。

このJSONから生成されるDataFrameは、元のDataFrameと同じです(可逆性)。

既存のJSONインターフェースでは、この変換は不可能です。

この例では、ntv-pandasリポジトリで定義されているntv_pandasモジュールを使用しています。

データ例

In [1]: from shapely.geometry import Point
        from datetime import date
        import pandas as pd
        import ntv_pandas as npd

In [2]: data = {'index':           [100, 200, 300, 400, 500, 600],
                'dates::date':     [date(1964,1,1), date(1985,2,5), date(2022,1,21), date(1964,1,1), date(1985,2,5), date(2022,1,21)],
                'value':           [10, 10, 20, 20, 30, 30],
                'value32':         pd.Series([12, 12, 22, 22, 32, 32], dtype='int32'),
                'res':             [10, 20, 30, 10, 20, 30],
                'coord::point':    [Point(1,2), Point(3,4), Point(5,6), Point(7,8), Point(3,4), Point(5,6)],
                'names':           pd.Series(['john', 'eric', 'judith', 'mila', 'hector', 'maria'], dtype='string'),
                'unique':          True }

In [3]: df = pd.DataFrame(data).set_index('index')

In [4]: df
Out[4]:       dates::date  value  value32  res coord::point   names  unique
        index
        100    1964-01-01     10       12   10  POINT (1 2)    john    True
        200    1985-02-05     10       12   20  POINT (3 4)    eric    True
        300    2022-01-21     20       22   30  POINT (5 6)  judith    True
        400    1964-01-01     20       22   10  POINT (7 8)    mila    True
        500    1985-02-05     30       32   20  POINT (3 4)  hector    True
        600    2022-01-21     30       32   30  POINT (5 6)   maria    True

JSON表現

In [5]: df_to_json = npd.to_json(df)
        pprint(df_to_json, width=120)
Out[5]: {':tab': {'coord::point': [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0], [3.0, 4.0], [5.0, 6.0]],
                  'dates::date': ['1964-01-01', '1985-02-05', '2022-01-21', '1964-01-01', '1985-02-05', '2022-01-21'],
                  'index': [100, 200, 300, 400, 500, 600],
                  'names::string': ['john', 'eric', 'judith', 'mila', 'hector', 'maria'],
                  'res': [10, 20, 30, 10, 20, 30],
                  'unique': [True, True, True, True, True, True],
                  'value': [10, 10, 20, 20, 30, 30],
                  'value32::int32': [12, 12, 22, 22, 32, 32]}}

可逆性

In [5]: df_from_json = npd.read_json(df_to_json)
        print('df created from JSON is equal to initial df ? ', df_from_json.equals(df))
Out[5]: df created from JSON is equal to initial df ?  True

その他の例は、リンクされたノートブックに記載されています。

Table Schema JSONインターフェースの例

以下の例では、複数のTable Schemaデータ型を持つDataFrameがJSONに変換されます。

このJSONから生成されるDataFrameは、元のDataFrameと同じです(可逆性)。

既存のTable Schema JSONインターフェースでは、この変換は不可能です。

In [1]: from shapely.geometry import Point
        from datetime import date

In [2]: df = pd.DataFrame({
            'end february::date': ['date(2023,2,28)', 'date(2024,2,29)', 'date(2025,2,28)'],
            'coordinates::point': ['Point([2.3, 48.9])', 'Point([5.4, 43.3])', 'Point([4.9, 45.8])'],
            'contact::email':     ['[email protected]', '[email protected]', '[email protected]']
            })

In [3]: df
Out[3]: end february::date coordinates::point             contact::email
        0         2023-02-28   POINT (2.3 48.9)         john.doe@table.com
        1         2024-02-29   POINT (5.4 43.3)    lisa.minelli@schema.com
        2         2025-02-28   POINT (4.9 45.8)  walter.white@breaking.com

JSON表現

In [4]: df_to_table = npd.to_json(df, table=True)
        pprint(df_to_table, width=140, sort_dicts=False)
Out[4]: {'schema': {'fields': [{'name': 'index', 'type': 'integer'},
                               {'name': 'end february', 'type': 'date'},
                               {'name': 'coordinates', 'type': 'geopoint', 'format': 'array'},
                               {'name': 'contact', 'type': 'string', 'format': 'email'}],
                    'primaryKey': ['index'],
                    'pandas_version': '1.4.0'},
         'data': [{'index': 0, 'end february': '2023-02-28', 'coordinates': [2.3, 48.9], 'contact': '[email protected]'},
                  {'index': 1, 'end february': '2024-02-29', 'coordinates': [5.4, 43.3], 'contact': '[email protected]'},
                  {'index': 2, 'end february': '2025-02-28', 'coordinates': [4.9, 45.8], 'contact': '[email protected]'}]}

可逆性

In [5]: df_from_table = npd.read_json(df_to_table)
        print('df created from JSON is equal to initial df ? ', df_from_table.equals(df))
Out[5]: df created from JSON is equal to initial df ?  True

その他の例は、リンクされたノートブックに記載されています。

スコープ

目的は、提案されたJSONインターフェースをあらゆる種類のデータ、およびorient="table"オプションまたは新しいオプションorient="ntv"で利用できるようにすることです。

提案されたインターフェースは、既存のデータと互換性があります。

動機

なぜorient=tableオプションを他のデータ型に拡張するのか?

コンパクトで可逆的なJSONインターフェースを持つことがなぜ重要なのか?

拡張型を考慮することは適切か?

これはpandasのみに役立つのか?

説明

提案されたソリューションは、いくつかの重要なポイントに基づいています。

データ型指定

データ型はNTVプロジェクトで定義および管理されます(名前、JSONエンコーダーとデコーダー)。

Pandas dtypeはNTV型と互換性があります。

pandas dtype NTV型
intxx intxx
uintxx uintxx
floatxx floatxx
datetime[ns] datetime
datetime[ns,] datetimetz
timedelta[ns] durationiso
string string
boolean boolean

注記

JSON型(暗黙的または明示的)は、pandas JSONインターフェースに従ってdtypeに変換されます。

JSON型 pandas dtype
number int64 / float64
string string / object
array object
object object
true, false boolean
null NaT / NaN / None

注記

その他のNTV型は、object dtypeに関連付けられています。

TableSchemaとpandasの対応

TableSchemaの型指定は、2つの属性formattypeによって行われます。

以下の表は、TableSchemaのformat / typeとpandasのNTVtype / dtypeの対応を示しています。

format / type NTV type / dtype
default / datetime / datetime64[ns]
default / number / float64
default / integer / int64
default / boolean / bool
default / string / object
default / duration / timedelta64[ns]
email / string email / string
uri / string uri / string
default / object object / object
default / array array / object
default / date date / object
default / time time / object
default / year year / int64
default / yearmonth month / int64
array / geopoint point / object
default / geojson geojson / object

注記

JSON形式

TableSchemaインターフェースのJSON形式は既存のものです。

グローバルインターフェースのJSON形式は、JSON-TAB仕様で定義されています。 これには、JSON-NDプロジェクトで最初に定義された命名規則と、カテゴリデータのサポートが含まれています。 この仕様は、スパースデータを含めるように更新する必要があります。

変換

データがobject以外のdtypeに関連付けられている場合、pandasの変換メソッドが使用されます。 それ以外の場合、NTV変換が使用されます。

pandas -> JSON

JSON -> pandas

使用法と影響

使用法

この提案は、重要な問題に対処しているように思われます。

互換性

インターフェースはNTV型なしで使用できます(既存のデータとの互換性 - 例を参照)。

インターフェースが使用可能な場合、JSONインターフェースに新しいorientオプションを追加することで、機能の使用は他の機能から分離されます。

pandasフレームワークへの影響

初期段階では、影響は非常に限定的です。

後の段階では、いくつかの開発が検討される可能性があります。

実行するリスク / 実行しないリスク

JSON-NTV形式とJSON-TAB形式は、(まだ)認識されておらず、使用されている形式ではありません。 pandasのリスクは、この関数が使用されないことです(機能的な影響はありません)。

一方、pandasによる早期使用により、pandasの期待とニーズをよりよく考慮し、pandasがサポートする型の進化について考察することができます。

実装

モジュール

NTV用に2つのモジュールが定義されています。

JSONインターフェースのpandas統合には、json-ntvモジュールのみをインポートする必要があります。

実装オプション

インターフェースは、NTVコネクタ(SeriesConnectorDataFrameConnector)として、および新しいpandas JSONインターフェースorientオプションとして実装できます。

pandasのいくつかの実装が可能です。

  1. 外部

    この実装では、インターフェースはNTV側でのみ使用できます。 このオプションは、JSONインターフェースのこの進化がpandasにとって有用でも戦略的でもないことを意味します。

  2. NTV側

    この実装では、インターフェースは両側で使用可能であり、変換はNTV内部で行われます。このオプションは、pandas側への影響を最小限に抑えるものです。

  3. pandas側

    この実装では、インターフェースは両側で使用可能であり、変換はpandas内部で行われます。このオプションにより、pandasはこの進化を制御し続けることができます。

  4. pandas制限付き

    この実装では、pandasインターフェースと変換はpandas内部に配置され、オブジェクト以外のdtypeのみに使用されます。このオプションにより、既存のdtypeと互換性のない型の導入を禁止しながら、コンパクトで可逆的なインターフェースを提供することが可能になります。

よくある質問

Q: orient="table"では、すでに提案されていることはできないのでしょうか?

A: 原則的には、はい。このオプションは型の概念を考慮しています。

しかし、これは非常に限定的です(ノートブックに追加された例を参照)。

現在のインターフェースは、table-schemaで定義されているデータ構造体と互換性がありません。これを可能にするには、提案されているような「型拡張」を統合する必要があります(これは、さらに、いくつかのフォーマットのインターフェースにあるextDtypeの概念で部分的に実現されています)。

Q: 一般的に、read_json/to_jsonでは、pandas用に1つの"table"フォーマットのみを持つべきです。また、フォーマットを変更する場合、後方互換性の問題もあります。テーブルインターフェースにバグがあることは、新しいインターフェースを追加する理由にはなりません(むしろ、それらのバグを修正する方が良いでしょう)。既存のフォーマットは、型の課題/ラウンドトリップの課題を修正する方法で適応させることはできますか?

A: 2つの追加の備考を追加します。

問題はバグ修正に限定されることはできないと考えており、特に、オープンデータソリューションにおいて、廃止されたCSVフォーマットからJSONフォーマットへの段階的な移行に伴い、JSONインターフェースの明確な戦略を定義する必要があります。

述べたように、提案されたソリューションは、現在のインターフェースのいくつかの欠点を addressedし、pandas環境にシンプルに適合できます(他のオプションは、JSONインターフェースはpandasの周辺機能であり、pandasの外部にとどまることができると考えることです)。orient='table'オプションに関係なく。

それでも、extDtypeの概念を明示的に管理するために、提案されたフォーマットとorient='table'フォーマットをマージすることは可能です。

Q: 私の知る限り、JSON NTVは、いかなる形式においても標準化されたJSONフォーマットではありません。pandas(そして私がこの問題に遭遇したgeopandas)は、デファクトスタンダードまたはデジュールスタンダードに従うべきであり、現時点ではコミュニティサポートのないファイルフォーマットを選択すべきではないと考えています。これは明らかに将来変更される可能性があり、このPRが見直されるべきです。なぜpandasはこの標準を使用するのでしょうか?

A: 問題で示されているように(そして添付のノートブックで詳述されているように)、JSONインターフェースは可逆的ではありません(to_jsonしてからread_jsonしても、常に初期オブジェクトが返されるとは限りません)。また、いくつかの欠点とバグが存在します。この問題の主な原因は、データ型がJSONフォーマットで考慮されていないことです(または、orient='table'オプションではごく一部しか考慮されていません)。

提案された解決策はこの問題に対処します(ノートブックの冒頭の例は、提案の利点をシンプルかつ明確に示しています)。

基盤となるJSON-NTVフォーマットに関しては、表形式データへの影響は非常に小さいです(フィールド名に型を追加することに限定されます)。それでも、この質問は適切です。JSON-NTVフォーマット(IETFインターネットドラフト)は、共有、文書化、サポート、実装されているフォーマットですが、確かにコミュニティサポートは今のところ限られていますが、拡大を求めているだけです!!

まとめ

結論として、

コアチームの決定

投票は9月11日から9月26日まで行われました。

不承認のコメント :

決定:

タイムライン

該当なし

PDEP履歴