본문으로 건너뛰기
김신건의 로그

[Pandas] clip / where / mask

· 수정 · 📖 약 1분 · 425자/단어 #python #pandas #conditional #clip
Pandas clip, Pandas where, Pandas mask, 조건부 갱신 pandas

정의

  • clip(lower, upper) : 범위 밖 값을 경계로 자르기 (outlier 처리)
  • where(cond, other) : cond 가 True 면 유지, False 면 other 로 치환
  • mask(cond, other) : where 의 반대 (cond True 가 치환)

어떤 것을 쓸까

flowchart TD
    A["값을 조건부로 바꾸고 싶다"] --> B{"어떤 목적?"}
    B -->|"범위 제한 (outlier clamp)"| C["clip(lower, upper)"]
    B -->|"True 면 유지 / False 면 치환"| D["where(cond, other)"]
    B -->|"True 면 치환 / False 면 유지"| E["mask(cond, other)"]
    B -->|"if-elif-else 다중 분기"| F["np.select(conditions, choices)"]
    B -->|"단순 2분기 (numpy)"| G["np.where(cond, a, b)"]

    C --> H["s.clip(0, 100)"]
    D --> I["s.where(s > 0, 0)"]
    E --> J["s.mask(s < 0, 0)"]
    F --> K["np.select([c1, c2], [v1, v2], default)"]

사용 상황

상황도구
음수 값을 0 으로 고정s.clip(lower=0)
백분위 기준 이상치 제거s.clip(q1, q99)
조건 불만족 행 NaN 처리s.where(cond)
특정 조건 행을 다른 값으로s.mask(cond, replacement)
여러 조건 분기 (연령대 분류 등)np.select

clip

s.clip(lower=0)               # 0 미만 → 0
s.clip(upper=100)              # 100 초과 → 100
s.clip(lower=0, upper=100)     # [0, 100] 범위로
python
import pandas as pd
s = pd.Series([-5, 0, 50, 150, 200])
print(s.clip(0, 100).tolist())
결과
[0, 0, 50, 100, 100]
originalclipped (0, 100)
-50
00
5050
150100
200100

컬럼별 다른 경계

df.clip(lower={'a': 0, 'b': -10}, upper={'a': 100, 'b': 10})

분위수 기반 clip

lower = df['amount'].quantile(0.01)
upper = df['amount'].quantile(0.99)
df['amount_clipped'] = df['amount'].clip(lower, upper)

실무에서 outlier 처리 패턴으로 자주 쓰인다.

where

s.where(cond, other)        # cond True 면 그대로, False 면 other

IMPORTANT

s.where(cond, other)cond 가 True 일 때 그대로 유지. numpy 의 np.where(cond, a, b) 와 의미가 정반대다.

python
import pandas as pd
s = pd.Series([10, 20, 30, 40])
print(s.where(s > 20, 0).tolist())
결과
[0, 0, 30, 40]

s > 20 이 False 인 위치 (10, 20) 가 0 으로 치환됨.

where 의 함수 형태

s.where(lambda x: x > 0, 0)              # cond 도 callable
s.where(s > 0, lambda x: -x)             # other 도 callable

where 로 NaN 마킹

# 조건 불만족 행을 NaN 으로
df['score'] = df['score'].where(df['score'] >= 0)
# other 생략 시 기본값 NaN

mask

s.mask(cond, other)         # where 의 반대

cond True 인 곳을 치환. 의미가 더 직관적인 경우가 많다.

# salary 가 100k 이상이면 'high', 아니면 원본 유지
df['salary_label'] = df['salary'].mask(df['salary'] >= 100_000, 'high')

mask 로 이상치 NaN 처리

df['amount'] = df['amount'].mask(
    (df['amount'] < 0) | (df['amount'] > 10_000),
    other=pd.NA,
)

np.where 와의 비교

import numpy as np
np.where(s > 20, s, 0)        # cond True → s, False → 0
s.where(s > 20, 0)             # cond True → s, False → 0 (같음)

같은 결과지만 pandas API 는 other 가 cond False 일 때, numpy 는 x 가 True 일 때.

np.select (다중 분기)

import numpy as np
conditions = [df['age'] < 13, df['age'] < 20, df['age'] < 65]
choices = ['child', 'teen', 'adult']
df['stage'] = np.select(conditions, choices, default='senior')

여러 if-elif-else 를 벡터화.

