Skip to Content

NULL 처리를 활용한 고객 데이터 정제

초급중급

BigQuery 실행 환경 설정

from google.cloud import bigquery import pandas as pd # 인증 설정 (서비스 계정 키 사용 시) # import os # os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "path/to/your-key.json" # BigQuery 클라이언트 생성 client = bigquery.Client(project='your-project-id') # 쿼리 실행 함수 def run_query(query): return client.query(query).to_dataframe()

학습 목표

이 레시피를 완료하면 다음을 할 수 있습니다:

  • NULL 값의 개념과 데이터 품질 영향 이해
  • IS NULL / IS NOT NULL로 결측치 탐지
  • COALESCE로 NULL 대체 및 우선순위 처리
  • IFNULL로 간단한 기본값 설정
  • CASE WHEN으로 복잡한 조건부 로직 구현
  • NULLIF로 특정 조건에서 NULL 생성

1. NULL 확인 - IS NULL, IS NOT NULL

이론

NULL은 “값이 없음” 또는 “알 수 없음”을 나타냅니다.

주의사항

  • column = NULL ❌ (작동 안 함)
  • column IS NULL ✅ (올바른 방법)
  • NULL과의 모든 연산 결과는 NULL

구문

WHERE column IS NULL WHERE column IS NOT NULL

실습: 고객 데이터 NULL 분석

