Apache Spark™ 3.2におけるPandas APIのサポート

[How to Execute Pandas Workloads in a Distributed Manner With Apache Spark \- The Databricks Blog](https://databricks.com/blog/2021/10/04/pandas-api-on-upcoming-apache-spark-3-2.html)の翻訳です。

来たるApache Spark™ 3.2リリースの一部としてpandas APIを発表できることを嬉しく思います。pandasはパワフルかつ柔軟性のあるライブラリであり、データサイエンスライブラリの標準として急速に成長しました。今では、[pandas](https://pandas.pydata.org/docs/)のユーザーは既存Sparkクラスターでpandas APIを活用することができます。

数年前、我々はSpark上でpandas DataFrame APIを実装するオープンソースプロジェクト[Koalas](https://koalas.readthedocs.io/en/latest/)を立ち上げ、これはデータサイエンティストの皆様に広く受け入れられました。最近、[Project Zen](https://issues.apache.org/jira/browse/SPARK-32082)(Data + AI Summit 2021の[Project Zen: Making Data Science Easier in PySpark](https://databricks.com/session_na21/project-zen-making-data-science-easier-in-pyspark)もご覧ください)の一環として[SPIP: Support pandas API layer on PySpark](https://issues.apache.org/jira/browse/SPARK-34849)によって、Koalasは正式にPySparkに統合されました。

pandasのユーザーは来たるSpark 3.2リリースでは、1行の変更のみで今のワークロードをスケールさせることができるようになります。
![code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7ace0544-58c9-b91b-bb15-3d0c776f1e1c.png)

この記事では、Spark 3.2におけるpandas APIサポートを要約し、特筆すべき機能、変更、ロードマップをハイライトします。

# シングルマシンの向こうのスケーラビリティ

pandasにおける既知の制限の一つは、シングルマシンの処理であるために、データボリュームに対して線形にスケールしないというものです。例えば、pandasにおいて、シングルマシンで利用できるメモリーよりも大きいデータセットを読み込もうとすると、アウトオブメモリーで処理が失敗します。
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/1d35b3ce-a7d3-5f7a-1661-5581a295ad22.png)
*pandas: 大規模CSVの読み込みによるアウトオブメモリー*

Sparkにおけるpandas APIはこの制限を乗り越え、Sparkを活用することでユーザーは大規模データセットを取り扱えるようになります。
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d82b939b-7795-104e-f6ec-9b376a0282df.jpeg)

Sparkにおけるpandas APIは大規模クラスターノードにおいても適切にスケールします。以下のグラフでは、異なるサイズのクラスターにおいて15TBのParquetデータセットを分析した際の性能を示しています。クラスターのそれぞれのマシンは8個のvCPU、61GBメモリーを搭載しています。
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/aac08ecb-ba07-02b5-7388-18bd316b1318.jpeg)
*Sparkにおけるpandas APIのスケールアウト*

テストにおいて、Sparkにおけるpandas APIの分散実行はほぼ線形にスケールしました。クラスターのマシンの数が倍になると、処理時間は半分に減少します。シンブルマシンと比較してスピードアップの度合いは劇的なものになります。例えば、*標準偏差(Standard deviation)*のベンチマークにおいては、256台のマシンのクラスターは、シングルマシンのほぼ250倍のデータを同じ時間で処理することができています(それぞれのマシンは8個のvCPU、61GBメモリーを搭載しています)。

|  | シングルマシン | 256台のクラスター |
|:--|:--|:--|
|Parquetデータセット   | 60GB  | 60GB x 250(15TB)  |
|標準偏差の処理時間(秒)   | 12s  | 10s  |

# 最適化されたシングルマシンのパフォーマンス

Sparkにおけるpandas APIは、Sparkエンジンの最適化によって、シングルマシンにおいても多くのケースでpandasの性能を上回ります。以下のグラフはシングルマシンで130GBのCSVデータセットに対して処理を行った際のpandasの性能とSparkのpandas APIの性能を比較したものです。
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7d01dea0-07e7-2f64-8864-dd2a430c4cce.png)
*pandas vs. Sparkのpandas API*

マルチスレッド処理とSpark SQLの[Catalyst Optimizer](https://databricks.com/jp/glossary/catalyst-optimizer)の両方が性能の最適化に貢献しています。例えば、*Join count*オペレーションはwhole-stageコード生成によって、4倍程度高速になります。コード生成なしで5.9秒、コード生成ありで1.6秒になります。

Sparkは特に処理を連鎖させる際に優位となります。pandasにおいては、ステップごとに全てのデータをメモリにロードする傾向がある一方、Catalystクエリーオプティマイザは、賢くデータをスキップするために、フィルタリング処理を認識し、ディスクベースのJOINを適用することができます。

フィルタリングされた二つのデータフレームをJOINし、JOINされたデータフレームの平均を計算するクエリーを考えてみます。Sparkのpandas APIは4.5秒で処理を完了しますが、以下のようにpandasにおいてはアウトオブメモリー(OOM)エラーで失敗します。
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/6790b624-e2bf-f19b-3208-015fd6b7622e.jpeg)
*pandas: オペレーションの連鎖でアウトオブメモリーに*

