pandas使いがDatabricksで詰まりそうなポイントと解決策

Databricksではpandasを使えるのですが、その過程でやりたいことによっては詰まることもあるかと思います。こちらでは、その際のティップスをまとめます。DatabricksではSparkを前提としている機能が多いので、場合によってはSparkデータフレームへの変換が必要となります。

話がややこしくなるので、Pandas API on Sparkに関しては一旦脇に置いておきます。

https://qiita.com/taka_yayoi/items/db8e1ea52afe5c282c94

# pandasデータフレームをテーブルとして保存したい

pandasデータフレームでは直接テーブルに書き込めないので、一旦Sparkデータフレームに変換してから保存します。

```py:Python
import pandas as pd

data = [
    ["James", "", "Smith", 30, "M", 60000],
    ["Michael", "Rose", "", 50, "M", 70000],
    ["Robert", "", "Williams", 42, "", 400000],
    ["Maria", "Anne", "Jones", 38, "F", 500000],
    ["Jen", "Mary", "Brown", 45, None, 0],
]
columns = ["FirstName", "MiddleName", "LastName", "Age", "Gender", "Salary"]

# pandasデータフレームの作成
pandasDF = pd.DataFrame(data=data, columns=columns)

# データフレームの表示
print(pandasDF)
```

```
  FirstName MiddleName  LastName  Age Gender  Salary
0     James                Smith   30      M   60000
1   Michael       Rose             50      M   70000
2    Robert             Williams   42         400000
3     Maria       Anne     Jones   38      F  500000
4       Jen       Mary     Brown   45   None       0
```

`saveAsTable`を用いて書き込み先のテーブルを指定します。ここでは、`takaakiyayoi_catalog.default.sample_table`を指定します。

```py:Python
# Sparkデータフレームへの変換
sparkDF = spark.createDataFrame(pandasDF) 

# テーブルへの書き込み
sparkDF.write.mode("overwrite").saveAsTable("takaakiyayoi_catalog.default.sample_table")
```

カタログエクスプローラからテーブルを確認できます。
![Screenshot 2023-11-08 at 19.12.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/ab297823-17ee-b88e-05d9-8dbfa6c84644.png)

# テーブルからpandasデータフレームに読み込みたい

こちらも、一旦Sparkデータフレームに読み込んだあとでpandasデータフレームに変換します。

```py:Python
# テーブルからの読み込み
newSparkDF = spark.read.table("takaakiyayoi_catalog.default.sample_table")

# pandasデータフレームへの変換
newPandasDF = newSparkDF.toPandas()
newSparkDF.show()
```
```
+---------+----------+--------+---+------+------+
|FirstName|MiddleName|LastName|Age|Gender|Salary|
+---------+----------+--------+---+------+------+
|    James|          |   Smith| 30|     M| 60000|
|  Michael|      Rose|        | 50|     M| 70000|
|   Robert|          |Williams| 42|      |400000|
|    Maria|      Anne|   Jones| 38|     F|500000|
|      Jen|      Mary|   Brown| 45|  NULL|     0|
+---------+----------+--------+---+------+------+
```


# 単一のCSVファイルとして保存したい

Sparkデータフレームでデータを加工した後で、単一のCSVファイルとして保存したいというのはよくある話かと思います。ただ、何も気にせずにSparkの`write`で保存すると期待した通りに動作しません。以下の例では[ボリューム](https://qiita.com/taka_yayoi/items/6f57f2bebd15fcb49976)にCSVファイルを出力しています。

```py:Python
sparkDF.write.format("csv").save("/Volumes/takaakiyayoi_catalog/default/data/sample_table.csv")
```

このように、ファイルではなくディレクトリが作成されます。
![Screenshot 2023-11-08 at 19.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/ea1e29e2-e7b8-c676-8797-4826557670ba.png)

ディレクトリには複数のファイルが作成されています。これは、Sparkが分散処理のためにファイルを[パーティション](https://qiita.com/taka_yayoi/items/bb32d5b7abafd600af98)という単位に分割する仕様となっているためです。設定で1ファイルにまとめることはできますが、メモリの制約がなければ素直にpandasデータフレームに変換してから保存した方が楽です。
![Screenshot 2023-11-08 at 19.19.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/2c57b0b8-e25d-99f3-d8c8-430231429a8a.png)

```py:Python
pandasDFtoWrite = sparkDF.toPandas()
pandasDFtoWrite.to_csv("/Volumes/takaakiyayoi_catalog/default/data/sample_table_file.csv")
```
これで単一のCSVファイルが作成されます。ボリュームにはpandas APIからも普通にアクセスできます。
![Screenshot 2023-11-08 at 19.22.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/ee86100d-3c80-ec7b-6d17-98acf8fbfd0f.png)

ボリュームにはシェルからもアクセスできます。

```sh:shell
%sh
cat /Volumes/takaakiyayoi_catalog/default/data/sample_table_file.csv
```

```
,FirstName,MiddleName,LastName,Age,Gender,Salary
0,James,,Smith,30,M,60000
1,Michael,Rose,,50,M,70000
2,Robert,,Williams,42,,400000
3,Maria,Anne,Jones,38,F,500000
4,Jen,Mary,Brown,45,,0
```

途中からボリュームが便利だという話に変わってしまった気がしなくもありません。

# 参考資料

- [サンプルを通じたPandasとPySparkデータフレームの比較](https://qiita.com/taka_yayoi/items/5f1ab08c6a97ba96bb1f)
- [PySparkことはじめ](https://qiita.com/taka_yayoi/items/a7ee6287031374efa88a#pandas%E3%81%A8%E3%81%AE%E3%82%84%E3%82%8A%E3%81%A8%E3%82%8A)
- [DatabricksにおけるPySpark、pandasデータフレームの変換の最適化](https://qiita.com/taka_yayoi/items/063c2360670f59b38471)
- [PySparkとPandasのDataFrameの相互変換](https://qiita.com/aikige/items/42d782d000b6959a424d)
- [PySpark: シングルファイルとしてCSVを保存する \- connecting the dots](https://ktksq.hatenablog.com/entry/pyspark-save-csv)
- [Databrikcs\(Spark\)のPysparkにて単一ファイルとしてCSVファイルを書き込む方法](https://qiita.com/manabian/items/78b57741885ecda8570a)
- [Solved: When I save a Spark dataframe using df\.write\.forma\.\.\. \- Databricks \- 21582](https://community.databricks.com/t5/data-engineering/when-i-save-a-spark-dataframe-using-df-write-format-quot-csv/td-p/21582)


### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

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