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

[Pandas] query / eval

· 수정 · 📖 약 1분 · 514자/단어 #python #pandas #selection #query #filter
Pandas query, Pandas eval, DataFrame.query, pandas 문자열 필터, pandas query 메서드

정의

  • DataFrame.query(expr) : 문자열로 boolean 표현식 을 전달해 행 필터링
  • DataFrame.eval(expr) : 문자열로 계산 표현식 을 평가

Pandas Boolean Indexing 의 가독성 있는 대안.

사용 상황

  • 조건이 복잡해 boolean indexing 이 장황해질 때
  • method chaining 안에서 필터를 인라인으로 넣고 싶을 때
  • 대용량 DataFrame 에서 numexpr 가속을 활용하고 싶을 때
  • SQL 스타일로 데이터를 탐색할 때

query 처리 흐름

flowchart LR
    EXPR["query 문자열"] --> PARSE["표현식 파싱"]
    PARSE --> ENV{"numexpr 설치?"}
    ENV -->|"Yes"| NE["numexpr 엔진 (빠름)"]
    ENV -->|"No"| PY["Python 엔진"]
    NE --> MASK["bool 마스크 생성"]
    PY --> MASK
    MASK --> FILTER["행 필터링"]
    FILTER --> OUT["결과 DataFrame"]

기본 사용법

df.query('age > 30')

# 동등 의미
df[df['age'] > 30]

긴 조건일수록 query 가 짧고 읽기 쉽다.

python
import pandas as pd
df = pd.DataFrame({
  'name': ['Alice', 'Bob', 'Charlie', 'Dave'],
  'age': [25, 30, 35, 40],
  'city': ['Seoul', 'Busan', 'Seoul', 'Daegu'],
})

result = df.query("age > 27 and city == 'Seoul'")
print(result)
결과
      name  age   city
2  Charlie   35  Seoul

외부 변수 참조 (@)

@ 로 Python 변수를 query 안에 끌어온다.

python
min_age = 27
cities = ['Seoul', 'Busan']
result = df.query("age > @min_age and city in @cities")
print(result)
결과
      name  age   city
1      Bob   30  Busan
2  Charlie   35  Seoul
# 함수 결과도 변수로 전달 가능
threshold = df['salary'].quantile(0.9)
df.query("salary >= @threshold")

연산자

연산자의미
==, !=, <, >, <=, >=비교
and, or, not논리 (권장)
&, |, ~비트 논리 (가능하지만 괄호 필요)
in, not in포함 여부
df.query("city in ['Seoul', 'Busan']")
df.query("not (age < 30)")
df.query("age > 25 and city != 'Daegu'")
df.query("10 < age < 40")          # Python 체이닝 비교 가능 (boolean indexing 과 다름)

컬럼명에 공백 / 특수문자

# 컬럼명에 공백이 있으면 backtick
df.query('`first name` == "Alice"')
df.query('`age (years)` > 30')

# 예약어 컬럼명도 backtick
df.query('`class` == "A"')

eval 로 계산

df.eval('total = price * qty')                # 새 컬럼 추가
df = df.eval('discount = price * 0.1', inplace=False)
df.eval('a + b - c')                          # Series 반환

대규모 DataFrame 에서 numexpr 기반 가속이 이뤄질 수 있다 (자동 감지).

# 여러 표현식 한 번에
df = df.eval("""
    total = price * qty
    tax = total * 0.1
    net = total - tax
""")

method chaining 과 query

query 는 method chaining 에서 특히 빛난다.

result = (
    df
    .query("status == 'active'")
    .query("age >= 18")
    .assign(score=lambda x: x['sales'] / x['visits'])
    .query("score > @threshold")
    .sort_values('score', ascending=False)
    .head(10)
)

boolean indexing 으로 같은 코드를 쓰면 df 이름이 반복되고 중간 변수가 필요해진다.

boolean indexing 과 비교

