標準 API で pandas コネクタを登録するサードパーティ プロジェクトを許可する PDEP-9
- 作成: 2023 年 3 月 5 日
- ステータス: 却下
- ディスカッション: #51799 #53005
- 著者: Marc Garcia
- 改訂: 1
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_period
と DataFrame.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 | c 、python 、pyarrow |
FWF | X | c 、python 、pyarrow |
|
JSON | X | X | ujson 、pyarrow |
HTML | X | X | lxml 、bs4/html5lib (パラメーター: flavor ) |
LaTeX | X | ||
XML | X | X | lxml 、etree (パラメーター: parser ) |
クリップボード | X | X | |
Excel | X | X | xlrd 、openpyxl 、odf 、pyxlsb (各エンジンでサポートされるファイル形式は異なります) |
HDF5 | X | X | |
Feather | X | X | |
Parquet | X | X | pyarrow 、fastparquet |
ORC | X | X | |
Stata | X | X | |
SAS | X | ||
SPSS | X | ||
Pickle | X | X | |
SQL | X | X | sqlalchemy 、dbapi2 (con パラメーターの型から推測) |
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コネクタには標準のAPIはなく、ユーザーはそれぞれを個別に学習する必要があります。pandasのI/O APIは、読み取り/書き込みや送り先/受け取りの代わりに読み取り/にを使用するという点で統一性がありませんので、多くの場合、開発者は慣例を無視します。また、開発者がpandasの慣例に従ったとしても、コネクタの開発者は、ほとんどの場合、関数を
pandas
またはDataFrame
のネームスペースにmonkeypatchしないので、ネームスペースが異なる可能性があります。 - サードパーティ製I/Oコネクタでは、DataFrameクラスを書き換えない限り、データのエクスポート時にメソッドチェーンを呼び出すことはできません。これは奨励されるべきではありません。
このドキュメントでは、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
グループのエントリポイントレジストリが読み取られ、pandas
、pandas.DataFrame
、および pandas.Series
の名前空間に動的にメソッドが作成されます。reader_
または writer_
で始まる名前のエントリポイントのみが pandas によって処理され、エントリポイントに登録された関数は、対応する pandas 名前空間で pandas ユーザーが使用できます。キーワードreader_
と writer_
に続くテキストは、関数の名前に使用されます。上の例では、エントリポイント名reader_duckdb
により、pandas.read_duckdb
が作成されます。writer_hive
という名前のエントリポイントは、メソッドDataFrame.to_hive
と Series.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
を作成することを推奨できます。
パラメーターの有無と名前付け同様の機能(データ内に含まれる列の一部のみをロードする、パスを扱う等)を提供するコネクタが多いので、コネクタ開発者に対する推奨事項の例:
columns
: この引数を使用して、ユーザーがある一部分の列をロードできるようにします。リストまたはタプルを許可します。path
: データセットがファイルディスク内のファイルである場合、この引数を使用します。文字列、pathlib.Path
オブジェクト、またはファイル記述子を許可します。文字列オブジェクトの場合、自動でダウンロードされるURL、自動で解凍される圧縮ファイル等を許可します。より簡単で一貫性のある方法でこれらに対処するために、特定のライブラリを推奨できます。schema
: スキーマのない(例えばcsv
)データセットの場合、Apache Arrowスキーマインスタンスの提供を許可し、提供されていない場合には自動的に型を推測します。
上記はあくまでガイドラインの例であって、ガイドラインの提案ではなく、この PDEP が承認された後に独立して策定されます。
コネクタのレジストリおよびドキュメントコネクタやそのドキュメントの発見を簡素化するには、コネクタ開発者は中央の場所にプロジェクトを登録し、ドキュメントに標準構造を使用することを奨励できます。これにより、利用可能なコネクタ及其のドキュメントを見つけるための統一された Web サイトを作成することができます。また、特定の実装のドキュメントをカスタマイズして、最終的な API を含めることもできます。
コネクタの例
このセクションでは、この提案からすぐに恩恵を受ける可能性のあるコネクタの具体的な例を記載します。
PyArrowには現在、Table.from_pandas
とTable.to_pandas
の機能があります。新しいインターフェイスでは、DataFrame.from_pyarrow
とDataFrame.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())
Polars、Vaex、および他のデータフレームフレームワークは、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のコネクタを簡単に実装できます。
制限事項
この提案の実装には、以下に記載されているいくつかの制限があります。
- マルチエンジンに対するサポートの欠落.現在のpandas I/O APIは同じ形式(同じ関数またはメソッド名)に対して複数のエンジンをサポートしています。たとえば
read_csv(engine='pyarrow', ...)
です。エンジンをサポートするには、特定の形式のすべてのエンジンが同じシグネチャ(同じパラメータ)を使用する必要がありますが、これは理想的ではありません。コネクタによって異なるパラメータが生じる可能性が高く、*args
および**kwargs
を使用するとユーザーにより複雑で困難な経験をもたらします。この理由から、この提案はエンジン用のオプションをサポートするのではなく、関数およびメソッド名が固有であることを優先しています。 - コネクタの型チェックに対するサポートの欠落.このPDEPは関数の動的作成およびメソッドを提案しており、それらはスタブを使用した型チェックではサポートされていません。これは、カスタムアクセッサなどのpandasの動的に作成された他のコンポーネントでも既に発生しています。
- 現在のI/O APIに対する改善の欠如この提案の議論では、
read
/to
(たとえばread
/write
)を使用することの一貫性の欠点を修正、非I/O操作に対してto_
プレフィックス付きメソッドの使用を回避、またはコネクタに対して専用のネームスペース(例:DataFrame.io
)を使用するように現在のpandas I/O APIを改善することが検討されています。これらの変更はすべてこのPDEPの範囲外です。
将来の計画
このPDEPは既存および将来のコネクタに対してより優れたAPIをサポートすることを目的としています。pandasコードベースに存在するコネクタに変更を実装することは、このPDEPの範囲外です。
このPDEPに関連した将来の議論に関するアイデアを以下に示します。
-
pandasのインポート時にI/Oプラグインを自動的にロードします。
-
SAS、SPSS、Google BigQueryなどの頻度の低いコネクタをpandasコードベースから削除し、このインターフェイスで登録されたサードパーティコネクタに移動します。
-
pandasコネクタに対してより優れたAPIについて議論する。たとえば、
from_*
メソッドの代わりにread_*
メソッドを使用する、I/Oコネクタとして使用されていないto_*
メソッドの名前を変更する、from/to、read/write、load/dumpなどの用語を統一する、またはコネクタに対して専用ネームスペースを使用する(例:pandas.io
ではなく一般的なpandas
ネームスペース)。 -
DataFrame
コンストラクタでサポートされている形式の一部をI/Oコネクタとして実装します。
PDEP-9の履歴
- 2023年3月5日: 初期バージョン
- 2023年5月30日: pandasの既存API、データフレームインターチェンジAPIを使用して大幅にリファクタリングし、プラグインのロードをユーザーに明示させる
- 2023年6月13日: PDEPは数回のイテレーション後もサポートを得られず、著者が拒否としてクローズ