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

[Pandas] str.contains / startswith / endswith

· 수정 · 📖 약 1분 · 407자/단어 #python #pandas #string #pattern #regex
Pandas str pattern, Pandas str contains, 문자열 매칭 pandas, Pandas 문자열 패턴 매칭, str.contains pandas

정의

문자열 Series 에서 패턴 매칭 으로 필터링/검색하는 메서드들. SQL 의 LIKE, WHERE str LIKE '%pat%' 패턴에 해당하며, .str accessor 를 통해 접근한다.

메서드 전체 지도

flowchart LR
    subgraph "위치 무관 포함 여부"
        C1["str.contains(pat)"]
    end

    subgraph "시작/끝 고정"
        C2["str.startswith(prefix)"]
        C3["str.endswith(suffix)"]
        C4["str.match(pat)"]
        C5["str.fullmatch(pat)"]
    end

    subgraph "위치/횟수 반환"
        C6["str.find(sub)"]
        C7["str.rfind(sub)"]
        C8["str.count(pat)"]
        C9["str.findall(pat)"]
    end

    INPUT["문자열 Series"] --> C1
    INPUT --> C2
    INPUT --> C3
    INPUT --> C4
    INPUT --> C5
    INPUT --> C6
    INPUT --> C7
    INPUT --> C8
    INPUT --> C9

contains

str.contains(pat, case=True, na=None, regex=True) - 부분 매칭 (내부적으로 re.search).

s.str.contains('python')                  # 'python' 포함
s.str.contains('python', case=False)      # 대소문자 무시
s.str.contains(r'^\d+$', regex=True)      # 정규식
s.str.contains('python', na=False)        # NaN → False
python
import pandas as pd
s = pd.Series(['Hello World', 'Python Pandas', 'pandas tutorial', None])
print(s.str.contains('python', case=False, na=False).tolist())
결과
[False, True, True, False]

startswith / endswith

s.str.startswith('Mr.')
s.str.endswith('.com')
s.str.startswith(('Mr.', 'Ms.', 'Dr.'))    # tuple 로 여러 패턴
s.str.endswith(('.jpg', '.png', '.gif'))    # tuple 지원

startswith / endswith정규식 미지원, 리터럴 비교만. 튜플로 여러 패턴을 OR 조건으로 검사한다.

python
import pandas as pd
emails = pd.Series(['alice@gmail.com', 'bob@naver.com', 'carol@gmail.net', None])

gmail = emails.str.endswith('@gmail.com', na=False)
print(gmail.tolist())
결과
[True, False, False, False]

match / fullmatch

s.str.match(r'^\d{3}-\d{4}$')     # 처음부터 매칭 (re.match)
s.str.fullmatch(r'^\d{3}-\d{4}$') # 전체 매칭 (re.fullmatch)
s.str.contains(r'\d{3}-\d{4}')    # 어느 위치든 매칭 (re.search)
메서드내부 동작SQL 유사체
contains(pat)re.search - 부분 매칭LIKE '%pat%'
match(pat)re.match - 처음부터LIKE 'pat%' (단순 경우)
fullmatch(pat)re.fullmatch - 전체= pat (패턴 일치)
startswith(s)str.startswith - 리터럴LIKE 's%'
endswith(s)str.endswith - 리터럴LIKE '%s'

find / rfind / index

s.str.find('o')         # 첫 위치 (없으면 -1)
s.str.rfind('o')        # 마지막 위치
s.str.findall(r'\w+')   # 모든 매치 (list 반환)

count

s.str.count('a')         # 'a' 등장 횟수
s.str.count(r'\d')       # 숫자 개수

필터에 활용

df[df['email'].str.contains('@gmail.com', na=False)]
df[df['name'].str.startswith('Mr.') | df['name'].str.startswith('Ms.')]
df[~df['url'].str.endswith('.png')]

실전 예시: 로그 분석

python
import pandas as pd

logs = pd.Series([
  'ERROR 500 /api/users',
  'INFO 200 /health',
  'WARN 429 /api/orders',
  'ERROR 404 /api/items',
  'DEBUG timer elapsed',
])

# ERROR 레벨만
errors = logs[logs.str.startswith('ERROR')]
print("에러:", errors.tolist())

# /api/ 경로만
api_logs = logs[logs.str.contains(r'/api/', na=False)]
print("API:", api_logs.tolist())

# 상태코드 5xx
server_err = logs[logs.str.contains(r' 5d{2} ', regex=True)]
print("5xx:", server_err.tolist())
결과
에러: ['ERROR 500 /api/users', 'ERROR 404 /api/items']
API: ['ERROR 500 /api/users', 'WARN 429 /api/orders', 'ERROR 404 /api/items']
5xx: ['ERROR 500 /api/users']

실전 예시: 이메일/URL 패턴

df = pd.DataFrame({'email': ['alice@gmail.com', 'bob@company.co.kr', 'invalid']})

