標準 API で pandas コネクタを登録するサードパーティ プロジェクトを許可する PDEP-9

PDEP まとめ

この文書は、I/O またはメモリ コネクタを pandas に実装するサードパーティ プロジェクトが、Python のエントリポイント システムを使用してそれらを登録し、通常の pandas I/O インターフェイスを使用して pandas ユーザーが利用できるようにすることを提案しています。たとえば、pandas から独立したパッケージは DuckDB からのリーダーと Delta Lake へのライターを実装でき、ユーザー環境にインストールすると、pandas に実装されているかのようにそれらを使用できます。たとえば

import pandas

pandas.load_io_plugins()

df = pandas.DataFrame.read_duckdb("SELECT * FROM 'my_dataset.parquet';")

df.to_deltalake('/delta/my_dataset')

これにより、既存のコネクタの数を簡単に拡張し、新しいフォーマットとデータベース エンジン、データ レイク テクノロジー、アウトオブコア コネクタ、新しい ADBC インターフェイスなどをサポートし、同時に pandas コードベースのメンテナンス コストを削減できます。

現時点

pandas は現在 pandas/io に実装されている I/O コネクタを使用して、さまざまなフォーマットからのデータのインポートとエクスポートをサポートしています。Python 構造または他のライブラリフォーマットなどのインメモリ構造へのコネクタもサポートしています。多くの場合、これらのコネクタは既存の Python ライブラリをラップしますが、他の場合、pandas は特定のフォーマットを読み書きするロジックを実装します。

場合によっては、同じフォーマットにさまざまなエンジンが存在します。これらのコネクタを使用するための API は pandas.read_<format>(engine='<engine-name>', ...) であり、データをインポートし、DataFrame.to_<format>(engine='<engine-name>', ...) でデータをエクスポートします。

メモリにエクスポートされたオブジェクト (Python dict など) の場合、API は DataFrame.to_<format>(...) の I/O と同じです。メモリ内のオブジェクトからインポートされたフォーマットの場合、API は read_ の代わりに from_ 接頭辞を使用して異なり、DataFrame.from_<format>(...) になります。

場合によっては、pandas API はデータをディスクまたはメモリ オブジェクトにエクスポートするために使用されるのではなく、DataFrame のインデックスを変換するために使用される DataFrame.to_* メソッドを提供します。DataFrame.to_periodDataFrame.to_timestamp です。

コネクタの依存関係はデフォルトではロードされず、コネクタが使用されるとインポートされます。依存関係がインストールされていない場合、ImportError が発生します。

>>> pandas.read_gbq(query)
Traceback (most recent call last):
  ...
ImportError: Missing optional dependency 'pandas-gbq'.
pandas-gbq is required to load data from Google BigQuery.
See the docs: https://pandas-gbq.readthedocs.io.
Use pip or conda to install pandas-gbq.

サポートされているフォーマット

IO ガイド ではフォーマットの一覧を参照できます。インメモリ オブジェクトを含むより詳細な表と DataFrame スタイラーの I/O コネクタは次に示されています。

