두 번째 Quest 할 때 어이없는 실수를 저질렀던 기억이 났다.
현업 때도 간혹 저지르는 실수인데 두 테이블을 join할 때,
key값을 잘못 가져다 쓰면 행이 늘어나게 되는 부분이다.
(코드를 짤 때 실수를 안하는 것도 중요하지만, row건수가 말이 되는지
결국 그 필드에서의 배경 지식도 중요할 것 같음.)
import pandas as pd
import numpy as np
import seaborn as sns
cc_df = pd.read_csv('data/fraud.csv')
pd.set_option('display.max_columns', 50) # 컬럼 수 최대 50개까지 보여줌!
# cc_df의 컬럼명과 자료형을 확인합니다.
cc_df.info()
# 기초적인 기술 통계량 계산
cc_df.describe()
# Q. job 컬럼에 포함된 값이 몇 종류인지 확인합니다.
# [[YOUR CODE]]
cc_df['job'].nunique()
# Q. cc_num 컬럼에 포함된 값이 몇 종류인지 확인합니다.
# [[YOUR CODE]]
cc_df['cc_num'].nunique()
# 불필요한 컬럼들을 제거합니다.
cc_df.drop(['merchant','first','last','street','city','state','zip','job','trans_num','unix_time'], axis = 1, inplace= True)
# cc_num 컬럼의 값을 기준으로 정렬합니다.
cc_df.sort_values('cc_num')
# cc_num 컬럼의 값마다 데이터 개수를 계산합니다.
cc_df['cc_num'].value_counts()
amt_info = cc_df.groupby('cc_num')['amt'].agg(['mean','std']).reset_index()
amt_info.to_pickle('./amt_info.pkl')
# Q. cc_num 컬럼을 기준으로, cc_df와 amt_info 데이터를 합쳐서 cc_df에 저장합니다.
# (left merge를 수행합니다.)
# [[YOUR CODE]]
cc_df = cc_df.merge(amt_info, on = 'cc_num', how = 'left')
# Q. 결제금액(amt)의 z-score를 계산하여 amt_z 컬럼에 저장합니다.
# 평균: mean, 표준편차: std
# [[YOUR CODE]]
cc_df['amt_z'] = (cc_df['amt']-cc_df['mean'])/(cc_df['std'])
# z-score 계산이 완료되었으니 mean, std 컬럼을 제거합니다.
cc_df.drop(['mean','std'], axis = 1, inplace = True)
# cc_num, category 컬럼의 값마다 결제금액(amt)의 평균, 표준편차를 계산하여 cat_info에 저장합니다.
cat_info = cc_df.groupby(['cc_num','category'])['amt'].agg(['mean','std']).reset_index()
cat_info.to_pickle('./cat_info.pkl')
# Q. cc_num 컬럼을 기준으로, cc_df와 cat_info 데이터를 합쳐서 cc_df에 저장합니다.
# (left merge를 수행합니다.)
# [[YOUR CODE]]
cc_df = cc_df.merge(cat_info, on = ['cc_num','category'], how = 'left') # on이 key값, how가 join 방법
cc_df.shape
# Q. 결제금액(amt)의 z-score를 계산하여 cat_amt_z 컬럼에 저장합니다.
# 평균: mean, 표준편차: std
# [[YOUR CODE]]
cc_df['cat_amt_z'] = (cc_df['amt'] - cc_df['mean'])/cc_df['std']
# z-score 계산이 완료되었으니 mean, std 컬럼을 제거합니다.
cc_df.drop(['mean','std'], axis =1 , inplace = True)
# Q. trans_date_trans_time 컬럼에서 시간 값을 추출하여 hour 컬럼에 저장합니다.
# (힌트: datetime 자료형을 이용합니다.)
# [[YOUR CODE]]
cc_df['hour'] = pd.to_datetime(cc_df['trans_date_trans_time']).dt.hour
# 결제시간을 morning, afternoon, night, evening으로 분류하기 위해 함수를 정의합니다.
def hour_func(x):
if (x >= 6) & (x < 12):
return 'morning'
elif (x >= 12) & (x < 18):
return 'afternoon'
elif (x >= 18) & (x < 23):
return 'night'
else:
return 'evening'
# Q. hour 컬럼에 hour_func 함수를 적용한 값을 hour_cat 컬럼에 저장합니다.
# [[YOUR CODE]]
cc_df['hour_cat'] = cc_df['hour'].apply(lambda x: hour_func(x))
# Q. 시간대별로 데이터 수를 계산하여 출력합니다.
# [[YOUR CODE]]
cc_df['hour'].value_counts().sort_index() # value_counts?로?
# cc_num 컬럼의 값마다 amt 컬럼의 데이터 수를 계산하여 all_cnt 변수로 저장합니다.
all_cnt = cc_df.groupby('cc_num')['amt'].count().reset_index()
# cc_num, hour_cat 컬럼의 값마다 amt 컬럼의 데이터 수를 계산하여 hour_cnt 변수로 저장합니다.
hour_cnt = cc_df.groupby(['cc_num','hour_cat'])['amt'].count().reset_index()
# Q. cc_num 컬럼을 기준으로 hour_cnt와 all_cnt를 합칩니다.
# (hour_cnt에서 left merge를 수행합니다.)
# [[YOUR CODE]]
hour_cnt = hour_cnt.merge(all_cnt, on='cc_num',how='left')
# Q. amt_x와 amt_y 컬럼의 이름을 각각 hour_cnt, total_cnt로 변경합니다.
# [[YOUR CODE]]
hour_cnt = hour_cnt.rename(columns={'amt_x':'hour_cnt','amt_y':'total_cnt'})
# Q. 전체 거래 건수(total_cnt)에 대한 시간대별 거래 건수(hour_cnt)의 비율을 계산하여
# hour_perc 컬럼에 저장합니다.
# [[YOUR CODE]]
hour_cnt['hour_perc'] = hour_cnt['hour_cnt'] / hour_cnt['total_cnt']
hour_cnt = hour_cnt[['cc_num','hour_cat','hour_perc']]
hour_cnt.to_pickle('./hour_cnt.pkl')
# Q. cc_num, hour_cat 컬럼을 기준으로 cc_df와 hour_cnt 데이터를 합칩니다.
# (cc_df에서 left merge를 수행합니다.)
# [[YOUR CODE]]
cc_df = cc_df.merge(hour_cnt, on = ['cc_num','hour_cat'], how='left')
# 시간 관련 feature 분석이 완료되었으므로 불필요해진 컬럼들을 제거합니다.
cc_df.drop(['trans_date_trans_time', 'hour', 'hour_cat'], axis =1 , inplace = True)
# 거리 계산에 필요한 geopy 라이브러리를 설치합니다.
!pip install geopy
from geopy.distance import distance
# 이렇게 위도와 경도 값으로 두 지점 사이의 거리를 구할 수 있습니다.
distance((48.8878, -118.2105), (49.159047, -118.186462)).km
# 고객의 위치와 상점의 위치 사이의 거리를 계산하여 distance 컬럼에 저장합니다.
# (모든 데이터가 계산되므로 실행 시간이 오래 걸릴 수 있습니다.
cc_df['distance'] = cc_df.apply(lambda x: distance((x['lat'], x['long']), (x['merch_lat'], x['merch_long'])).km, axis = 1)
from datetime import datetime
# 데이터 10000개만 계산해도 시간이 이렇게 걸립니다.
start_time = datetime.now()
cc_df.head(10000).apply(lambda x: distance((x['lat'], x['long']), (x['merch_lat'], x['merch_long'])).km, axis = 1)
datetime.now() - start_time
# Q. cc_num 컬럼의 값마다 distance의 평균, 표준편차를 계산하여 dist_info 변수에 저장합니다.
# [[YOUR CODE]]
dist_info = cc_df.groupby(['cc_num'])['distance'].agg(['mean','std']).reset_index()
# dist_info
dist_info.to_pickle('./dist_info.pkl')
# Q. cc_num 컬럼을 기준으로 cc_df와 dist_info 데이터를 합칩니다.
# (cc_df에서 left merge를 수행합니다.)
# [[YOUR CODE]]
cc_df = cc_df.merge(dist_info, on = 'cc_num', how='left')
# Q. distance 값의 z-score를 계산하여 dist_z 컬럼에 저장합니다.
# [[YOUR CODE]]
cc_df['dist_z'] = (cc_df['distance'] - cc_df['mean'])/(cc_df['std'])
# 불필요해진 컬럼들을 제거합니다.
cc_df.drop(['lat','long','merch_lat','merch_long','mean','std'], axis = 1, inplace = True)
# Q. dob 컬럼에서 연도 값만 추출하여 dob 컬럼에 저장합니다.
# [[YOUR CODE]]
cc_df['dob'] = pd.to_datetime(cc_df['dob']).dt.year
# cc_df.head()
# 범주형 컬럼 'category'에 몇 종류의 값이 있는지 확인해봅시다.
cc_df['category'].nunique()
# Q. cc_df의 범주형 데이터에 원-핫 인코딩을 적용합니다.
# (drop_first 옵션은 True로 설정합니다.)
# [[YOUR CODE]]
cc_df = pd.get_dummies(cc_df, columns=['category', 'gender'], drop_first=True)
# 이제 불필요해진 cc_num 컬럼을 제거합니다.
cc_df.drop('cc_num', axis = 1, inplace = True)