항목Boolean Indexingquery()
가독성복잡 조건 시 장황간결
성능 (소규모)빠름파싱 오버헤드 있음
성능 (대규모)기본numexpr 활용 시 더 빠름
컬럼명 제약없음예약어/공백 시 backtick 필요
외부 변수직접 참조@var 필요
복잡한 메서드.str.contains() 등 자유제한적
체이닝 비교불가 (10 < age < 40)가능
# boolean indexing 이 더 자연스러운 경우
df[df['name'].str.startswith('A')]
df[df['tags'].apply(lambda x: 'python' in x)]

# query 가 더 자연스러운 경우
df.query("10 < age < 40 and city in ['Seoul', 'Busan']")

numexpr 가속

# numexpr 설치 여부 확인
import numexpr
print(numexpr.__version__)

# 대용량 DataFrame 에서 query 가 더 빠른 경우
import pandas as pd
df = pd.DataFrame({'a': range(1_000_000), 'b': range(1_000_000)})

# numexpr 사용 (설치된 경우 자동)
df.query("a > 500000 and b < 800000")

# 엔진 명시
df.query("a > 500000", engine='numexpr')   # numexpr 강제
df.query("a > 500000", engine='python')    # Python 강제

실전 패턴

날짜 범위 필터

start = '2024-01-01'
end   = '2024-12-31'
df.query("date >= @start and date <= @end")

여러 값 포함 필터

valid_statuses = ['active', 'pending', 'trial']
df.query("status in @valid_statuses")

수치 범위 + 카테고리 복합

df.query("10 < age < 65 and department in ['eng', 'data'] and salary > 5000")

함정

1. 문자열 비교에 따옴표

df.query("name == 'Alice'")    # ✓ 안쪽 single quote
df.query('name == "Alice"')    # ✓ 안쪽 double quote
df.query('name == Alice')       # ❌ Alice 라는 컬럼을 찾으려 함

2. backtick 없으면 공백 컬럼 인식 안 됨

df.query('first name == "Alice"')        # ❌
df.query('`first name` == "Alice"')      # ✓

3. & / | 의 괄호

df.query('(age > 30) & (city == "Seoul")')    # ✓
df.query('age > 30 & city == "Seoul"')         # 의도와 다른 해석 가능
# and / or 가 더 안전
df.query('age > 30 and city == "Seoul"')       # ✓

4. 외부 함수 호출 불가

# ❌ query 안에서 함수 호출 불가
df.query("name.str.startswith('A')")

# ✓ boolean indexing 사용
df[df['name'].str.startswith('A')]

5. 소규모 DataFrame 에서 오버헤드

# 행이 수백 개 이하면 query 파싱 비용이 더 클 수 있음
# 소규모: boolean indexing 이 빠름
# 대규모 (100만+ 행): query + numexpr 가 유리

관련 위키

이 글의 용어 (5개)
[Pandas] .loc / .ilocpandas
정의 - : 라벨 기반 인덱싱 (index/column 이름) - : 정수 위치 기반 인덱싱 (0-based) 이 두 indexer 가 pandas 선택의 표준. 만으로는 헷갈리…
[Pandas] 성능 / 메모리 최적화pandas
정의 pandas 의 흔한 성능 함정과 최적화 패턴. 벡터화 + dtype 선택 + 알고리즘 의 조합이 핵심. 사용 상황 | 상황 | 권장 접근 | |:---|:---| | 단순…
[Pandas] Boolean Indexingpandas
정의 Boolean Indexing 은 True/False 의 Series 를 인덱서로 전달 해 행을 선택하는 패턴. pandas 의 가장 흔한 필터링 방법. 마스크 생성과 적용…
[Pandas] DataFramepandas
정의 은 2차원 레이블 테이블. 각 열이 , 모든 열이 같은 (행 라벨) 를 공유. SQL 테이블 / Excel 시트 / R data.frame 의 Python 대응체. 구조 시…
[Pandas] pipe / method chainingpandas
정의 는 DataFrame/Series 자체를 함수의 첫 인자로 넘겨 method chaining 흐름에 사용자 함수를 끼워 넣는 API. functional style pand…

💬 댓글

사이트 검색 / 명령어

검색

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