본문 바로가기
목차
Python/pandas

[Pandas] Boolean Mask 와 where()/mask()

by ds31x 2025. 8. 28.
728x90
반응형

https://www.youtube.com/watch?v=mLsLRe4_f7I

Boolean Mask란:

Boolean mask는 True 또는 False로 구성(=boolean)된 시퀀스(Series/DataFrame/ndarray/list) 객체를 이용하여 Pandas에서 특정 데이터를 선택하는 등의 마스킹(masking)을 하는 것을 가리킴.


사용방식:

  • Series mask :
    • column 또는 row 필터링: 특정 condition(True/Flase로 치환되는 expression)을 통해 False가 되는 row 또는 column을 제거.
    • Series mask 에서 반환값에는 NA 생성없음 (=제거됨).
  • DataFrame mask :
    • cell 마스킹에 사용됨.
    • Boolean mask에서 False에 해당하는 위치의 cell의 값을 NA 로 치환
    • NA로 치환되는 cell의 값은 실제로 반환값에서 해당하는 위치의 dtype에 따라 np.nan 또는 pd.NA임.
  • where()/mask() :
    • conditional replacement 에 사용됨.
    • 해당 메서드에 주어진 condition의 결과 True 또는 False 값에 따라
    • 해당 위치의 값이 유지 또는 치환됨.

NA (not available or missing value, 결측치) 종류:

  • 다음의 전통 NumPy dtype 은 np.nan 사용
    • float64
    • int64
    • object
  • 다음의 Pandas 확장 dtype 은 pd.NA 사용.
    • Int64
    • Float64
    • string
    • boolean

1. 예제 코드를 위한 데이터

import pandas as pd
import numpy as np

# 혼합 dtype 예제:
# - name: object(string) 기본 dtype
# - score, speed: int -> pandas가 정수로 유지 (전통 NumPy int64)
# - status: 'string' 확장 dtype(의도적으로 지정) → 결측 시 pd.NA 사용
# - grp: object
df = pd.DataFrame({
    "name":   ["Alice", "Bob", "Charlie", "David", "Eva"],
    "score":  [85, 40, 72, 90, 55],
    "speed":  [33, 27, 31, 36, 29],
    "status": pd.Series(["ok", "ng", "ok", "ok", "ng"], dtype="string"),
    "grp":    ["A", "B", "A", "B", "A"],
})

# 확인: 각 열 dtype
print(df.dtypes)
# name      object      : 전통 NumPy(object) : 마스킹 시 np.nan
# score       int64     : 전통 NumPy(int64) : 마스킹 시 np.nan
# speed       int64     : 전통 NumPy(int64) : 마스킹 시 np.nan
# status    string      : 확장 dtype(string) : 마스킹 시 pd.NA
# grp       object      : 전통 NumPy(object) : 마스킹 시 np.nan

2. Series mask로 “행(row)” 필터링

  • condition을 만족하지 않는 행은 아예 제외 : NA 생성 없음.
  • 복수의 condition을 사용하려면 연산자 &(AND), |(OR), ~(NOT) 사용하고 각 condition은 parentheses로 감쌈.

2-1. 단일 조건: score >= 70 인 행만 선택

mask_row = (df["score"] >= 70)  # Boolean Series (index alignment 보장)
df_row_filtered = df[mask_row]  # 행 필터링, 조건 불만족 행은 제거됨
print(df_row_filtered)

2-2. 복합 조건: (grp == 'A') & (speed >= 30)

cond = (df["grp"].eq("A")) & (df["speed"] >= 30)
df_row_filtered2 = df[cond]
print(df_row_filtered2)

2-3. 일부 column 만 보려면: loc로 컬럼 지정

df_row_minimal = df.loc[df["score"] >= 70, ["name", "score", "grp"]]
print(df_row_minimal)
  • iloc의 사용도 가능한데,
  • ["name", "score", "grp"]의 일부 column 지정을 0 이상의 정수 index로 바꿔야 함: [0,1,4]
  • 행 조건: df["score"] >= 70은 Boolean Series로 iloc에서도 Boolean mask로 동작함. 단 iloc은 이 경우 Series의 index를 무시하고 위치기반으로만 동작.

