Skip to content

PySpark での Null と NA の扱い: 実践クリーニングレシピ

Updated on

Null を放置すると、集計値・JOIN・エクスポートが壊れます。一方で、意図の曖昧な dropna 呼び出しは、有用な行まで静かに消してしまうことがあります。チームには、PySpark で Null を「調査する・埋める・捨てる・安全に比較する」ための明確なパターンが必要です。

PySpark の組み込み Null ハンドリング機能 ― fillnadropnana.replacecoalesce、Null セーフ比較 ― を使えば、パイプラインの挙動を予測可能かつ透明に保てます。

Python Pandas DataFrame からノーコードでデータ可視化を素早く作りたいですか?

PyGWalker は可視化つき Exploratory Data Analysis 用の Python ライブラリです。PyGWalker (opens in a new tab) を使うと、pandas dataframe(および polars dataframe)を、Jupyter Notebook 上でのデータ分析・可視化ワークフローにおける tableau 代替のユーザーインターフェースへと変換し、インタラクティブな探索を簡単に行えます。

PyGWalker for Data visualization (opens in a new tab)

クイック判断ガイド

目的APIメモ
デフォルト値で埋めるfillnaカラムごとに dict 指定・型を合わせる
スパースな行を落とすdropnahowthreshsubset を制御
ダミー値を置換na.replaceプレースホルダ文字列・数値を入れ替え
最初の非 Null を選ぶcoalesce優先度順にカラムを結合
Null セーフな等価比較<=> または eqNullSafeNull 同士を等しいとみなす

セットアップ

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")))
)

このシーケンスにより、ダミー値を標準化し、必須カラムを満たさない行を落とし、業務的に妥当なデフォルトで埋め、レポーティング向けの明確なフォールバック列を残せます。