원본 데이터를 그대로 쓰지 않고 문자열, 집계, 기존 컬럼의 조합으로 모델에 도움이 될 새 Feature를 만드는 방법을 정리한다.
1. Feature Extraction이 필요한 이유
원본 컬럼에 정보가 숨어 있어도 모델이 그대로 이해하기 어려울 수 있다.
이름에서 호칭과 성을 꺼내거나, 객실 등급별 평균 요금을 만들면 원본보다 의미가 분명한 Feature를 얻을 수 있다.
2. 먼저 학습·테스트 데이터를 분리한다
from sklearn.model_selection import train_test_split
SEED = 42
X_tr, X_te = train_test_split(df, random_state=SEED, test_size=0.2)
X_tr = X_tr.reset_index(drop=True)
X_te = X_te.reset_index(drop=True)
X_tr.shape, X_te.shape
실행 결과
((712, 12), (179, 12))
집계 기준이나 변환 규칙은 학습 데이터에서 만들고 테스트 데이터에는 적용만 해야 한다. 테스트 데이터를 함께 보고 규칙을 만들면 데이터 누수가 발생할 수 있다.
3. 필요 없는 식별자 삭제하기
X_tr['passengerid'].nunique(), X_tr.shape[0]
# (712, 712)
X_tr.drop('passengerid', axis=1, inplace=True)
X_te.drop('passengerid', axis=1, inplace=True)
모든 행에서 값이 다른 단순 ID는 일반적인 패턴을 학습시키기 어렵다. 실제 의미가 없는 식별자라면 제거한다.
4. 자료형을 목적에 맞게 바꾸기
X_tr['pclass'] = X_tr['pclass'].astype('category')
X_te['pclass'] = X_te['pclass'].astype('category')
X_tr['age'] = X_tr['age'].astype('float32')
X_te['age'] = X_te['age'].astype('float32')
숫자로 저장되어 있어도 등급처럼 범주를 뜻하면 category가 적절하다. 반대로 계산할 값은 숫자형으로 맞춘다.
5. 문자열에서 새 정보를 추출하기
5-1. 앞뒤 공백 제거
X_tr['name'] = X_tr['name'].map(lambda x: x.strip())
X_tr['ticket'] = X_tr['ticket'].map(lambda x: x.strip())
5-2. 이름에서 호칭 찾기
dict_designation = {
'Mr.': '남성', 'Master.': '남성',
'Mrs.': '여성', 'Miss.': '여성'
}
def add_designation(name):
for key, value in dict_designation.items():
if key in name:
return value
return 'unknown'
X_tr['designation'] = X_tr['name'].map(add_designation)
X_te['designation'] = X_te['name'].map(add_designation)
포함 여부를 이용하면 긴 이름 문자열에서 Mr., Mrs., Miss. 같은 호칭을 찾아 의미 있는 범주형 Feature로 바꿀 수 있다.
5-3. 이름과 티켓 문자열 분리
X_tr['first_name'] = X_tr['name'].map(lambda x: x.split(',')[0].strip())
X_te['first_name'] = X_te['name'].map(lambda x: x.split(',')[0].strip())
def add_ticket_number(ticket):
try:
return int(ticket.split(' ')[-1])
except ValueError:
return 0
X_tr['ticket_number'] = X_tr['ticket'].map(add_ticket_number)
X_te['ticket_number'] = X_te['ticket'].map(add_ticket_number)
6. 집계값을 새 Feature로 만들기
6-1. 객실 등급별 평균 요금
df_pivot = pd.pivot_table(
X_tr, index='pclass', values='fare', aggfunc='mean'
).reset_index()
df_pivot.rename(columns={'fare':'fare_mean_by_pclass'}, inplace=True)
X_tr = pd.merge(X_tr, df_pivot, how='left', on='pclass')
X_te = pd.merge(X_te, df_pivot, how='left', on='pclass')
학습 데이터에서 만든 등급별 평균 요금을 학습·테스트 데이터에 동일하게 합친다.
6-2. groupby로 여러 통계 만들기
agg_dict = {'survived':'mean', 'sibsp':'nunique', 'parch':'nunique'}
df_groupby = X_tr.groupby('pclass').agg(agg_dict).reset_index()
한 기준 컬럼에 대해 평균, 고유값 개수 등 여러 집계값을 한 번에 만들 수 있다. 단, 타겟 평균처럼 정답을 사용하는 Feature는 데이터 누수에 특히 주의해야 한다.
7. map과 apply로 컬럼 변환·조합하기
def sub_age(age):
return age // 10
X_tr['sub_age'] = X_tr['age'].map(sub_age)
def add_sub_embarked(row):
return str(row['embarked']) + str(row['pclass']) + str(row['sibsp']) + str(row['parch'])
X_tr['sub_embarked'] = X_tr.apply(add_sub_embarked, axis=1)
map(): 한 컬럼의 값 하나씩 바꿀 때 사용한다.
apply(axis=1): 여러 컬럼을 한 행 단위로 함께 사용할 때 편리하다.
8. 오래 걸리는 반복 작업은 tqdm으로 확인하기
from tqdm.auto import tqdm
for value in tqdm(values, desc='feature extraction'):
# 반복 작업
pass
tqdm.pandas()
df['new_col'] = df['col'].progress_apply(function)
tqdm은 반복 진행률과 남은 시간을 보여준다. 계산 결과를 바꾸지는 않지만 대용량 전처리 상태를 확인하기 좋다.
핵심: 좋은 파생변수는 원본 정보의 의미를 더 잘 드러낸다. 규칙과 집계값은 학습 데이터에서 만들고 테스트에는 그대로 적용한다.
'개발 공부 > 데이터 분석' 카테고리의 다른 글
| [데이터 분석] 데이터 클렌징 시작하기 | 결측치 확인·삭제·대체·검증 (0) | 2026.09.03 |
|---|---|
| [데이터 분석] Titanic 변수 관계 분석 | 교차표·피벗테이블·상관관계 (0) | 2026.09.03 |
| [데이터 분석] 분포와 이상치 분석 | 왜도·첨도·로그 변환 이해하기 (0) | 2026.09.02 |
| [데이터 분석] EDA 시작하기 | Titanic 구조 확인 (1) | 2026.09.02 |
| [데이터 분석] Seaborn 데이터 시각화 기초 (0) | 2026.09.01 |