PDEP-10: デフォルトの文字列推論実装に必要な依存関係としてのPyArrow
- 作成日: 2023年4月17日
- ステータス: 承認済み
- ディスカッション: #52711 #52509
- 作成者: Matthew Roeschke Patrick Hoefler
- 改訂版: 1
概要
このPDEPは、
- pandas 3.0以降、PyArrowが必須のランタイム依存関係になることを提案しています。
- pandas 3.0以降でサポートされるPyArrowの最小バージョンは、PyArrowのバージョン7です。
- PyArrowの最小バージョンが変更された場合、少なくとも2年間リリースされている最新のバージョンにPyArrowが更新されます。
- pandas 2.1のリリースノートには、pandas 3.0以降でPyArrowが必須の依存関係になるという大きな警告が表示されます。pandasのイシュートラッカーにフィードバック用のイシューを固定します。リリースノートの記述はこのイシューを指し示します。
- pandas 2.2以降、pandasがインポートされた際にユーザー環境にPyArrowがインストールされていない場合、pandasは`FutureWarning`を発生させます。これにより、警告が1つだけ発生し、必要に応じてユーザーが簡単に警告を抑制できるようになります。この警告はフィードバック用のイシューを指し示します。
- pandas 3.0以降、文字列データに対して推論されるデフォルトの型は、`object`ではなく`ArrowDtype` with `pyarrow.string`になります。さらに、以下にリストされているすべてのデータ型も`object`として保存する代わりに推論します。
これにより、**ユーザーにとって即座にメリットがもたらされる**と同時に、将来的な大きなメリットへの道が開かれます。
背景
PyArrowは、pandasに幅広い追加機能を提供する、pandasのオプション依存関係です。
- pandas 0.21.0以降、PyArrowはParquetのI/O読み込み機能を提供しています。
- pandas 1.2.0以降、pandasはPyArrowを`ExtensionArray`インターフェースに統合し、PyArrowを基盤としたオプションの文字列データ型を提供しています。
- pandas 1.4.0以降、PyArrowはCSVのI/O読み込み機能を提供しています。
- pandas 1.5.0以降、pandasは`ArrowExtensionArray`と`ArrowDtype`を提供し、`ExtensionArray`インターフェース内ですべてのPyArrowデータ型をサポートしています。
- pandas 2.0.0以降、すべてのI/OリーダーはPyArrowベースのデータ型を返すオプションを持ち、多くのメソッドはPyArrowの計算関数を活用して、pandas内のPyArrowベースのデータ(特に文字列型と日付型)を高速化しています。
pandas 2.0以降、次のような利点を持つPyArrowをNumPyの代替データ表現として利用できます。
- すべてのデータ型で一貫した`NA`サポート;
- `decimal`、`date`、ネスト型などのより幅広いデータ型のサポート;
- Arrowをベースとした他のデータフレームライブラリとの優れた相互運用性。
モチベーション
前段落で説明したすべての機能は現在オプションですが、PyArrowはpandasの多くの領域に大きく統合されています。pandasのロードマップでは、Apache Arrowとの相互運用性の向上[^1]と、Pythonエコシステム内外の多くのプロジェクト[^2]がArrow形式を採用または操作していることを示しており、PyArrowを必須の依存関係とすることで、Arrowエコシステムへの信頼度を高め(そしてそれとの相互運用性を向上させる)追加のシグナルを提供します。
ユーザーにとっての即時的なメリット1:pyarrow文字列
現在、ユーザーがデータ型を指定せずにpandasコンストラクターに文字列データを渡すと、結果のデータ型は`object`になります。これは、pyarrow文字列と比較して、メモリ使用量とパフォーマンスが大幅に劣ります。1.2.0以降で利用可能なpyarrow文字列サポートにより、3.0でpyarrowを必須とすることで、pandasは推論された型をより効率的なpyarrow文字列型にデフォルト設定できます。
In [1]: import pandas as pd
In [2]: pd.Series(["a"]).dtype
# Current behavior
Out[2]: dtype('O')
# Future behavior in 3.0
Out[2]: string[pyarrow]
Dask開発者は、pyarrow文字列のパフォーマンスとメモリこちらを調査し、現在の`object`dtypeよりも大幅な改善が見られることを発見しました。
簡単なデモ
import string
import random
import pandas as pd
def random_string() -> str:
return "".join(random.choices(string.printable, k=random.randint(10, 100)))
ser_object = pd.Series([random_string() for _ in range(1_000_000)])
ser_string = ser_object.astype("string[pyarrow]")\
PyArrowベースの文字列は、NumPyオブジェクト文字列よりも大幅に高速です。
str.len
In[1]: %timeit ser_object.str.len()
118 ms ± 260 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In[2]: %timeit ser_string.str.len()
24.2 ms ± 187 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
str.startswith
In[3]: %timeit ser_object.str.startswith("a")
136 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In[4]: %timeit ser_string.str.startswith("a")
11 ms ± 19.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ユーザーにとっての即時的なメリット2:ネストされたデータ型
現在、pandas `Series`に`dict`を格納しようとすると、再びひどい`object`dtypeになります。
In [6]: pd.Series([{'a': 1, 'b': 2}, {'a': 2, 'b': 99}])
Out[6]:
0 {'a': 1, 'b': 2}
1 {'a': 2, 'b': 99}
dtype: object
`pyarrow`が必須であれば、これは`pyarrow.struct`として自動的に推論でき、これもメモリとパフォーマンスの向上につながります。
ユーザーにとっての即時的なメリット3:相互運用性
他のArrowベースのデータフレームライブラリの人気が高まっています。メモリ表現が同じであれば、それらとの相互運用性が向上し、
import pandas as pd
import polars as pl
df = pd.DataFrame(
{
'a': ['one', 'two'],
'b': [{'name': 'Billy', 'age': 3}, {'name': 'Bob', 'age': 4}],
}
)
pl.from_pandas(df)
ゼロコピー操作が可能になります。複数のデータフレームライブラリを使用しているユーザーは、それらの間をより簡単に切り替えることができます。
将来的なユーザーメリット
PyArrowを必須にすることで、pandas内の関連開発が簡素化され、PyArrowの方が適しているNumPy機能が向上する可能性があります。
-
コンストラクターまたはインデックス操作中にPyArrowオブジェクトの推論を実行するために、PyArrowが利用可能かどうかをランタイムチェックする必要がなくなります。
-
NumPyオブジェクトdtypeはできる限り回避されます。つまり、PyArrowに相当するdtypeはすべて自動的にそのようなdtypeとして推論されます。これには以下が含まれます。
- decimal
- binary
- ネスト型(リストまたは辞書データ)
- 文字列
- 時間
- 日付
開発者のメリット
まず、これにより、オプションの依存関係チェックが不要になるため、pyarrowベースのデータ型の開発が簡素化されます。
次に、冗長な機能を削除できる可能性があります。 - `read_parquet`内のfastparquetエンジン; - `read_csv`ロジックの簡素化の可能性(さらなる調査が必要); - 因子化; - 日付/タイムゾーン操作。
欠点
PyArrowを含めると、pandasのインストールサイズが当然大きくなります。たとえば、ホイールからpipを使用してpandasとPyArrowをインストールする場合、numpyとpandasは約70MB必要ですが、PyArrowを含めるとさらに120MB必要になります。インストールサイズの増加は、AWS Lambdaなどのスペースが限られた開発環境や展開環境でpandasを使用する場合に悪影響を及ぼします。
さらに、`pip install`または`conda install`を介してホイールを利用できない環境にpandasをインストールする場合、ソースからインストールするときに、Arrow C++および関連する依存関係もビルドする必要があります。これらの環境には以下が含まれます。
- Alpine Linux(Dockerコンテナのベースとして一般的に使用されます)
- WASM(pyodideとpyscript)
- Python開発バージョン
最後に、pandasの開発とリリースは、PyArrowの開発とリリースのペースを考慮する必要があります。たとえば、新しくリリースされたPythonバージョンをサポートする場合、pandasは新しいpandasバージョンをリリースする前に、そのPythonバージョンのPyArrowのホイールサポートを考慮する必要があります。
よくある質問
Q:pandasは、pyarrow文字列とpyarrow structの代わりに、numpy文字列とnumpy voidデータ型を使用できないのはなぜですか?
A:NumPy文字列はまだ利用できませんが、pyarrow文字列は利用できます。NumPy voidデータ型はpyarrow structとは異なり、他のArrowベースのデータフレームライブラリとの同じ相互運用性のメリットが得られません。
Q:すべてのpyarrow dtypeの準備はできていますか?それらをデフォルトにするには早すぎませんか?
A:3.0までに準備できる可能性が高いです。ただし、それらをデフォルトに(まだ)していません。たとえば、`pd.Series([1, 2, 3])`は引き続き`np.int64`として自動的に推論されます。現在NumPyを基盤とした相当するものがない、そして`object`dtypeとして保存されているdtype(文字列やネストされたデータ型など)のデフォルトのみを変更します。
PDEP-10履歴
- 2023年4月17日:最初のバージョン
- 2023年5月8日:pandas 2.1ではなくpandas 3.0でpyarrowを必須にするように提案を変更
[^1] https://pandas.dokyumento.jp/docs/development/roadmap.html#apache-arrow-interoperability [^2] https://arrow.apache.org/powered_by/