SELECT 'age' AS column_name, COUNT(*) AS total_rows, COUNT(age) AS non_null_count, COUNT(*) - COUNT(age) AS null_count, ROUND((COUNT(*) - COUNT(age)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.users` UNION ALL SELECT 'gender' AS column_name, COUNT(*) AS total_rows, COUNT(gender) AS non_null_count, COUNT(*) - COUNT(gender) AS null_count, ROUND((COUNT(*) - COUNT(gender)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.users` UNION ALL SELECT 'state' AS column_name, COUNT(*) AS total_rows, COUNT(state) AS non_null_count, COUNT(*) - COUNT(state) AS null_count, ROUND((COUNT(*) - COUNT(state)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.users` ORDER BY null_percentage DESC

실행 결과:

column_nametotal_rowsnon_null_countnull_countnull_percentage
state10000010000000.0
age10000010000000.0
country10000010000000.0
gender10000010000000.0
ℹ️
핵심 개념
  • COUNT(*): 전체 행 수 (NULL 포함)
  • COUNT(column): NULL이 아닌 행 수만 카운트
  • NULL 비율 = (전체 - Non-NULL) / 전체 * 100

퀴즈 1: 주문 아이템 NULL 분석

문제

order_items 테이블에서:

  1. returned_at, shipped_at, delivered_at 컬럼의 NULL 비율 계산
  2. 총 행 수, NULL 개수, NULL 비율(%) 출력
  3. NULL 비율이 높은 순으로 정렬

힌트: COUNT(*) - COUNT(column), UNION ALL

정답 보기

SELECT 'returned_at' AS column_name, COUNT(*) AS total_rows, COUNT(returned_at) AS non_null_count, COUNT(*) - COUNT(returned_at) AS null_count, ROUND((COUNT(*) - COUNT(returned_at)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.order_items` UNION ALL SELECT 'shipped_at' AS column_name, COUNT(*) AS total_rows, COUNT(shipped_at) AS non_null_count, COUNT(*) - COUNT(shipped_at) AS null_count, ROUND((COUNT(*) - COUNT(shipped_at)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.order_items` UNION ALL SELECT 'delivered_at' AS column_name, COUNT(*) AS total_rows, COUNT(delivered_at) AS non_null_count, COUNT(*) - COUNT(delivered_at) AS null_count, ROUND((COUNT(*) - COUNT(delivered_at)) * 100.0 / COUNT(*), 2) AS null_percentage FROM `your-project-id.thelook_ecommerce.order_items` ORDER BY null_percentage DESC

실행 결과:

column_nametotal_rowsnon_null_countnull_countnull_percentage
returned_at1801201805816206289.97
delivered_at1801206322611689464.90
shipped_at1801201170146310635.04

비즈니스 인사이트:

  • returned_at NULL 높음: 대부분 제품이 반품되지 않음 (정상)
  • shipped_at NULL: 아직 배송되지 않은 주문
  • delivered_at NULL: 배송 중이거나 미배송 상태

2. COALESCE - NULL 대체 (우선순위)

이론

COALESCE는 여러 값 중 첫 번째 NOT NULL 값을 반환합니다.

구문

COALESCE(value1, value2, value3, default_value)

활용 예시

  • 여러 연락처 중 유효한 것 선택
  • 기본값 설정
  • 우선순위 기반 선택

실습: state 기본값 설정

SELECT user_id, first_name, last_name, state, country, COALESCE(state, 'Unknown State') AS state_cleaned, COALESCE(country, 'Unknown Country') AS country_cleaned, COALESCE(state, country, 'Unknown Location') AS location FROM `your-project-id.thelook_ecommerce.users` WHERE state IS NULL OR country IS NULL LIMIT 20

퀴즈 2: 평균값으로 NULL 대체

문제

users 테이블에서:

  1. 전체 평균 연령을 계산 (서브쿼리 또는 CTE 사용)
  2. COALESCE로 age가 NULL인 경우 평균 연령으로 대체
  3. 원본 age와 정제된 age_cleaned 비교

힌트: COALESCE(age, (SELECT AVG(age) FROM …))

정답 보기

WITH avg_age AS ( SELECT ROUND(AVG(age), 0) AS average_age FROM `your-project-id.thelook_ecommerce.users` WHERE age IS NOT NULL ) SELECT user_id, first_name, age AS age_original, COALESCE(age, (SELECT average_age FROM avg_age)) AS age_cleaned, CASE WHEN age IS NULL THEN 'Imputed' ELSE 'Original' END AS data_source FROM `your-project-id.thelook_ecommerce.users` LIMIT 1000

결측치 대체 전략:

  • 평균값: 숫자형 데이터, 분포가 정규분포에 가까울 때
  • 중앙값: 이상치가 많을 때 더 안정적
  • 최빈값: 범주형 데이터
  • 그룹별 평균: 성별, 지역별로 다른 평균 적용

3. IFNULL - 간단한 NULL 대체

이론

IFNULL은 COALESCE의 간단한 버전으로 2개 인자만 받습니다.

구문

IFNULL(column, default_value)

COALESCE vs IFNULL

  • COALESCE(a, b, c, d): 여러 값 비교
  • IFNULL(a, b): 단순 대체 (더 간결)

실습: 간단한 기본값 설정

SELECT user_id, first_name, last_name, gender, state, IFNULL(gender, 'Unknown') AS gender_cleaned, IFNULL(state, 'N/A') AS state_cleaned, CONCAT(IFNULL(first_name, ''), ' ', IFNULL(last_name, '')) AS full_name FROM `your-project-id.thelook_ecommerce.users` WHERE gender IS NULL OR state IS NULL LIMIT 30

퀴즈 3: state NULL을 ‘Unknown’으로 대체 후 분석

문제

users 테이블에서:

  1. IFNULL로 state가 NULL인 경우 ‘Unknown’으로 대체
  2. 정제된 state별 고객 수 집계
  3. 상위 15개 주(state)만 조회

힌트: IFNULL(state, ‘Unknown’), GROUP BY, ORDER BY

정답 보기

SELECT IFNULL(state, 'Unknown') AS state_cleaned, COUNT(*) AS customer_count, ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM `your-project-id.thelook_ecommerce.users`), 2) AS percentage FROM `your-project-id.thelook_ecommerce.users` GROUP BY state_cleaned ORDER BY customer_count DESC LIMIT 15

실행 결과:

state_cleanedcustomer_countpercentage
Guangdong53765.38
England41264.13
California37483.75
Shanghai24432.44
Texas24072.41

비즈니스 활용:

  • Unknown 비율이 높음: 데이터 수집 프로세스 개선 필요
  • 지역별 고객 분포: 마케팅 타겟팅, 물류 센터 위치 결정

4. CASE WHEN - 조건부 로직

이론

CASE WHEN은 SQL의 if-else 문입니다.

구문

CASE WHEN condition1 THEN result1 WHEN condition2 THEN result2 ELSE default_result END

활용 예시

  • 고객 세분화 (연령대, 등급 등)
  • 조건부 집계
  • 복잡한 비즈니스 로직 구현

실습: 연령대별 고객 세분화

SELECT user_id, first_name, age, CASE WHEN age IS NULL THEN 'Unknown' WHEN age < 20 THEN '10대' WHEN age < 30 THEN '20대' WHEN age < 40 THEN '30대' WHEN age < 50 THEN '40대' WHEN age < 60 THEN '50대' ELSE '60대+' END AS age_group, CASE WHEN age IS NULL THEN 'N/A' WHEN age >= 40 THEN 'VIP' ELSE 'Regular' END AS customer_tier FROM `your-project-id.thelook_ecommerce.users` LIMIT 50

실행 결과:

user_idfirst_nameageage_groupcustomer_tier
92691Tara4240대VIP
64986Jennifer4240대VIP
46547Kelly4640대VIP
26697Christina2720대Regular

퀴즈 4: 연령대별 VIP/일반 고객 주문 분석

문제

usersorders를 JOIN하여:

  1. CASE WHEN으로 연령대 분류 (10대, 20대, 30대, 40대+, Unknown)
  2. CASE WHEN으로 고객 등급 분류:
    • age >= 40: ‘VIP’
    • age < 40: ‘Regular’
    • age IS NULL: ‘Unknown’
  3. 연령대별, 등급별 주문 건수와 평균 아이템 수 계산

힌트: JOIN, GROUP BY age_group, customer_tier

정답 보기

SELECT CASE WHEN u.age IS NULL THEN 'Unknown' WHEN u.age < 20 THEN '10대' WHEN u.age < 30 THEN '20대' WHEN u.age < 40 THEN '30대' ELSE '40대+' END AS age_group, CASE WHEN u.age IS NULL THEN 'Unknown' WHEN u.age >= 40 THEN 'VIP' ELSE 'Regular' END AS customer_tier, COUNT(DISTINCT o.order_id) AS order_count, AVG(o.num_of_item) AS avg_items_per_order FROM `your-project-id.thelook_ecommerce.users` u JOIN `your-project-id.thelook_ecommerce.orders` o ON u.user_id = o.user_id GROUP BY age_group, customer_tier ORDER BY CASE age_group WHEN '10대' THEN 1 WHEN '20대' THEN 2 WHEN '30대' THEN 3 WHEN '40대+' THEN 4 WHEN 'Unknown' THEN 5 END, customer_tier

실행 결과:

age_groupcustomer_tierorder_countavg_items_per_order
10대Regular168611.44
20대Regular213851.45
30대Regular210761.45
40대+VIP654711.44

비즈니스 인사이트:

  • VIP 고객 (40대+): 주문 건수와 구매량 패턴 분석
  • Regular 고객 (20-30대): 성장 가능성 높은 세그먼트

5. NULLIF - 조건부 NULL 생성

이론

NULLIF는 두 값이 같으면 NULL을 반환합니다.

구문

NULLIF(expression1, expression2) -- expression1 == expression2이면 NULL, 아니면 expression1

활용 예시

  • 0으로 나누기 방지: value / NULLIF(count, 0)
  • 기본값 제거: NULLIF(column, 'default')
  • 무의미한 값 제외

실습: NULLIF로 0 나누기 방지

SELECT p.category, COUNT(*) AS total_products, SUM(CASE WHEN oi.order_id IS NOT NULL THEN 1 ELSE 0 END) AS sold_products, -- NULLIF로 0 나누기 방지 ROUND( SUM(CASE WHEN oi.order_id IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2 ) AS sell_through_rate FROM `your-project-id.thelook_ecommerce.products` p LEFT JOIN `your-project-id.thelook_ecommerce.order_items` oi ON p.product_id = oi.product_id GROUP BY p.category ORDER BY sell_through_rate DESC LIMIT 15

퀴즈 5: 안전한 평균 계산

문제

orders에서:

  1. status별로 그룹화
  2. 주문 건수, 총 아이템 수, 평균 아이템 수 계산
  3. NULLIF로 0 나누기 방지하여 평균 계산

힌트: SUM(num_of_item) / NULLIF(COUNT(*), 0)

정답 보기

SELECT status, COUNT(*) AS order_count, SUM(num_of_item) AS total_items, ROUND(SUM(num_of_item) / NULLIF(COUNT(*), 0), 2) AS avg_items_per_order FROM `your-project-id.thelook_ecommerce.orders` GROUP BY status ORDER BY order_count DESC

실행 결과:

statusorder_counttotal_itemsavg_items_per_order
Shipped37302537881.44
Complete31161451681.45
Processing25171362051.44
Cancelled18648269011.44
Returned12511180581.44

NULLIF 활용 시나리오:

  • 0 나누기 방지: value / NULLIF(denominator, 0)
  • 무의미한 값 제거: NULLIF(column, '')
  • 기본값 필터링: NULLIF(column, 'N/A')

정리

학습한 NULL 처리 함수

함수용도예시
IS NULLNULL 확인WHERE age IS NULL
COALESCE(a,b,c)첫 번째 NOT NULL 값COALESCE(state, country, ‘Unknown’)
IFNULL(a,b)간단한 대체IFNULL(age, 0)
CASE WHEN조건부 로직연령대 분류, 고객 등급
NULLIF(a,b)조건부 NULL 생성NULLIF(count, 0)

NULL 처리 베스트 프랙티스

  1. 데이터 품질 모니터링

    • 정기적으로 NULL 비율 측정
    • 임계값 초과 시 알림
  2. 적절한 대체 전략

    • 숫자: 평균, 중앙값, 0
    • 범주: 최빈값, ‘Unknown’
    • 날짜: 특정 기준일, 이전/다음 값
  3. 비즈니스 로직 구현

    • CASE WHEN으로 복잡한 분류
    • 고객 세분화, 제품 등급화
  4. 안전한 연산

    • NULLIF로 0 나누기 방지
    • COALESCE로 기본값 보장

다음 단계

NULL 처리를 마스터했습니다! 다음으로 CTE와 코호트 분석에서 복잡한 쿼리 구조화와 고급 분석 기법을 배워보세요.

Last updated on

🤖AI 모의면접실전처럼 연습하기