to_datetime이 전체 시간의 절반을 먹을 때 — format 지정의 효과

to_datetime이 전체 시간의

배치 스크립트 실행 시간을 cProfile로 떠보면, CSV 몇 개 읽고 날짜 컬럼 하나 변환했을 뿐인데 pd.to_datetime 한 줄이 전체 시간의 절반 가까이를 차지하는 경우가 있습니다. 원인은 대부분 포맷 추론입니다. format 인자를 명시적으로 넣어주면 pandas가 행마다 날짜 형식을 다시 추측하는 과정을 건너뛰기 때문에 같은 데이터라도 체감될 정도로 빨라집니다. 다만 데이터 안에 포맷이 섞여 있으면 이 방법이 오히려 에러를 내므로, 어떤 조건에서 통하고 어디서 막히는지 같이 짚어보겠습니다.

느려지는 지점을 먼저 확인하는 법

감으로 “날짜 변환이 느린 것 같다”고 판단하기 전에 어디서 시간이 새는지 숫자로 확인하는 과정이 먼저입니다. 표준 라이브러리 cProfile을 쓰면 함수별 누적 시간(cumtime)과 자체 실행 시간(tottime)을 바로 볼 수 있습니다.


import cProfile
import pandas as pd

df = pd.read_csv("events.csv")
cProfile.run("pd.to_datetime(df['created_at'])", sort="cumtime")

결과 상위 줄에 array_to_datetime이나 dateutil.parser._parse 같은 함수가 반복적으로 호출되며 누적 시간을 많이 잡아먹고 있다면, 포맷을 매 행마다 추측하고 있다는 신호입니다. 반대로 array_strptime이 상위에 보이면 이미 빠른 경로를 타고 있는 상태입니다.

포맷을 지정하지 않으면 내부에서 무슨 일이 벌어지나요?

format 인자 없이 pd.to_datetime(series)를 호출하면 pandas는 먼저 결측치가 아닌 값 몇 개를 보고 공통 포맷을 추측합니다. 이 추측이 전체 데이터에 들어맞으면 그나마 다행이지만, 문자열 길이나 구분자가 조금씩 다른 행이 섞여 있으면 추측이 실패하고 행 단위로 파싱 전략을 바꿉니다.

이때 내부적으로 dateutil.parser.parse에 의존하게 되는데, 이 함수는 문자열을 쪼개고 정규식과 휴리스틱으로 연, 월, 일을 판별하는 범용 파서입니다. 포맷이 정해져 있다는 전제 없이 “이게 날짜 맞아?”부터 따지는 구조라서, 포맷이 고정된 로그 데이터에 쓰기엔 들이는 연산이 과합니다. 행 수가 수십만 건을 넘어가면 이 오버헤드가 누적되어 전체 실행 시간의 상당 부분을 차지하게 됩니다.

format 인자 하나로 체감되는 차이

변환하려는 모든 행이 같은 형식이라는 사실을 알고 있다면, format 인자에 그 형식을 직접 적어주는 것만으로 pandas가 추측 단계를 생략하고 C로 구현된 array_strptime 경로를 바로 탑니다. 포맷 문자열 지시자는 파이썬 표준 라이브러리의 strftime 규칙을 그대로 따르기 때문에 %Y-%m-%d %H:%M:%S처럼 익숙한 형태로 쓰면 됩니다.


import pandas as pd
import time

s = pd.Series(["2024-01-15 09:30:00"] * 1_000_000)

start = time.perf_counter()
pd.to_datetime(s)
print("format 미지정:", time.perf_counter() - start)

start = time.perf_counter()
pd.to_datetime(s, format="%Y-%m-%d %H:%M:%S")
print("format 지정:", time.perf_counter() - start)

직접 실행해 보면 format을 지정한 쪽의 출력 시간이 눈에 띄게 작게 나오는 것을 확인할 수 있습니다. 데이터 규모가 커질수록 두 값의 차이도 함께 벌어지는데, 이는 행마다 반복되던 추측 비용이 고정 비용으로 바뀌기 때문입니다.

포맷이 섞인 데이터에선 통하지 않습니다

이 방법의 전제는 “모든 행이 같은 형식”이라는 점입니다. 실제로는 외부 API 응답이나 사용자 입력 로그처럼 한 컬럼 안에 2024-01-15와 2024/02/20, 20240310이 뒤섞여 있는 경우가 흔합니다. 이런 데이터에 고정 format을 주면 형식이 다른 행에서 바로 예외가 발생합니다.

CSV 데이터 분석 프로파일링 터미널 벤치마크 결과


mixed = pd.Series(["2024-01-15", "2024/02/20", "20240310"])

pd.to_datetime(mixed, format="%Y-%m-%d")
# ValueError: time data "2024/02/20" doesn't match format "%Y-%m-%d"

pd.to_datetime(mixed, format="mixed")
# pandas 2.0 이상에서 사용 가능, 행마다 포맷을 다시 추론

format="mixed"나 format="ISO8601"은 pandas 2.0부터 추가된 옵션으로 섞인 포맷을 에러 없이 처리해주지만, 내부적으로는 여전히 행별 판단이 들어가기 때문에 고정 format만큼 빠르지는 않습니다. 애초에 수집 단계에서 날짜 문자열 형식을 통일할 수 있다면 그쪽이 더 나은 선택입니다.

pandas 2.0 전후로 달라진 옵션들

과거에는 infer_datetime_format=True 옵션을 켜서 첫 행에서 포맷을 추론한 뒤 나머지 행에 재사용하는 절충안이 있었습니다. pandas 2.0부터는 이 인자가 deprecated 상태이며, 호출 시 FutureWarning이 뜹니다. 버전별 상황을 정리하면 다음과 같습니다.

옵션 pandas 1.x pandas 2.0 이상 비고
format 명시 안 함 사용 가능, 느림 사용 가능, 느림 행별 추측 발생
infer_datetime_format=True 사용 가능 Deprecated(경고 발생) 2.0 이상에서는 자동 추론으로 대체
format="%Y-%m-%d" 등 고정값 사용 가능, 빠름 사용 가능, 빠름 전 행이 동일 형식일 때만
format="mixed" 미지원 사용 가능 혼합 포맷 허용, 고정 포맷보다 느림
format="ISO8601" 미지원 사용 가능 ISO 8601 변형 포맷 전용

지금 운영 중인 pandas 버전이 2.0 미만이라면 infer_datetime_format 대신 가능한 한 고정 format을 쓰는 쪽이 안전하고, 2.0 이상으로 올렸다면 혼합 데이터는 format="mixed"로, 단일 포맷이면 여전히 명시적 format으로 처리하는 편이 빠릅니다.

지금 바로 format 인자를 넣어 보세요

날짜 변환이 느리다고 느껴질 때 가장 먼저 할 일은 cProfile로 실제 병목이 to_datetime인지 확인하는 것이고, 맞다면 데이터의 날짜 형식이 전 행에서 동일한지부터 살펴보는 것입니다. 형식이 고정돼 있다면 format 인자 한 줄로 끝나고, 섞여 있다면 pandas 2.0 이상에서 format="mixed"나 "ISO8601"을 쓰거나, 더 근본적으로는 수집 단계에서 정규식으로 포맷을 통일하는 전처리를 추가하는 쪽이 장기적으로 더 빠릅니다. 다음 배치 작업을 돌리기 전에 지금 쓰고 있는 to_datetime 호출부터 format 인자가 비어 있는지 한 번 열어 보시길 권해드립니다.

문자열 컬럼이 메모리 절반을 먹을 때: object·category·Arrow 비교

Leave a Comment