フォーマット リーダー ライター エンジン
CSV X X cpythonpyarrow
FWF X cpythonpyarrow
JSON X X ujsonpyarrow
HTML X X lxmlbs4/html5lib(パラメーター: flavor
LaTeX X
XML X X lxmletree(パラメーター: parser
クリップボード X X
Excel X X xlrdopenpyxlodfpyxlsb(各エンジンでサポートされるファイル形式は異なります)
HDF5 X X
Feather X X
Parquet X X pyarrowfastparquet
ORC X X
Stata X X
SAS X
SPSS X
Pickle X X
SQL X X sqlalchemydbapi2conパラメーターの型から推測)
BigQuery X X
dict X X
records X X
文字列 X
markdown X
xarray X

この文書を書いている時点で、io/モジュールには100,000行近くのPython、C、Cythonコードが含まれています。

pandasにフォーマットを含めるのかどうかについては客観的な基準はなく、上のリストは主に、開発者がpandasで特定のフォーマットのコネクタを実装することに興味を持っていた結果です。

pandasで処理可能なデータに使用できる既存のフォーマットの数は常に増加しており、pandasでは人気のフォーマットにすら追従するのは困難です。PyArrow、PySpark、Iceberg、DuckDB、Hive、Polarsなどへのコネクタを用意するのは理にかなっています。

同時に、2019年のユーザー調査で示されているように、頻繁には使用されなくなったフォーマットもあります。これらのあまり人気のないフォーマットには、SPSS、SAS、Google BigQuery、Stataが含まれています。調査に含まれていたのはI/Oフォーマットのみで、recordsやxarrayなどのメモリフォーマットは含まれていないことに注意してください。

すべてのフォーマットをサポートするためのメンテナンス費用は、コードの管理とプルリクエストの見直しだけでなく、依存関係のインストール、コードのコンパイル、テストの実行などにCIシステムで費やされる時間でも大幅にコストがかかります。

場合によっては、一部のコネクタのメインの保守担当者はpandasのコア開発チームではなく、特定のフォーマットに特化した人です。

提案

現在のpandasのアプローチはある程度はうまく機能していますが、pandasで発生するメンテナンスが大きくなりすぎず、同時にユーザーが関心のある、あらゆる異なるフォーマットや表現を、簡単かつ直感的な方法で操作できる安定したソリューションを確保するのは難しいものです。

現在、サードパーティ製のパッケージはpandasへのコネクタを実装できますが、それにはいくつかの制限があります。

このドキュメントでは、pandasのI/Oコネクタの開発を、これらの制限を克服する標準的な方法でサードパーティライブラリに公開することを提案します。

提案の実装

この提案を実装するには、pandasに対して大きな変更を加える必要はなく、次に定義するAPIが使用されます。

ユーザーAPI

ユーザーは、標準のパッケージングツール(pip、condaなど)を使用して、pandasコネクタを実装したサードパーティパッケージをインストールできます。これらのコネクタは、pandasが自動的に対応するメソッド「pandas.read_*」、「pandas.DataFrame.to_*」、および「pandas.Series.to_*」を作成するために使用するエントリポイントを実装する必要があります。このインターフェイスによって、任意の関数名やメソッド名が作成されることはありません。許可されるのは「read_*」と「to_*」パターンだけです。

適切なパッケージをインストールし、関数 pandas.load_io_plugins() を呼び出すだけで、ユーザーはこのようなコードを使用できます。

import pandas

pandas.load_io_plugins()

df = pandas.read_duckdb("SELECT * FROM 'dataset.parquet';")

df.to_hive(hive_conn, "hive_table")

この API ではメソッドの連鎖が可能です。

(pandas.read_duckdb("SELECT * FROM 'dataset.parquet';")
       .to_hive(hive_conn, "hive_table"))

一般的にユーザーはほんの僅かのフォーマットのサブセットしか使用しないため、I/O 関数とメソッドの合計数は少ないと予想されます。その数は、人気のないフォーマット(SAS、SPSS、BigQuery など)を pandas コアからサードパーティパッケージに削除すれば、現状からさらに少なくなる可能性があります。これらのコネクタの移動はこの提案には含まれていません。今後、別の提案として検討される可能性があります。

プラグイン登録

サードパーティパッケージは entrypoints を実装して、dataframe.io グループの下で実装するコネクタを定義します。

たとえば、read_duckdb 関数を実装する仮説のプロジェクト pandas_duckdb は、pyproject.toml を使用して次のエントリポイントを定義できます。

[project.entry-points."dataframe.io"]
reader_duckdb = "pandas_duckdb:read_duckdb"

ユーザーが pandas.load_io_plugins() を呼び出すと、dataframe.io グループのエントリポイントレジストリが読み取られ、pandaspandas.DataFrame、および pandas.Series の名前空間に動的にメソッドが作成されます。reader_または writer_で始まる名前のエントリポイントのみが pandas によって処理され、エントリポイントに登録された関数は、対応する pandas 名前空間で pandas ユーザーが使用できます。キーワードreader_writer_に続くテキストは、関数の名前に使用されます。上の例では、エントリポイント名reader_duckdbにより、pandas.read_duckdbが作成されます。writer_hiveという名前のエントリポイントは、メソッドDataFrame.to_hiveSeries.to_hiveを作成します。

reader_または writer_で始まらないエントリポイントは、このインターフェイスによって無視されますが、この API の将来の拡張機能、または他の関連するデータフレーム I/O インターフェイスで使用できるため、例外は発生しません。

内部 API

コネクタはデータフレームインターチェンジ API を使用してデータを pandas に提供します。コネクタからデータを読み取り、pandas.read_<format>への応答としてユーザーに返す前に、データフレームインターチェンジインターフェイスからデータが解析され、pandas DataFrame に変換されます。実際には、コネクタは pandas DataFrame または PyArrow Table を返す可能性がありますが、インターフェイスはデータフレームインターチェンジ API を実装するすべてのオブジェクトをサポートします。

コネクタのガイドライン

ユーザーにより良く、より一貫したエクスペリエンスを提供するために、用語と動作を統一するためのガイドラインが作成されます。次に、統一する一部のトピックを定義します。

名前の競合を回避するためのガイドライン。 すでに起こっているように、特定のフォーマットに対して複数の実装が存在することが期待されるため、コネクタの命名方法に関するガイドラインが作成されます。最も簡単な方法は、複数のコネクタが存在することが予想される場合は、おそらくto_<format>_<implementation-id>タイプの文字列をフォーマットとして使用することです。たとえば、LanceDB の場合は、おそらく 1 つだけのコネクタが存在し、lance という名前を使用できます(これは pandas.read_lance または DataFrame.to_lance を作成します)。しかし、Arrow2 Rust 実装に基づく新しい csv リーダーがある場合、ガイドラインは csv_arrow2 を使用して pandas.read_csv_arrow2 を作成することを推奨できます。

パラメーターの有無と名前付け同様の機能(データ内に含まれる列の一部のみをロードする、パスを扱う等)を提供するコネクタが多いので、コネクタ開発者に対する推奨事項の例:

上記はあくまでガイドラインの例であって、ガイドラインの提案ではなく、この PDEP が承認された後に独立して策定されます。

コネクタのレジストリおよびドキュメントコネクタやそのドキュメントの発見を簡素化するには、コネクタ開発者は中央の場所にプロジェクトを登録し、ドキュメントに標準構造を使用することを奨励できます。これにより、利用可能なコネクタ及其のドキュメントを見つけるための統一された Web サイトを作成することができます。また、特定の実装のドキュメントをカスタマイズして、最終的な API を含めることもできます。

コネクタの例

このセクションでは、この提案からすぐに恩恵を受ける可能性のあるコネクタの具体的な例を記載します。

PyArrowには現在、Table.from_pandasTable.to_pandasの機能があります。新しいインターフェイスでは、DataFrame.from_pyarrowDataFrame.to_pyarrowを登録することもできます。これにより、PyArrowが環境内にインストールされている場合、pandasユーザーは慣れ親しんでいるインターフェイスを使用してコンバータを利用できます。PyArrowテーブルとのより良い統合については、#51760で議論されました。

現在の API:

pyarrow.Table.from_pandas(table.to_pandas()
                               .query('my_col > 0'))

提案された API:

(pandas.read_pyarrow(table)
       .query('my_col > 0')
       .to_pyarrow())

PolarsVaex、および他のデータフレームフレームワークは、pandasとの相互運用性をより明確な APIにするサードパーティプロジェクトから恩恵を受けることができます。Polarsとの統合は#47368でリクエストされました。

現在の API:

polars.DataFrame(df.to_pandas()
                   .query('my_col > 0'))

提案された API:

(pandas.read_polars(df)
       .query('my_col > 0')
       .to_polars())

DuckDBは、データのロード前に述語をプッシュできるアウトオブコアエンジンを提供します。これにより、メモリを大幅に有効活用し、ロード時間を大幅に短縮できます。eagerな性質により、pandasはこれを単独で実装することは容易ではありませんが、DuckDBローダーの恩恵を受けることができます。ローダーは既にpandas内で実装できます(#45678ですでに提案されています)。任意のAPIを備えたサードパーティの拡張機能として実装できます。ただし、この提案により、標準的で直感的なAPIを持つサードパーティの拡張機能を作成できます。

pandas.read_duckdb("SELECT *
                    FROM 'dataset.parquet'
                    WHERE my_col > 0")

アウトオブコアアルゴリズムは、フィルタリングやグループ化などの操作をデータのロードにプッシュします。現在これは可能ではありませんが、アウトオブコアアルゴリズムを実装するコネクタはこのインターフェイスを使用して開発できます。

Hive、Iceberg、Prestoなどのビッグデータシステムは、データをpandasにロードするための標準的な方法から恩恵を受けることができます。また、クエリ結果を矢印として返すことができる通常のSQLデータベースは、SQL AlchemyとPython構造に基づく既存のものよりも、より優れ、高速なコネクタから恩恵を受けることができます。

ドメイン固有のフォーマットを含む他のフォーマットは、明確で直感的なAPIでpandaのコネクタを簡単に実装できます。

制限事項

この提案の実装には、以下に記載されているいくつかの制限があります。

将来の計画

このPDEPは既存および将来のコネクタに対してより優れたAPIをサポートすることを目的としています。pandasコードベースに存在するコネクタに変更を実装することは、このPDEPの範囲外です。

このPDEPに関連した将来の議論に関するアイデアを以下に示します。

PDEP-9の履歴