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の最小バージョンが引き上げられた場合、PyArrowは少なくとも2年間リリースされている最新バージョンに引き上げられます。
- pandas 2.1のリリースノートには、pandas 3.0以降でPyArrowが必須の依存関係になることについて大きな警告が記載されます。pandasの課題トラッカーにフィードバックに関する課題を固定します。リリースノートの注意書きはその課題を指すようにします。
- pandas 2.2以降、pandasがインポートされたときにPyArrowがユーザー環境にインストールされていない場合、pandasは
FutureWarningを発生させます。これにより、警告が1つだけ発生し、ユーザーは必要に応じて簡単に警告を抑制できます。この警告はフィードバックに関する課題を指すようにします。 - pandas 3.0以降、文字列データに推論されるデフォルトの型は
objectの代わりにpyarrow.stringを持つArrowDtypeになります。さらに、以下にリストされているすべてのdtypeも、objectとして保存される代わりに推論されます。
これにより、**ユーザーに即座のメリット**がもたらされ、将来的にはさらに大きなメリットをもたらす道が開かれます。
背景
PyArrowは、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が自動的にそのように推論されることを意味します。これには以下が含まれます。
- decimal
- binary
- ネストされた型(リストまたは辞書データ)
- strings
- time
- date
開発者メリット
まず、これによりオプションの依存関係チェックが不要になるため、pyarrowをバックエンドとするデータ型の開発が簡素化されます。
第二に、冗長な機能を削除できる可能性があります。 - read_parquetのfastparquetエンジン; - read_csvロジックの簡素化の可能性(さらなる調査が必要); - 因数分解; - datetime/timezone操作。
欠点
PyArrowを含めることで、当然ながらpandasのインストールサイズが増加します。例えば、pipからホイールを使用してpandasとPyArrowをインストールする場合、numpyとpandasには約70MBが必要であり、PyArrowを含めるとさらに120MBが必要になります。インストールサイズの増加は、AWS Lambdaなどのスペースに制約のある開発またはデプロイ環境でpandasを使用する際に悪影響を及ぼします。
さらに、ユーザーがpip installまたはconda installを通じてホイールが利用できない環境でpandasをインストールする場合、ソースからインストールする際にArrow C++および関連する依存関係もビルドする必要があります。これらの環境には以下が含まれます。
- Alpine Linux(Dockerコンテナのベースとして一般的に使用される)
- Python開発バージョン
最後に、pandasの開発とリリースは、PyArrowの開発とリリースサイクルを考慮する必要があります。例えば、新しくリリースされたPythonバージョンをサポートする場合、pandasは新しいpandasバージョンをリリースする前に、そのPythonバージョンのPyArrowのホイールサポートも考慮する必要があります。
F.A.Q.
Q: pandasはpyarrow文字列とpyarrow構造体ではなく、numpy文字列とnumpy voidデータ型を使用できないのですか?
A: NumPy文字列はまだ利用可能ではありませんが、pyarrow文字列は利用可能です。NumPy voidデータ型はpyarrow構造体とは異なり、他の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日: PyArrowを2.1ではなくpandas 3.0で必須にするという提案に変更
[^1] https://pandas.dokyumento.jp/docs/development/roadmap.html#apache-arrow-interoperability [^2] https://arrow.apache.org/powered_by/