Pysparkのnull値の扱いと操作について

本記事は、Pyspark操作時のnullの扱いや、nullに関わる関数の実行結果について、簡単にまとめたものとなっております。

# 0 データ準備
各操作では、以下のデータフレームを使用して行うものとする。
(データフレームの名前は**df**とする。)

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  001  |  apple | 100 |
|  002  |  banana | 80 |
| null | orange | 120 |
| 004 | peach | null |



# 1 実行例

### ①nullを含む値の並び替え

Pysparkでは、`sort()`や`orderBy()`の使用時、デフォルトの場合ではnullが先頭に来るように並び替えられる。

```ruby:Pyspark
df1 = df.orderBy("id")
display(df1)
```
**実行結果** 

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  null  |  orange | 120 |
|  001  |  apple | 100 |
| 002 | banana| 80 |
| 004 | peach | null |

nullが一番後ろに来るように並び替えたい場合には、
昇順 → `asc_nulls_last()` 
降順 → `desc_nulls_last()`
を使用する。

```ruby:Pyspark
from pyspark.sql.functions import col 

df2 = df.orderBy(col("id").asc_nulls_last())
display(df2)
```

**実行結果**

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  001  |  apple | 100 |
|  002  |  banana | 80 |
| 004 | peach | null |
| null | orange | 120 |

参考
[pyspark.sql.DataFrame.orderBy](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.orderBy.html)
[pyspark.sql.Column.asc_nulls_last](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.Column.asc_nulls_last.html?highlight=asc_nulls_last#pyspark.sql.Column.asc_nulls_last)
[pyspark.sql.functions.desc_nulls_last](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.functions.desc_nulls_last.html)


### ②nullを含む行の消去
`dropna()`を用いる事で、列内にnullが1つでも含まれている行の消去が可能である。

```ruby:Pyspark
df1 = df.dropna()
display(df1)
```

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  001  |  apple | 100 |
|  002  |  banana | 80 |

またオプションとして、`how='all'`をつける事で、全ての値がnullである行を消去する事も可能である。
(今回の例のデータフレームではそのような行は存在しないため、そのままのデータが返される。)

詳しいオプションについては、以下のドキュメントを参照。

参考
[pyspark.sql.DataFrame.dropna](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.dropna.html)

### ③nullを含む行の抽出
`isNull()`を用いる事で、特定のカラムでnullを含む行のみを取り出すことが可能である。

```ruby:Pyspark
df1 = df.filter(col("id").isNull())
display(df1)
```

**実行結果**

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  null  |  orange | 120 |


また、`isNotNull()`を用いる事で、nullを含まない行のみを取り出すことも可能である。

```ruby:Pyspark
df2 = df.filter(col("id").isNotNull())
display(df2)
```

**実行結果**

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  001  |  apple | 100 |
|  002  |  banana | 80 |
| 004 | peach | null |

参考
[pyspark.sql.Column.isNull](URL:https://spark.apache.org/docs/3.1.1/api/python//reference/api/pyspark.sql.Column.isNull.html)
[pyspark.sql.Column.isNotNull](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.Column.isNotNull.html)

### ④nullの置き替え
`fillna()`を用いる事で、nullを別の値に置き替える事が可能である。

```ruby:Pyspark
df1 = df.fillna('unknown')
display(df1)
```

**実行結果**

|  id  |  item_name  | price |
| ---- | ---- | ---- |
|  001  |  apple | 100 |
|  002  |  banana | 80 |
| unknown | orange | 120 |
| 004 | peach | null |

引数内のデータ型と一致するカラムのみが置き替えの対象となるため、**id**列のnullは`unknown`に置き換わるが、**price**列のnullは変化しない。

また、`coalesce()`を用いる事で、nullを別のカラムの値で置き替えることが可能である。

```ruby:Pyspark
from pyspark.sql.functions import coalesce

df2 = df.select(coalesce(col("id"), col("item_name")))
display(df2)
```

**実行結果**

|coalesce(id, item_name)|
|----|
|001| 
|002|
|orange|
|004|

上記の例では、**id**列内のnullについて、**item_name**列の同じ行の値(orange)に置き換えられている。

また下記の例のように、`lit()`を組み合わせる事で、nullを特定の固定値に設定し直す事も可能である。

```ruby:Pyspark
from pyspark.sql.functions import lit

df3 = df.select(coalesce(col("id"), lit("unknown")))
display(df3)
```

**実行結果**

|coalesce(id, item_name)|
|----|
|001| 
|002|
|unknown|
|004|

参考
[pyspark.sql.DataFrame.fillna](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.fillna.html)
[pyspark.sql.functions.coalesce](URL:https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.functions.coalesce.html)