# 실무 예: 매출 등급 분류
conditions = [
    df['revenue'] < 1_000,
    df['revenue'] < 10_000,
    df['revenue'] < 100_000,
]
choices = ['bronze', 'silver', 'gold']
df['tier'] = np.select(conditions, choices, default='platinum')

실전 패턴

outlier 처리

# 1, 99 percentile 로 clip
lower = df['amount'].quantile(0.01)
upper = df['amount'].quantile(0.99)
df['amount_clipped'] = df['amount'].clip(lower, upper)

조건부 컬럼 생성

df['discount'] = df['price'].where(df['vip'], df['price'] * 0.9)
# vip 이면 그대로, 아니면 10% 할인

연속 mask 체인

df['value'] = (
    df['value']
    .where(df['value'] >= 0, 0)         # 음수 → 0
    .where(df['value'] <= 1_000, 1_000) # 1000 초과 → 1000
)
# 또는 clip 으로 동일하게:
df['value'] = df['value'].clip(0, 1_000)

pandas 2.x CoW 와 where / mask

# pandas 2.x Copy-on-Write 환경
# where / mask 는 항상 새 Series 반환 → 원본 불변
s2 = s.where(s > 0, 0)    # s 는 그대로, s2 가 변경된 버전
s.where(s > 0, 0, inplace=True)   # inplace: 원본 수정 (CoW 에서 경고 가능)

DataFrame.where (축 방향)

df.where(df > 0)             # 모든 셀: 0 이하 → NaN
df.where(df.notna(), -999)   # NaN 인 셀 → -999
df.mask(df < 0, 0)           # 음수 셀 → 0

함정

1. where / mask 의 의미 헷갈림

기억법: where 는 when True 유지. mask 는 masks True (가린다).

2. cond 의 shape 일치

df.where(some_bool_df)        # 같은 shape DataFrame
df.where(df['x'] > 0)         # broadcast (행 단위)

3. other 의 dtype 강등

s = pd.Series([1, 2, 3], dtype='int64')
s.where(s > 1, 'small')       # dtype 이 object 로 강등
# 의도했으면 괜찮지만, 수치 연산 이후 예상치 못한 결과 가능

4. np.where 의미 혼동

# np.where(cond, x, y): cond True → x, False → y
# s.where(cond, other): cond True → s 그대로, False → other
# 반대처럼 보이지만 실제로는 동일한 결과

s.where(s > 0, -1)            # 양수 유지, 나머지 -1
np.where(s > 0, s, -1)        # 동일한 결과

5. clip 과 where 의 NA 처리

s = pd.Series([pd.NA, 5, 10], dtype='Int64')
s.clip(0, 8)      # [<NA>, 5, 8]  (NA 는 그대로)
s.where(s <= 8)   # [<NA>, 5, 8]  (NA 는 False 처리 → 치환)
# NA 에 대한 동작이 다름. clip 은 NA 보존, where 는 NA 를 False 로

6. inplace 와 체이닝

# ❌ 체이닝 중 inplace
df['col'].where(cond, 0, inplace=True)   # SettingWithCopyWarning 가능

# ✓ 직접 할당
df['col'] = df['col'].where(cond, 0)

관련 위키

이 글의 용어 (5개)
[Pandas] .loc / .ilocpandas
정의 - : 라벨 기반 인덱싱 (index/column 이름) - : 정수 위치 기반 인덱싱 (0-based) 이 두 indexer 가 pandas 선택의 표준. 만으로는 헷갈리…
[Pandas] Boolean Indexingpandas
정의 Boolean Indexing 은 True/False 의 Series 를 인덱서로 전달 해 행을 선택하는 패턴. pandas 의 가장 흔한 필터링 방법. 마스크 생성과 적용…
[Pandas] cut / qcutpandas
정의 - : 값 기반 binning (구간을 직접 지정) - : 빈도 기반 binning (분위수로 같은 크기) 연속형 변수를 범주형으로 변환. ML 의 feature engin…
[Pandas] dropna / fillnapandas
정의 - : NaN 이 있는 행/열 제거 - : NaN 을 특정 값으로 대체 데이터 분석의 가장 기본적인 결측치 처리. 사용 상황 - CSV 로드 후 빈 셀이 NaN 으로 읽혔을…
[Pandas] replace / astypepandas
정의 - : 특정 값을 다른 값으로 치환 - : dtype 변환 dtype 변환 경로 replace 기본 <CodeWithOutput language="python" output…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기