# 유효한 이메일 (단순 검사)
valid = df['email'].str.contains(r'^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$', regex=True, na=False)

# 도메인 추출
df['domain'] = df['email'].str.extract(r'@([\w.-]+)')

# 특정 도메인만
gmail_users = df[df['email'].str.endswith('@gmail.com', na=False)]

성능: 리터럴 vs 정규식

flowchart LR
    Q{"패턴 종류"} -->|"단순 문자열"| A["str.contains(pat, regex=False)\n또는 str.startswith / endswith"]
    Q -->|"복잡한 패턴"| B["str.contains(pat, regex=True)"]
    A --> FA["빠름 - Python str 비교"]
    B --> FB["느림 - re.compile 오버헤드"]
import pandas as pd
import time

s = pd.Series(['hello world'] * 1_000_000)

# 리터럴: regex=False 명시
t0 = time.perf_counter()
_ = s.str.contains('world', regex=False)
print(f"regex=False: {time.perf_counter()-t0:.3f}s")

# 정규식: 불필요하면 느림
t0 = time.perf_counter()
_ = s.str.contains('world', regex=True)
print(f"regex=True:  {time.perf_counter()-t0:.3f}s")

일반적으로 regex=False 가 3-10배 빠르다.

대용량 최적화 팁

# 1. 가능하면 startswith/endswith (리터럴, 가장 빠름)
s.str.startswith('ERROR')

# 2. regex=False 명시
s.str.contains('pattern', regex=False)

# 3. 대소문자 변환 후 비교 (case=False 보다 빠를 수 있음)
s.str.lower().str.startswith('error')

# 4. PyArrow backend 사용 시 str 연산도 빨라짐
s = s.astype('string[pyarrow]')
s.str.contains('ERROR')

함정

1. NaN 처리

df[df['col'].str.contains('x')]
# NaN 인 행 → NaN (Falsy 가 아님, boolean indexing 에러)

df[df['col'].str.contains('x', na=False)]
# ✓ NaN → False, 안전

WARNING

na=False 를 빠뜨리면 ValueError: Cannot mask with non-boolean array containing NA / NaN values 발생. 항상 na=False 명시 권장.

2. 정규식 default 변화

pandas 1.x 까지 regex=True 가 기본. pandas 2.x 에서도 동일하지만, 단순 리터럴 검색에는 regex=False 를 명시해 의도를 분명히 하고 성능을 높이는 것이 좋다.

# 이 두 코드는 동작이 다를 수 있음
s.str.contains('.')       # regex=True → 임의 문자 1개 매칭
s.str.contains('.', regex=False)  # 리터럴 '.' 검색

3. 대소문자

s.str.contains('seoul')             # 대소문자 구분
s.str.contains('seoul', case=False) # 무시
s.str.contains('(?i)seoul')         # 정규식의 inline flag

4. startswith / endswith 는 tuple 만

# tuple 은 가능
s.str.startswith(('A', 'B', 'C'))

# list 는 불가
s.str.startswith(['A', 'B', 'C'])  # TypeError

5. match 는 앵커가 없다

str.matchre.match 를 사용해 문자열의 시작 에서만 매칭한다. 끝을 잠그려면 $ 앵커를 추가하거나 fullmatch 를 써야 한다.

s = pd.Series(['abc123', '123abc'])
s.str.match(r'\d+')     # [False, True] - 시작이 숫자인 것만
s.str.fullmatch(r'\d+') # [False, False] - 전체가 숫자인 것만

관련 위키

이 글의 용어 (5개)
[Pandas] .str accessorpandas
정의 는 Series 의 각 문자열 원소에 벡터화된 문자열 메서드 를 적용하는 accessor. Python 의 거의 모든 메서드를 NaN-aware 로 제공한다. accesso…
[Pandas] Boolean Indexingpandas
정의 Boolean Indexing 은 True/False 의 Series 를 인덱서로 전달 해 행을 선택하는 패턴. pandas 의 가장 흔한 필터링 방법. 마스크 생성과 적용…
[Pandas] isin / isna / notnapandas
정의 | 메서드 | 의미 | |:---|:---| | | 각 원소가 values 안에 있는지 (boolean Series) | | | NaN/NaT/None 여부 ( 별칭) | …
[Pandas] Seriespandas
정의 는 1차원 레이블 배열. NumPy + 의 결합. 의 한 열이 곧 Series. dict-like (key = index label) 이자 list-like (순서 있는 배…
[Pandas] str regex (extract / split / replace)pandas
정의 의 정규식 기반 메서드. 패턴 추출, 분리, 치환 의 핵심. 의 정규식 특화 부분. 기본 문자열 메서드는 참고. 사용 상황 - 로그 문자열에서 IP, 날짜, 에러 코드 등을…

💬 댓글

사이트 검색 / 명령어

검색

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