3. Series mask로 “열(column)” 필터링

  • 다음의 예제는 label이 ‘s’로 시작하는 복수의 column을 필터링: score, speed, status 3개.
  • column에 대한 boolean mask는 columns size와 동일 길이여야 하며,
  • 이 마스크를 Series 개체로 쓰는 경우 indexdf.columns와 일치해야함.

3-1. ndarray[bool] 마스크로 's' 시작 열만 선택 → 여러 열(score, speed, status) 유지

cols_mask = df.columns.str.startswith("s")  # 예: [False, True, True, True, False]
df_cols_filtered = df.loc[:, cols_mask]     # 열 필터링, NaN 생성 없음(열 제거 방식)
print(df_cols_filtered)

3-2. Boolean Series 마스크로 동일 동작 (index를 columns에 정렬)

cols_mask_series = pd.Series(cols_mask, index=df.columns)
df_cols_filtered2 = df.loc[:, cols_mask_series]
print(df_cols_filtered2)

3-3. 문자열 패턴 활용(참고): 's'로 시작하고 'e' 포함 열만

advanced_cols_mask = df.columns.str.match(r"^s") & df.columns.str.contains("e")
print(df.loc[:, advanced_cols_mask])  # 여기서는 score, speed 중 'e'가 있는 speed만 남음

4. DataFrame mask로 “셀(cell)” 마스킹

  • 이 경우 False 위치를 결측치로 치환하고 DataFrame mask와 같은 shape의 반환값을 가짐.

4-1. DataFrame 전체 조건 생성: 각 열에 서로 다른 기준

mask_df = pd.DataFrame({
    # name: 길이 > 3인 이름만 남기고, 아니면 결측치로
    "name":   (df["name"].str.len() > 3),
    # score: 60 초과만 남기고, 아니면 결측치로
    "score":  (df["score"] > 60),
    # speed: 30 이상만 남기고, 아니면 결측치로
    "speed":  (df["speed"] >= 30),
    # status: 값이 'ok'인 곳만 남기고, 아니면 결측치로
    "status": (df["status"].eq("ok")),
    # grp: 'B'인 곳만 남기고, 아니면 결측치로
    "grp":    (df["grp"].eq("B")),
})

4-2. 셀 마스킹: False: 결측치로 치환

masked = df[mask_df]  # df.where(mask_df)와 동일 효과
print(masked)
  • 반환되는 masked의 전통적인 NumPy 데이터 타입인 column은 다음과 같음.
    • name(NumPy object),
    • score(int64),
    • speed(int64),
    • grp(object)
  • NumPy 데이터 타입인 경우, boolean mask에서 False의 위치는 np.nan 로 표시
  • Pandas의 확장 데이터 타입인 status(string 확장 dtype)에서 False 위치는 <NA>로 표시: pd.NA

주의: int64 열이 결측을 갖게 되면
내부적으로 float로 upcast되어 보일 수 있음
(이 경우 소수점으로 출력됨).

위의 column filtering을 위한 boolean mask mask_df를 row filtering으로 전환가능:

  • mask_df.all(axis=1): 모든 열 조건이 True인 행만 유지.
  • mask_df.any(axis=1): 하나라도 True인 행 유지.

4-3. DataFrame mask: 행 필터 조건으로 축약

rows_all_true = df[mask_df.all(axis=1)]  # 모든 열 조건 만족 행만
rows_any_true = df[mask_df.any(axis=1)]  # 하나라도 만족하는 행
print(rows_all_true)
print(rows_any_true)

5. where()/mask()로 조건부 대치 (False 위치를 NaN 또는 다른 값으로 대치)

  • where(cond, other=np.nan):
    • cond=True : 유지
    • cond=False : other에 할당된 값으로 대치.
  • mask(cond, other=np.nan):
    • cond=True : other로 대치
    • cond=False : 유지.
  • other가 미지정시 dtype에 따른 NA로 처리됨.

