PySpark での Null と NA の扱い: 実践クリーニングレシピ
Updated on
Null を放置すると、集計値・JOIN・エクスポートが壊れます。一方で、意図の曖昧な dropna 呼び出しは、有用な行まで静かに消してしまうことがあります。チームには、PySpark で Null を「調査する・埋める・捨てる・安全に比較する」ための明確なパターンが必要です。
PySpark の組み込み Null ハンドリング機能 ― fillna、dropna、na.replace、coalesce、Null セーフ比較 ― を使えば、パイプラインの挙動を予測可能かつ透明に保てます。
Python Pandas DataFrame からノーコードでデータ可視化を素早く作りたいですか?
PyGWalker は可視化つき Exploratory Data Analysis 用の Python ライブラリです。PyGWalker (opens in a new tab) を使うと、pandas dataframe(および polars dataframe)を、Jupyter Notebook 上でのデータ分析・可視化ワークフローにおける tableau 代替のユーザーインターフェースへと変換し、インタラクティブな探索を簡単に行えます。
クイック判断ガイド
| 目的 | API | メモ |
|---|---|---|
| デフォルト値で埋める | fillna | カラムごとに dict 指定・型を合わせる |
| スパースな行を落とす | dropna | how、thresh、subset を制御 |
| ダミー値を置換 | na.replace | プレースホルダ文字列・数値を入れ替え |
| 最初の非 Null を選ぶ | coalesce | 優先度順にカラムを結合 |
| Null セーフな等価比較 | <=> または eqNullSafe | Null 同士を等しいとみなす |
セットアップ
from pyspark.sql import SparkSession, functions as F
spark = SparkSession.builder.appName("null-handling").getOrCreate()
df = spark.createDataFrame(
[
(1, "Alice", None, None),
(2, "Bob", "basic", 0),
(3, None, "premium", None),
],
"id INT, name STRING, tier STRING, score INT",
)Null パターンの把握
null_stats = df.select(
*[
F.sum(F.col(c).isNull().cast("int")).alias(f"{c}_nulls")
for c in df.columns
]
)- 各カラムの Null 件数を数えたうえで、「埋めるか・落とすか」を決める。
欠損値を埋める(fill)
filled = df.fillna({"name": "unknown", "tier": "basic", "score": 0})- dict で指定して型を揃える。数値カラムを文字列で埋めるのは避ける。
- 日付・タイムスタンプは、
to_date('1970-01-01')のようなドメイン固有のデフォルトを使う。
行を選択的に削除(drop)
drop_sparse = df.dropna(thresh=3) # 非 Null が 3 列以上ある行だけ残す
drop_missing_tier = df.dropna(subset=["tier"])threshを使うと「消しすぎ」を防げる。- すべてのカラムに対する一括
dropna()よりも、必要なカラムだけをsubsetで指定する方が、有用な行を残しやすい。
ダミー値(sentinel)の置換
clean_sent = df.na.replace({"N/A": None, "": None}, subset=["tier", "name"])"N/A"や空文字などのプレースホルダを、fill や drop を行う前に正しい Null に変換しておく。
coalesce で最初の非 Null を選ぶ
with_fallback = df.withColumn("primary_tier", F.coalesce("tier", F.lit("basic")))coalesceは引数リストの中で最初に現れる非 Null を返す。
Null セーフ比較
matches = df.where(F.expr("tier <=> 'premium'"))<=>(またはeqNullSafe)は、Null 同士を等しいものとして扱う。通常の=は Null 同士の比較で結果が Null になり、フィルタ条件としては偽扱いになる。
下流の JOIN を安全にする
- JOIN キーを一律に埋めるのではなく、業務ルール上許される場合にのみ fill する。それ以外は明示的に Null キーをフィルタする。
- ファクトテーブルとディメンションの JOIN では、「JOIN の前に Null キー行を削除するか」「残しておき、後段で特別扱いするか」をあらかじめ決めておく。
最小限のクリーニング・パイプライン例
clean = (
df
.na.replace({"N/A": None, "": None})
.dropna(subset=["name"]) # name は必須
.fillna({"tier": "basic", "score": 0})
.withColumn("tier_clean", F.coalesce("tier", F.lit("basic")))
)このシーケンスにより、ダミー値を標準化し、必須カラムを満たさない行を落とし、業務的に妥当なデフォルトで埋め、レポーティング向けの明確なフォールバック列を残せます。
