PySpark Null과 NA 다루기: 실용적인 클리닝 레시피
Updated on
Null을 방치하면 지표 계산, 조인, 내보내기(export)가 모두 깨진다. 반대로 무심코 dropna를 호출하면 중요한 행이 조용히 사라질 수 있다. 팀은 PySpark에서 null을 점검하고, 채우고, 삭제하고, 안전하게 비교하기 위한 명확한 패턴이 필요하다.
PySpark가 제공하는 기본 null 처리 도구들—fillna, dropna, na.replace, coalesce, 그리고 null-safe 비교—을 활용하면 파이프라인을 예측 가능하고 투명하게 유지할 수 있다.
Python Pandas Dataframe에서 No code로 빠르게 Data Visualization을 만들고 싶나요?
PyGWalker는 Visualization 기반 Exploratory Data Analysis를 위한 Python 라이브러리입니다. PyGWalker (opens in a new tab)는 pandas dataframe(및 polars dataframe)을 시각적 탐색을 위한 tableau 대체 User Interface로 전환하여, Jupyter Notebook에서의 데이터 분석과 시각화 워크플로를 단순화해 줍니다.
빠른 의사결정 가이드
| 목표 | API | 메모 |
|---|---|---|
| 기본값으로 채우기 | fillna | 컬럼별 dict 사용; 타입을 맞출 것 |
| 듬성듬성한 행 제거 | dropna | how, thresh, subset 제어 |
| 특수 표식(sentinel) 값 치환 | na.replace | 플레이스홀더 문자열·숫자 교체 |
| 첫 번째 non-null 선택 | coalesce | 우선순위에 따라 컬럼 결합 |
| Null-safe 동등 비교 | <=> 또는 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
]
)- 채우거나(drop/fill) 삭제하기 전에 컬럼별 null 개수를 먼저 확인한다.
결측값 채우기
filled = df.fillna({"name": "unknown", "tier": "basic", "score": 0})- dict를 사용해 컬럼별로 지정하면 타입을 정합하게 유지할 수 있다. 숫자 컬럼을 문자열로 채우는 것은 피할 것.
- 날짜/타임스탬프 컬럼은 도메인에 맞는 기본값(
to_date('1970-01-01')등)을 사용한다.
선택적으로 행 삭제하기
drop_sparse = df.dropna(thresh=3) # non-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"])- 채우기나 삭제를 수행하기 전에, 플레이스홀더 문자열을 올바른 null로 변환해 둔다.
Coalesce로 첫 번째 non-null 선택
with_fallback = df.withColumn("primary_tier", F.coalesce("tier", F.lit("basic")))coalesce는 인자 목록에서 첫 번째 non-null 값을 반환한다.
Null-safe 비교
matches = df.where(F.expr("tier <=> 'premium'"))<=>(또는eqNullSafe)는 두 값이 모두 null인 경우를 “같다”로 본다. 일반적인=비교는 null 비교 시 null을 반환한다.
이후 단계의 조인 보호하기
- 조인 키를 채워 넣을지는 비즈니스 규칙에 따라 신중히 결정하고, 그렇지 않다면 null 키는 명시적으로 필터링한다.
- fact-to-dimension 조인의 경우, 조인 전에 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")))
)이 순서는 플레이스홀더를 표준화하고, 필수 필드를 보존하며, 비즈니스 기본값으로 채우고, 리포팅을 위한 명확한 fallback 컬럼을 남긴다.