5-1. Series where - NumPy 타입

  • 전통 NumPy dtype(float64) : False 위치는 np.nan
    s_float = pd.Series([10.0, 20.0, 30.0], dtype="float64")
    out1 = s_float.where(s_float > 15)  # 15 초과만 유지, 나머지는 np.nan
    print(out1)

5-2. Series where - 확장 타입

  • 확장 dtype(Int64) : False 위치는 pd.NA
    s_int_ext = pd.Series([10, 20, 30], dtype="Int64")
    out2 = s_int_ext.where(s_int_ext > 15)  # 15 초과만 유지, 나머지는 <NA>(pd.NA)
    print(out2)

5-3. DataFrame where - Broad Casting

  • 단일 조건의 결과 값이 브로드캐스팅
    #     score >= 70인 행은 유지, 그 외 행의 모든 셀은 결측치로 대치
    out3 = df.where(df["score"] >= 70)
    print(out3)
    # 해석:
    # - name/score/speed/grp(object,int 등 전통 dtype): np.nan
    # - status(string 확장 dtype): pd.NA

5-4. False 위치를 NaN이 아닌 다른 값으로 대치 - 권장

  • 다음의 예는 score<70 인 행의 'score'-1로 바꾸는 식으로 열 단위 치환을 수행.
df_score_replaced = df.copy()
df_score_replaced["score"] = df["score"].where(df["score"] >= 70, other=-1)  # 미달 점수 -1
print(df_score_replaced)

5-5. where()의 반대 동작: mask()

  • mask(cond, other) = where(~cond, other)
s_masked = s_float.mask(s_float > 15, other=-999)  # 15 초과인 곳만 -999로 치환
print(s_masked)

6) 주의할 점

  • multiple condition의 경우 bitwise operation 이용:
    • boolean op. 인 and/or/not 사용 못함.
    • & / | / ~ 사용.
  • multiple condition의 경우 각 개별 condition은 괄호(( ))로 감싸기.
  • NA (or missing value) 전파:
    • DataFrame 마스킹 및 where()에서 False 에 따른 NA 발생.
    • 이후 fillna/dropna 의 사용을 통한 처리도 많이 이용됨.
  • 성능 및 가독성을 위해 복잡한 condition은 변수로 보관하여 재사용할 것.

7. 요약

  • Series mask
    • 행/열 필터링
    • False 위치의 데이터는 제거, NA 생성 없음.
  • DataFrame mask
    • 셀 마스킹
    • False 위치를 결측치로 치환.
  • where()/mask()
    • 조건부 대치
    • False 위치를 임의의 값으로 치환 가능
    • 브로드캐스팅·열별 적용 유연.

같이보면 좋은 자료들

2024.03.18 - [Python] - [DL] Tensor: Indexing <Simple, Slicing, Fancy, Boolean Mask>

 

[DL] Tensor: Indexing <Simple, Slicing, Fancy, Boolean Mask>

NumPy나 PyTorch, Tensorflow의 텐서들도파이썬의 list 또는 tubple 에서의 indexing과 slicing이 거의 그대로 사용됨.2023.07.12 - [Python] - [Python] list (sequence type) : summary [Python] list (sequence type) : summarylist는 ordered m

ds31x.tistory.com

2024.03.19 - [Python] - [ML] where: numpy 의 idx찾기

 

[ML] where: numpy 의 idx찾기

np.where 함수numpy에서 ndarray 인스턴스에서 특정 조건을 만족하는 elements의 위치(index, idx)를 찾는 기능을numpy 모듈의 where 함수가 제공해줌.사실 where 함수는 특정 조건에 따라 값을 바꾸어주는 기능

ds31x.tistory.com


2025.05.16 - [Python/pandas] - [ML] pandas.DataFrame 에서 EDA에 적합한 메서드 요약

 

[ML] pandas.DataFrame 에서 EDA에 적합한 메서드 요약

Pandas DataFrame에서 탐색적 데이터 분석(EDA)에 사용할 수 있는 주요 메서드들은 다음과 같음:2024.05.18 - [분류 전체보기] - [ML] Exploratory Data Analysis (EDA) [ML] Exploratory Data Analysis (EDA)Exploratory Data Analysis (

ds31x.tistory.com

 

728x90