본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 1. · 15 Views
Pandas 심화 데이터 전처리 완벽 가이드
실무에서 반드시 필요한 Pandas 데이터 전처리 기법을 다룹니다. 결측치 처리부터 문자열, 날짜 데이터까지 초급 개발자도 쉽게 따라할 수 있도록 스토리텔링 방식으로 설명합니다.
목차
1. 결측치 확인 및 처리
김개발 씨는 오늘 첫 데이터 분석 프로젝트를 맡았습니다. 신나는 마음으로 고객 데이터를 열어보니, 군데군데 빈 칸이 눈에 띕니다.
"이 빈 칸들은 어떻게 해야 하지?" 멍하니 화면을 바라보는 김개발 씨에게 옆자리 박시니어 씨가 다가왔습니다.
**결측치(Missing Value)**란 데이터에서 값이 비어있는 상태를 말합니다. 마치 설문조사에서 응답하지 않은 문항처럼, 데이터 수집 과정에서 누락된 값들입니다.
Pandas에서는 **dropna()**로 결측치가 있는 행을 삭제하거나, **fillna()**로 특정 값으로 채울 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
import numpy as np
# 결측치가 포함된 샘플 데이터 생성
df = pd.DataFrame({
'name': ['김철수', '이영희', None, '박민수'],
'age': [25, np.nan, 30, 28],
'salary': [3000, 3500, np.nan, 4000]
})
# 결측치 확인하기
print(df.isnull().sum())
# 결측치가 있는 행 삭제
df_dropped = df.dropna()
# 결측치를 특정 값으로 채우기
df_filled = df.fillna({'name': '미입력', 'age': df['age'].mean(), 'salary': 0})
김개발 씨는 입사 3개월 차 주니어 데이터 분석가입니다. 오늘 처음으로 실제 고객 데이터를 다루게 되었는데, 데이터를 열어보는 순간 당황하고 말았습니다.
분명히 1000명의 고객 정보가 있어야 하는데, 곳곳에 빈 칸이 보였기 때문입니다. 선배 개발자 박시니어 씨가 다가와 화면을 살펴봅니다.
"아, 이건 결측치예요. 실제 데이터에서는 정말 흔하게 나타나는 현상이죠." 그렇다면 결측치란 정확히 무엇일까요?
쉽게 비유하자면, 결측치는 마치 시험지에서 학생이 답을 쓰지 않고 넘어간 문제와 같습니다. 선생님이 채점할 때 빈 칸을 발견하면 0점 처리를 할 수도 있고, 평균 점수로 대체할 수도 있습니다.
이처럼 데이터 분석에서도 결측치를 어떻게 처리하느냐에 따라 분석 결과가 달라질 수 있습니다. 결측치가 왜 문제가 될까요?
첫째, 많은 머신러닝 알고리즘은 결측치가 있으면 아예 동작하지 않습니다. 둘째, 결측치를 그대로 두면 평균이나 합계 같은 통계 계산이 왜곡될 수 있습니다.
셋째, 결측치의 패턴 자체가 중요한 정보일 수도 있는데, 무작정 삭제하면 이런 정보를 놓치게 됩니다. 바로 이런 문제를 해결하기 위해 Pandas는 **dropna()**와 fillna() 두 가지 핵심 메서드를 제공합니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **isnull().sum()**을 보면 각 컬럼별로 결측치가 몇 개인지 한눈에 확인할 수 있습니다.
이 단계가 가장 먼저 수행되어야 합니다. 현황을 모르고 처리 방법을 결정하는 것은 위험하기 때문입니다.
다음으로 **dropna()**는 결측치가 하나라도 있는 행을 통째로 삭제합니다. 간단하고 확실한 방법이지만, 데이터가 많이 줄어들 수 있다는 단점이 있습니다.
마지막으로 **fillna()**는 결측치를 원하는 값으로 채웁니다. 위 코드에서는 name 컬럼은 '미입력'이라는 문자열로, age 컬럼은 평균값으로, salary 컬럼은 0으로 채웠습니다.
컬럼의 특성에 따라 다른 전략을 적용할 수 있다는 것이 fillna()의 장점입니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰에서 고객의 구매 데이터를 분석한다고 가정해봅시다. 전화번호가 없는 고객은 그 행을 삭제하기보다는 '미등록'으로 채우는 것이 좋습니다.
반면 나이 정보가 없다면 전체 고객의 평균 나이로 채우거나, 비슷한 구매 패턴을 가진 고객의 나이로 채우는 방법을 고려할 수 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 무조건 0이나 평균으로 채우는 것입니다. 예를 들어 몸무게 데이터에 0을 채우면 평균 체중 계산이 크게 왜곡됩니다.
따라서 해당 컬럼의 의미와 분석 목적을 고려해서 적절한 값을 선택해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "결측치를 무조건 삭제하거나 채우는 게 아니라, 상황에 맞게 판단해야 하는군요!" 결측치 처리를 제대로 이해하면 더 정확한 분석 결과를 얻을 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 결측치 처리 전에 반드시 isnull().sum()으로 현황부터 파악하세요
- 수치형 데이터는 평균(mean)이나 중앙값(median)으로, 범주형 데이터는 최빈값(mode)으로 채우는 것이 일반적입니다
- dropna(subset=['컬럼명'])을 사용하면 특정 컬럼의 결측치만 기준으로 행을 삭제할 수 있습니다
2. 중복 데이터 제거
김개발 씨가 열심히 결측치를 처리하고 나니, 이번에는 이상한 점이 눈에 띄었습니다. 같은 고객 정보가 여러 번 반복되어 나타나는 것입니다.
"이 사람 정보가 왜 세 번이나 있지?" 고개를 갸우뚱하는 김개발 씨에게 박시니어 씨가 말했습니다. "데이터 수집 과정에서 중복이 생긴 거예요.
이것도 처리해야 합니다."
중복 데이터란 동일한 내용의 행이 여러 번 존재하는 것을 말합니다. 마치 주소록에 같은 사람이 여러 번 저장된 것과 같습니다.
Pandas에서는 **duplicated()**로 중복 여부를 확인하고, **drop_duplicates()**로 중복을 제거할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 중복이 포함된 샘플 데이터
df = pd.DataFrame({
'customer_id': [101, 102, 101, 103, 102],
'name': ['김철수', '이영희', '김철수', '박민수', '이영희'],
'purchase': [50000, 30000, 50000, 45000, 35000]
})
# 중복 행 확인 (True면 중복)
print(df.duplicated())
# 중복된 행 개수 확인
print(f"중복 행 개수: {df.duplicated().sum()}")
# 중복 제거 (첫 번째 행만 남김)
df_unique = df.drop_duplicates()
# 특정 컬럼 기준으로 중복 제거
df_by_id = df.drop_duplicates(subset=['customer_id'], keep='last')
김개발 씨는 결측치 처리를 마치고 뿌듯한 마음으로 데이터를 다시 살펴보고 있었습니다. 그런데 뭔가 이상합니다.
고객 ID 101번 김철수 씨의 정보가 두 번이나 나타나는 것입니다. 게다가 102번 이영희 씨도 마찬가지입니다.
"이게 왜 이렇게 된 거죠?" 김개발 씨가 물었습니다. 박시니어 씨가 설명을 시작합니다.
"데이터를 여러 소스에서 합치거나, 시스템 오류로 같은 데이터가 여러 번 저장되는 경우가 종종 있어요. 이런 중복 데이터를 그대로 두면 분석 결과가 왜곡될 수 있습니다." 그렇다면 중복 데이터란 정확히 무엇일까요?
쉽게 비유하자면, 중복 데이터는 마치 출석부에 같은 학생 이름이 두 번 적힌 것과 같습니다. 전체 학생 수를 셀 때 이 학생을 두 번 세면 안 되겠죠.
이처럼 데이터 분석에서도 중복된 데이터는 통계를 왜곡시키는 원인이 됩니다. 중복 데이터가 왜 문제가 될까요?
첫째, 총합이나 개수를 구할 때 같은 값이 여러 번 계산됩니다. 둘째, 머신러닝 모델이 특정 패턴을 과대평가하게 됩니다.
셋째, 저장 공간이 낭비되고 처리 속도가 느려집니다. 바로 이런 문제를 해결하기 위해 Pandas는 **duplicated()**와 drop_duplicates() 메서드를 제공합니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **duplicated()**는 각 행이 중복인지 아닌지를 True/False로 알려줍니다.
기본적으로 처음 나타난 행은 False, 두 번째부터 나타난 동일한 행은 True가 됩니다. 이를 통해 어떤 행이 중복인지 파악할 수 있습니다.
다음으로 **drop_duplicates()**는 중복된 행을 제거합니다. 기본 설정인 keep='first'는 첫 번째 행만 남기고 나머지를 삭제합니다.
keep='last'로 바꾸면 마지막 행만 남깁니다. 특히 주목할 부분은 subset 파라미터입니다.
모든 컬럼이 완전히 동일한 경우만 중복으로 볼 수도 있지만, 특정 컬럼만 기준으로 중복을 판단해야 할 때도 있습니다. 위 코드에서 df_by_id는 customer_id만 기준으로 중복을 제거합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 이커머스 회사에서 주문 데이터를 분석한다고 가정해봅시다.
같은 주문이 시스템 오류로 두 번 저장되었다면, 매출 집계가 두 배로 뻥튀기됩니다. 이런 문제를 방지하기 위해 주문 ID를 기준으로 중복을 제거해야 합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 무조건 전체 컬럼 기준으로 중복을 제거하는 것입니다.
하지만 같은 고객이 같은 상품을 여러 번 구매하는 것은 정상적인 데이터일 수 있습니다. 따라서 어떤 컬럼을 기준으로 중복을 판단할지 비즈니스 로직을 고려해서 결정해야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 바로 코드를 작성해봤습니다.
"customer_id 기준으로 중복을 제거하니까 데이터가 깔끔해졌네요!" 중복 데이터 처리를 제대로 이해하면 더 정확한 분석 결과를 얻을 수 있습니다. 여러분도 데이터를 다루기 전에 항상 중복 여부를 확인하는 습관을 들이세요.
실전 팁
💡 - 중복 제거 전에 duplicated().sum()으로 몇 개의 중복이 있는지 먼저 확인하세요
- keep='first'는 처음 나온 행을, keep='last'는 마지막 행을, keep=False는 모든 중복 행을 삭제합니다
- 중복을 제거한 후에는 인덱스가 불연속적이 될 수 있으므로 reset_index(drop=True)로 인덱스를 재정렬하세요
3. 데이터 타입 변환
김개발 씨가 데이터를 정리하고 나이 컬럼의 평균을 구하려고 했습니다. 그런데 이상한 에러가 발생합니다.
"왜 숫자끼리 평균을 못 구하지?" 당황한 김개발 씨가 데이터 타입을 확인해보니, 나이가 문자열로 저장되어 있었습니다. "숫자처럼 생겼는데 숫자가 아니라고요?"
데이터 타입 변환은 컬럼의 자료형을 다른 형태로 바꾸는 것입니다. 마치 텍스트 파일의 '25'를 계산 가능한 숫자 25로 바꾸는 것과 같습니다.
Pandas에서는 **astype()**으로 명시적 변환을, pd.to_numeric(), pd.to_datetime() 등으로 안전한 변환을 할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 타입이 잘못된 샘플 데이터
df = pd.DataFrame({
'user_id': ['1', '2', '3', '4'],
'age': ['25', '30', '미입력', '28'],
'salary': [3000000, 3500000, 4000000, 3200000],
'is_active': [1, 0, 1, 1]
})
# 현재 데이터 타입 확인
print(df.dtypes)
# 간단한 타입 변환 (오류 없는 경우)
df['user_id'] = df['user_id'].astype(int)
# 오류 가능성이 있는 변환 (errors='coerce'로 안전하게)
df['age'] = pd.to_numeric(df['age'], errors='coerce')
# 정수를 불리언으로 변환
df['is_active'] = df['is_active'].astype(bool)
김개발 씨는 고객의 평균 나이를 계산하려고 df['age'].mean()을 실행했습니다. 그런데 TypeError가 발생합니다.
"숫자인데 왜 평균을 못 구하지?" 화면을 노려보던 김개발 씨에게 박시니어 씨가 다가왔습니다. "df.dtypes로 데이터 타입을 확인해보세요." 박시니어 씨의 말에 김개발 씨가 확인해보니, age 컬럼의 타입이 'object'로 되어 있습니다.
숫자처럼 보이지만 실제로는 문자열이었던 것입니다. 그렇다면 데이터 타입 변환이란 정확히 무엇일까요?
쉽게 비유하자면, 데이터 타입은 마치 그릇의 종류와 같습니다. 물을 담으려면 컵이 필요하고, 밥을 담으려면 그릇이 필요합니다.
마찬가지로 계산을 하려면 숫자 타입이, 날짜 계산을 하려면 datetime 타입이 필요합니다. 데이터가 올바른 '그릇'에 담겨있어야 원하는 작업을 할 수 있습니다.
데이터 타입이 왜 잘못될까요? CSV 파일에서 데이터를 읽어올 때, Pandas는 각 컬럼의 타입을 자동으로 추론합니다.
하지만 숫자 컬럼에 '미입력' 같은 문자가 하나라도 있으면, Pandas는 안전하게 전체를 문자열로 처리합니다. 또한 엑셀에서 복사해온 데이터나 웹 스크래핑 데이터는 대부분 문자열로 들어옵니다.
바로 이런 문제를 해결하기 위해 Pandas는 여러 가지 타입 변환 방법을 제공합니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 df.dtypes는 각 컬럼의 현재 데이터 타입을 보여줍니다. 이 단계가 가장 먼저 수행되어야 합니다.
문제가 뭔지 알아야 해결할 수 있기 때문입니다. 다음으로 **astype(int)**는 문자열을 정수로 변환합니다.
user_id 컬럼은 모든 값이 숫자로 변환 가능하므로 문제없이 작동합니다. 하지만 주의할 점이 있습니다.
astype()은 변환이 불가능한 값이 있으면 에러를 발생시킵니다. 이럴 때 사용하는 것이 **pd.to_numeric()**입니다.
errors='coerce' 옵션을 주면, 변환할 수 없는 값(예: '미입력')은 NaN으로 바꿔줍니다. 에러 대신 결측치로 처리하는 안전한 방법입니다.
마지막으로 **astype(bool)**은 0과 1을 False와 True로 변환합니다. 활성 사용자 여부 같은 이진 데이터에 유용합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 은행에서 대출 심사 데이터를 분석한다고 가정해봅시다.
소득 정보가 문자열로 저장되어 있다면 숫자로 변환해야 평균 소득이나 소득 분포를 계산할 수 있습니다. 또한 대출 승인 여부(Y/N)를 불리언으로 변환하면 조건 필터링이 훨씬 쉬워집니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 무작정 astype()을 사용하는 것입니다.
데이터에 예외적인 값이 하나라도 있으면 전체 변환이 실패합니다. 따라서 pd.to_numeric()의 errors='coerce' 옵션처럼 안전한 변환 방법을 사용하고, 변환 후에는 생성된 NaN 값을 처리해야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. pd.to_numeric()으로 age 컬럼을 변환한 김개발 씨는 드디어 평균 나이를 계산할 수 있었습니다.
"타입만 바꿨을 뿐인데 모든 게 해결됐네요!" 데이터 타입 변환을 제대로 이해하면 데이터 분석의 첫 관문을 수월하게 통과할 수 있습니다. 데이터를 받으면 항상 dtypes부터 확인하는 습관을 들이세요.
실전 팁
💡 - 데이터를 받으면 가장 먼저 df.dtypes와 df.info()로 타입을 확인하세요
- pd.to_numeric(), pd.to_datetime() 등은 errors='coerce' 옵션으로 안전하게 변환할 수 있습니다
- 메모리 절약이 필요하다면 int64 대신 int32나 int16으로 다운캐스팅을 고려하세요
4. apply 함수 활용
김개발 씨에게 새로운 미션이 주어졌습니다. 고객의 나이에 따라 '청년', '중년', '장년'으로 등급을 매겨야 합니다.
"for문으로 한 줄씩 돌면서 처리해야 하나?" 머리를 긁적이는 김개발 씨에게 박시니어 씨가 더 우아한 방법을 알려주었습니다.
apply() 함수는 DataFrame이나 Series의 각 요소에 사용자 정의 함수를 적용하는 방법입니다. 마치 공장의 컨베이어 벨트처럼, 데이터가 함수를 통과하면서 변환됩니다.
for문보다 간결하고, 복잡한 변환 로직을 깔끔하게 적용할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
df = pd.DataFrame({
'name': ['김철수', '이영희', '박민수', '최지은'],
'age': [25, 45, 62, 33],
'salary': [3000000, 5500000, 7000000, 4200000]
})
# 나이에 따른 세대 구분 함수
def categorize_age(age):
if age < 30:
return '청년'
elif age < 50:
return '중년'
else:
return '장년'
# apply로 새 컬럼 생성
df['generation'] = df['age'].apply(categorize_age)
# lambda로 간단한 변환 (연봉을 만원 단위로)
df['salary_10k'] = df['salary'].apply(lambda x: x // 10000)
# 여러 컬럼을 활용한 apply (axis=1)
df['info'] = df.apply(lambda row: f"{row['name']}({row['generation']})", axis=1)
김개발 씨는 고객 데이터에 세대 구분 컬럼을 추가해야 합니다. 처음에는 for문으로 한 줄씩 처리하려고 했습니다.
하지만 데이터가 100만 건이나 됩니다. "이거 다 돌리면 얼마나 걸리지..." 박시니어 씨가 미소를 지으며 다가왔습니다.
"Pandas에는 **apply()**라는 강력한 도구가 있어요. for문보다 훨씬 간결하고, 대부분의 경우 더 빠릅니다." 그렇다면 apply() 함수란 정확히 무엇일까요?
쉽게 비유하자면, apply()는 마치 공장의 컨베이어 벨트와 같습니다. 원재료(데이터)가 벨트를 타고 지나가면서 가공 기계(함수)를 통과합니다.
모든 원재료가 같은 과정을 거쳐 완제품으로 변환됩니다. 이처럼 apply()도 데이터의 각 요소에 동일한 함수를 자동으로 적용합니다.
apply()가 왜 필요할까요? Pandas에서 기본 제공하는 연산(덧셈, 뺄셈, 평균 등)만으로는 복잡한 비즈니스 로직을 처리하기 어렵습니다.
예를 들어 "30세 미만은 청년, 50세 미만은 중년, 그 외는 장년"이라는 조건은 단순 연산으로 표현할 수 없습니다. 이럴 때 사용자 정의 함수와 apply()의 조합이 빛을 발합니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 categorize_age라는 함수를 정의했습니다.
이 함수는 나이를 받아서 세대를 문자열로 반환합니다. 일반적인 Python 함수입니다.
다음으로 **df['age'].apply(categorize_age)**를 실행합니다. 이 한 줄로 age 컬럼의 모든 값이 categorize_age 함수를 통과하면서 변환됩니다.
25는 '청년'으로, 45는 '중년'으로, 62는 '장년'으로 바뀝니다. lambda 표현식을 사용하면 더 간단한 변환을 한 줄로 처리할 수 있습니다.
lambda x: x // 10000은 익명 함수로, 값을 10000으로 나눈 몫을 반환합니다. 특히 주목할 부분은 axis=1 옵션입니다.
기본적으로 apply()는 각 요소에 함수를 적용하지만, axis=1을 주면 각 행(row) 전체를 함수에 전달합니다. 이렇게 하면 여러 컬럼의 값을 조합해서 새로운 값을 만들 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 마케팅 팀에서 고객 세그먼트를 나눈다고 가정해봅시다.
나이, 구매 금액, 방문 횟수 등 여러 요소를 종합해서 VIP, 일반, 휴면 고객으로 분류해야 합니다. 이런 복잡한 로직을 함수로 정의하고 apply()로 적용하면, 수백만 고객을 순식간에 분류할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 작업에 apply()를 사용하는 것입니다.
단순한 연산(곱하기, 더하기 등)은 벡터화 연산이 훨씬 빠릅니다. 예를 들어 df['salary'] * 2는 apply(lambda x: x * 2)보다 수십 배 빠릅니다.
apply()는 복잡한 로직이 필요할 때만 사용하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.
apply()를 사용해본 김개발 씨는 감탄했습니다. "for문 열 줄이 한 줄로 줄었어요!
게다가 코드도 훨씬 읽기 쉽네요." apply() 함수를 제대로 이해하면 복잡한 데이터 변환도 우아하게 처리할 수 있습니다. 여러분만의 함수를 만들어서 데이터에 적용해 보세요.
실전 팁
💡 - 단순 연산은 벡터화(df['col'] * 2)가, 복잡한 로직은 apply()가 적합합니다
- 여러 컬럼을 참조해야 할 때는 axis=1을 사용하세요
- apply() 내부에서 print()를 사용하면 함수가 어떻게 작동하는지 디버깅할 수 있습니다
5. 문자열 처리
김개발 씨는 이번에 텍스트 데이터를 정리해야 합니다. 이메일 주소에서 도메인만 추출하고, 이름의 공백을 제거하고, 전화번호 형식을 통일해야 합니다.
"문자열 처리가 이렇게 많다니..." 한숨을 쉬는 김개발 씨에게 박시니어 씨가 Pandas의 비밀 무기를 알려주었습니다.
str accessor는 Pandas Series의 문자열 데이터를 다루는 특별한 도구입니다. 마치 스위스 군용 칼처럼, 분리, 추출, 치환, 대소문자 변환 등 다양한 문자열 작업을 한 번에 처리할 수 있습니다.
Python의 문자열 메서드를 시리즈 전체에 벡터화하여 적용합니다.
다음 코드를 살펴봅시다.
import pandas as pd
df = pd.DataFrame({
'name': [' 김철수 ', 'lee young hee', 'PARK MIN SU'],
'email': ['kim@gmail.com', 'lee@naver.com', 'park@daum.net'],
'phone': ['010-1234-5678', '01012345678', '010.1234.5678']
})
# 공백 제거
df['name_clean'] = df['name'].str.strip()
# 대소문자 변환
df['name_title'] = df['name'].str.strip().str.title()
# 문자열 분리 및 추출
df['domain'] = df['email'].str.split('@').str[1]
# 정규표현식으로 특수문자 제거
df['phone_clean'] = df['phone'].str.replace(r'[^0-9]', '', regex=True)
# 특정 문자열 포함 여부 확인
df['is_gmail'] = df['email'].str.contains('gmail')
김개발 씨는 고객 데이터를 정리하다가 머리가 아파왔습니다. 이름 앞뒤에 공백이 붙어있는 경우도 있고, 어떤 이름은 전부 대문자, 어떤 이름은 전부 소문자입니다.
전화번호도 형식이 제각각입니다. "이걸 일일이 고치려면 얼마나 걸리지?" 박시니어 씨가 웃으며 말했습니다.
"Pandas의 str accessor를 사용하면 됩니다. 문자열 처리의 만능 도구예요." 그렇다면 str accessor란 정확히 무엇일까요?
쉽게 비유하자면, str accessor는 마치 문서 편집기의 '모두 바꾸기' 기능과 같습니다. 문서에서 특정 단어를 한 번에 다른 단어로 바꾸듯이, DataFrame의 전체 컬럼에 문자열 연산을 일괄 적용합니다.
Python의 기본 문자열 메서드(strip, split, replace 등)를 시리즈 전체에 사용할 수 있게 해주는 마법의 다리입니다. str accessor가 왜 필요할까요?
문자열 처리는 데이터 전처리에서 가장 시간이 많이 걸리는 작업 중 하나입니다. 공백 제거, 대소문자 통일, 특수문자 처리, 패턴 추출 등 해야 할 일이 많습니다.
for문으로 한 줄씩 처리하면 코드가 길어지고 속도도 느립니다. str accessor를 사용하면 이 모든 작업을 한 줄로 처리할 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **str.strip()**은 문자열 앞뒤의 공백을 제거합니다.
' 김철수 '가 '김철수'로 깔끔하게 변합니다. 다음으로 **str.title()**은 각 단어의 첫 글자를 대문자로, 나머지는 소문자로 변환합니다.
'lee young hee'가 'Lee Young Hee'로 바뀝니다. 여러 str 메서드를 체이닝(연결)해서 사용할 수 있다는 점도 주목하세요.
**str.split('@').str[1]**은 특히 강력합니다. 먼저 '@'를 기준으로 문자열을 분리한 후, 두 번째 요소([1])를 추출합니다.
이메일에서 도메인만 쏙 빼내는 것입니다. **str.replace()**는 정규표현식을 지원합니다.
r'[^0-9]'는 '숫자가 아닌 모든 문자'를 의미하며, 이를 빈 문자열로 치환하면 숫자만 남습니다. 전화번호의 하이픈, 점 등이 모두 제거됩니다.
마지막으로 **str.contains()**는 특정 문자열 포함 여부를 True/False로 반환합니다. 필터링할 때 매우 유용합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 로그 데이터를 분석한다고 가정해봅시다.
로그 메시지에서 에러 코드를 추출하거나, 특정 패턴의 URL을 찾거나, 타임스탬프를 파싱해야 할 수 있습니다. str accessor와 정규표현식을 조합하면 복잡한 텍스트 파싱도 몇 줄로 해결할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 NaN 값 처리를 잊는 것입니다.
문자열 컬럼에 NaN이 있으면 str 메서드가 에러를 발생시킬 수 있습니다. 따라서 str 연산 전에 결측치를 처리하거나, na=False 옵션을 사용해야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. str accessor를 알게 된 김개발 씨는 눈이 반짝였습니다.
"for문 없이 한 줄로 전체 컬럼을 처리할 수 있다니, 정말 편리하네요!" str accessor를 제대로 이해하면 텍스트 데이터 정리가 훨씬 수월해집니다. 정규표현식까지 익히면 어떤 문자열 패턴도 다룰 수 있습니다.
실전 팁
💡 - str 메서드는 체이닝이 가능합니다: df['col'].str.strip().str.lower()
- 정규표현식을 사용하려면 regex=True 옵션을 명시하세요
- NaN이 있는 컬럼에서는 str.contains()에 na=False를 추가하세요
6. 날짜 시간 데이터 처리
김개발 씨의 마지막 과제는 날짜 데이터 처리입니다. 주문일시에서 요일별 매출을 분석하고, 특정 기간의 데이터를 필터링해야 합니다.
"날짜 계산은 항상 헷갈려..." 난감해하는 김개발 씨에게 박시니어 씨가 Pandas의 datetime 기능을 소개해주었습니다.
날짜/시간 데이터 처리는 데이터 분석에서 매우 중요한 영역입니다. Pandas는 **pd.to_datetime()**으로 문자열을 날짜로 변환하고, dt accessor로 연도, 월, 요일 등을 추출합니다.
마치 달력을 프로그래밍으로 다루는 것처럼, 날짜 연산과 시계열 분석이 가능해집니다.
다음 코드를 살펴봅시다.
import pandas as pd
df = pd.DataFrame({
'order_id': [1, 2, 3, 4, 5],
'order_date': ['2024-01-15', '2024-02-20', '2024-03-10', '2024-03-15', '2024-04-01'],
'amount': [50000, 75000, 30000, 120000, 45000]
})
# 문자열을 datetime으로 변환
df['order_date'] = pd.to_datetime(df['order_date'])
# 연도, 월, 요일 추출
df['year'] = df['order_date'].dt.year
df['month'] = df['order_date'].dt.month
df['day_name'] = df['order_date'].dt.day_name()
# 특정 기간 필터링
start_date = '2024-02-01'
end_date = '2024-03-31'
mask = (df['order_date'] >= start_date) & (df['order_date'] <= end_date)
df_filtered = df[mask]
# 날짜 차이 계산 (오늘부터 며칠 전인지)
df['days_ago'] = (pd.Timestamp.now() - df['order_date']).dt.days
김개발 씨는 드디어 데이터 전처리의 마지막 관문에 도착했습니다. 주문 데이터에서 월별 매출 추이를 분석하고, 최근 30일 주문만 필터링해야 합니다.
"날짜 계산은 윤년도 있고 월마다 일수도 다르고... 복잡하지 않을까?" 박시니어 씨가 안심시켜줍니다.
"걱정 마세요. Pandas의 datetime 기능을 사용하면 날짜 계산이 정말 쉬워집니다." 그렇다면 날짜/시간 데이터 처리란 정확히 무엇일까요?
쉽게 비유하자면, pd.to_datetime()은 마치 달력 해석사와 같습니다. '2024-01-15'라는 문자열을 보면, "이건 2024년 1월 15일 월요일이고, 올해의 15번째 날이며, 1분기에 속한다"라고 해석해줍니다.
이렇게 해석된 날짜는 연산이 가능해집니다. 두 날짜의 차이를 구하거나, 한 달 후 날짜를 계산하는 것이 가능해지는 것입니다.
날짜 처리가 왜 중요할까요? 비즈니스 데이터 분석의 상당 부분은 시간과 관련되어 있습니다.
월별 매출 추이, 요일별 방문자 수, 계절별 판매량 등 시간 축을 기준으로 한 분석이 매우 많습니다. 날짜가 단순 문자열로 저장되어 있으면 이런 분석이 불가능합니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **pd.to_datetime()**은 문자열을 datetime 객체로 변환합니다.
'2024-01-15'라는 텍스트가 실제 날짜 데이터로 바뀌는 것입니다. Pandas는 다양한 날짜 형식을 자동으로 인식하지만, 애매한 경우 format 파라미터로 형식을 지정할 수 있습니다.
다음으로 dt accessor는 str accessor의 날짜 버전입니다. dt.year는 연도를, dt.month는 월을, dt.day_name()은 요일 이름을 추출합니다.
이 외에도 dt.quarter(분기), dt.week(주차), dt.hour(시간) 등 다양한 속성을 추출할 수 있습니다. 날짜 비교는 일반 숫자 비교처럼 >=, <= 연산자를 사용합니다.
위 코드에서는 2024년 2월 1일부터 3월 31일까지의 주문만 필터링합니다. 마지막으로 날짜 차이 계산도 간단합니다.
두 datetime을 빼면 timedelta 객체가 되고, .dt.days로 일수를 추출할 수 있습니다. 현재 시점 기준 며칠 전 주문인지 계산하는 것입니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 구독 서비스 회사에서 고객 이탈을 분석한다고 가정해봅시다.
마지막 로그인 날짜에서 현재까지 며칠이 지났는지 계산하고, 30일 이상 미접속 고객을 추출할 수 있습니다. 또한 가입월 기준으로 코호트 분석을 하거나, 요일별 트래픽 패턴을 파악할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 타임존을 고려하지 않는 것입니다.
글로벌 서비스라면 UTC 기준으로 저장하고, 필요할 때 로컬 시간으로 변환해야 합니다. 또한 날짜 형식이 일관되지 않은 데이터(2024-01-15, 01/15/2024, 15-Jan-2024 혼재)는 변환 시 오류가 발생할 수 있으므로 format 파라미터를 명시하는 것이 안전합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. datetime 기능을 마스터한 김개발 씨는 뿌듯한 미소를 지었습니다.
"월별 매출 추이 분석이 이렇게 간단할 줄이야!" 박시니어 씨가 김개발 씨의 어깨를 토닥였습니다. "수고했어요.
결측치, 중복, 타입 변환, apply, 문자열, 날짜까지... 이제 데이터 전처리의 기본기는 다 갖췄네요." 날짜/시간 데이터 처리를 제대로 이해하면 시계열 분석의 문이 활짝 열립니다.
여러분도 실제 데이터로 오늘 배운 내용을 연습해 보세요.
실전 팁
💡 - 날짜 형식이 복잡하면 pd.to_datetime(df['col'], format='%Y/%m/%d')처럼 형식을 명시하세요
- 날짜로 그룹핑할 때는 dt.to_period('M')으로 월 단위로 변환하면 편리합니다
- 영업일 기준 계산이 필요하면 pd.bdate_range()나 BDay offset을 활용하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.