# インタラクティブなデータの可視化

pandasは、静的なプロットチャートを提供する[matplotlib](https://matplotlib.org/)をデフォルトで使用します。例えば、以下のコードは静的なチャートを生成します。

```py:Python
# Area
pandas.DataFrame(
    np.random.rand(100, 4), columns=list("abcd")).plot.area()
```
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/04452261-a05f-4d10-c877-7a19f73942bc.png)

一方で、Sparkのpandas APIは、インタラクティブなチャートを提供する[plotly](https://plotly.com/python/)をデフォルトで使用します。例えば、これによってユーザーはインタラクティブにズームイン、ズームアウトが行えます。プロットのタイプに基づき、Sparkのpandas APIはインタラクティブなチャートを生成する際、内部でベストな計算実行方法を自動で決定します。

```py:Python
# Area
pyspark.pandas.DataFrame(
    np.random.rand(100, 4), columns=list("abcd")).plot.area()
```
![Pandas-API-on-Upcoming-Apache-Spark-3.2-blog-img-8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d0ceec6d-fc72-c282-d906-00059cc736c5.jpeg)

# Sparkにおける統合分析機能の活用

pandasはバッチ処理によるPythonデータサイエンス向けに設計されていますが、SparkはSQL、ストリーミング処理、機械学習を含む統合分析向けに設計されています。これらのギャップを埋めるために、Sparkのpandas APIは以下のように、上級ユーザーがSparkエンジンを活用できるように異なる手段を提供しています。

- 以下のように、Sparkの最適化されたSQLエンジンを用いて、SQLでデータを直接クエリーします。

```py:Python
>>> import pandas as pd
>>> import pyspark.pandas as ps
>>> pdf = pd.DataFrame({"a": [1, 3, 5]})  # pandas DataFrame
>>> sdf = spark.createDataFrame(pdf)  # PySpark DataFrame
>>> psdf = sdf.to_pandas_on_spark()  # pandas-on-Spark DataFrame
>>> # Query via SQL
... ps.sql("SELECT count(*) as num FROM {psdf}")
```


- Pythonオブジェクトと自然にやり取りするために、文字列を内挿する文法もサポートしています。

```py:Python
>>> pred = range(4)
>>> # String interpolation with Python instances
... ps.sql("SELECT * from {psdf} WHERE a IN {pred}")
```

- Sparkのpandas APIはストリーミング処理もサポートしています。

```py:Python
>>> def func(sdf, _):
...     # pandas-on-Spark DataFrame
...     psdf = sdf.to_pandas_on_spark()
...     psdf.describe()
...
>>> spark.readStream.format(
...     "kafka").load().writeStream.foreachBatch(func).start()
```

- Sparkのスケーラブルな機械学習ライブラリを容易に呼び出すことも可能です。

```py:Python
>>> from pyspark.ml.feature import StringIndexer
>>> sdf = psdf.to_spark()  # PySpark DataFrame
>>> indexer = StringIndexer(
...     inputCol="category", outputCol="categoryIndex")
>>> indexed = indexer.fit(sdf).transform(sdf)
>>> indexed.show()
```

PySparkとSparkのpandas APIの相互運用性に関しては、こちらの[記事](https://databricks.com/blog/2020/08/11/interoperability-between-koalas-and-apache-spark.html)もご覧ください。

# 次に来るのは？

次のSparkリリースに向けて、ロードマップでは以下にフォーカスしています。

- **更なるタイプヒント** Sparkのpandas APIのコードは現状部分的に型付けされており、静的分析とオートコンプリートを可能にしています。将来的には、コード全てが完全に型付けされます。
- **パフォーマンス改善** Sparkのpandas APIには性能改善の余地がいくつかあります。これらは、エンジンとSQLオプティマイザをより密連携させることで改善することができます。
- **安定性** NaNやNAのような欠損値において、例外的なケースで異なる振る舞いをする場合があるなど、修正が必要な箇所が存在しています。さらに、Sparkのpandas APIはこれらのケースにおいて最新バージョンの挙動に従うようにする予定です。
- **APIカバレッジの強化** Sparkのpandas APIによるpandas APIのカバレッジは83%に到達しており、さらに増加し続けています。現状のターゲットは90%です。

バグや必要とする機能の欠如に直面した場合には、[問題をファイル](https://issues.apache.org/jira/projects/SPARK)してください。もちろん、コミュニティへの貢献も歓迎しています。

# 使い始めてみる
![pandas-api-blog-img-last.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/846e7cf8-2159-4c89-4cf7-2f116e773725.png)
*Databricks Runtime 10.0(beta)*

:::note warn
Databricks Runtime 10.0はデフォルトでは表示されません(2021/10/10時点)。上のようにDatabricks Runtime Versionで「10.0」と入力してください。
:::

Databricks Runtime 10.0 Beta(Apache Spark 3.2)でSparkのpandas APIを試してみたいのであれば、[Databricksコミュニティエディション、あるいは、フリートライアル](https://databricks.com/jp/try-databricks)にサインアップしてください。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)