세로형
Recent Posts
Recent Comments
Link
03-29 03:47
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

꿈 많은 사람의 이야기

[6주차] 새벽 5시 캐글(kaggle) 필사하기 - home credit 데이터 편 - 2 본문

kaggle(캐글)

[6주차] 새벽 5시 캐글(kaggle) 필사하기 - home credit 데이터 편 - 2

이수진의 블로그 2019. 2. 10. 15:04


이번 캐글 필사 편은 지난 필사 편(https://lsjsj92.tistory.com/435)에 이은 home credit의 두 번째 필사입니다.


이번에는 다른 커널을 진행해봅니다.

지난 커널에서는 EDA를 위주로 봤는데요

이번에는 실제 모델을 만들고 제출을 해봅니다.

그리고 application_train.csv의 기본 파일 외에 bureau라는 data를 가지고 

고객이 이 회사에서 대출하기 전에 다른 금융 기관에서 대출했던 내역을 참고해서 모델을 만들어 봅니다.




필사한 커널은 사진 위에 나와있습니다.

마찬가지로 기본적인 numpy, pandas, matplotlib, seaborn을 import합니다.

그리고 application_train, test.csv를 가져오지 않고 bureau.csv를 가져옵니다.

이 데이터는 고객이 과거 다른 금융 기관에서 대출했던 내역의 정보를 가지고 있습니다.

이 데이터를 가지고 agg 연산자를 통해 mean, max, min, sum, count 값을 뽑아내면서 데이터를 핸들링 하고

이 처리된 데이터를 train.csv 내용에 merge 시킬 것입니다.



bureau 내용을 보면 위와 같습니다.

SK_ID_CURR과 SK_ID_BUREAU가 있으며 그 외 15개의 컬럼이 존재합니다.

그리고 info를 보니까 int, float 등의 number type이 있구요

obejct type의 feature도 존재합니다.

나중에 이것들을 기준으로 데이터를 agg합니다.

왜냐하면 categorical data에서 agg를 할 때와 numerical 데이터를 agg할 때에 나올 수 있는 값이 다르기 때문입니다.



먼저 bureau에서 SK_ID_CURR을 기준으로 groupby를 합니다. SK_ID_CURR은 고객이 가지고 있는 데이터 개수마다 존재하니까요

그리고 SK_ID_BUREAU가 몇 번 나왔는지 count를 세보면 위와 같이 나오게 됩니다.

그래서 이렇게 나오는 것을 previous_loan_count 변수에 담아둡니다.

bureau_groupby('SK_ID_CURR', as_index = False)['SK_ID_BUREAU'].count().rename(columns={'SK_ID_BUREAU' : 'previous_loan_counts'})

와 같이 할 수 있습니다.



그리고 앞서 설명드린데로 application_train.csv를 불러와서 train에 넣은 뒤 train.merge(previous_loan_counts, on = 'SK_ID_CURR', how = 'left)를 이용해서 데이터를 합쳐줍니다.



그리고 seaborn의 kdeplot을 이용해 해당 컬럼에 밀도를 볼건데요

여기서 .ix를 사용합니다. pandas에서 dataframe을 다룰 때

data.iloc, data.loc, data.ix 이렇게 3가지가 있습니다.

iloc는 integer position을 통해 값을 찾을 수 있고

loc는 label을 통해 값을 찾습니다.

ix는 둘 다 가능합니다. 만약 label이 숫자라면 label-based index만 합니다.



kdeplot을 만드는 것은 함수로 합니다.

조금 더 보시면 아시겠지만 이제 거의 모든 기능을 함수로 만듭니다.

kdeplot을 만드는 kdf_target도 마찬가지입니다.

corr = df['TARGET].corr(df[var_name])을 통해서 corr 값을 가져오고

avg_repaid = df.ix[df['TARGET'] == 0, var_name].median()

avg_not_repaid = df.ix[df['TARGET'] == 1, var_name].median()을 통해서

상환을 한 사람과 하지 않은 사람들의 median값을 담습니다.

그리고 sns.kdeplot(df.ix[df['TARGET'] == 0, var_name]과

sns.kdeplot(df.ix[df['TARGET'] == 1, var_name]의 2가지 kdeplot을 그려줍니다.



그래서 그릴 때는 위와 같이 원하는 컬럼명과 dataframe을 전달해주면 됩니다.

위는 EXT_SOURCE_3 컬럼에 대해서 train을 전달하여 train에 있는 EXT_SOURCE_3의 kdeplot을 그려줍니다.


이제 우리가 새로 추가한 previous_loan_counts를 kdeplot으로 봐보죠



이렇게 그려집니다. 뭔가 TARGET을 구분하는데 있어 특별한 것이 있어보이진 않습니다.



이제 사실상 본격적인 내용입니다.

bureau에서 가지고온 previous_loan이 별 특별한 것이 없다는 것을 보았습니다.

이제 bureau에서 단순한 count 값이 아닌, SK_ID_CURR별로 묶고, SK_ID_BUREAU는 drop 시킨 후 각 SK_ID_CURR별로 각 컬럼당 count, mean, max, min, sum 값을 뽑아냅니다.

bureau_agg = bureau.drop(columns = ['SK_ID_BUREAU']).groupby('SK_ID_CURR', as_index = False).agg(['count', 'mean', 'max','min', 'sum']).reset_index()를 통해서 진행합니다.


이렇게 하면 각 컬럼 별로 count, mean, max, min, sum이 추가가 되는데요

위를 보면 컬럼이 2단계로 나뉘어져있죠?

CREDIT_DAY_OVERDUE 밑에 mean, max, min 등이 있습니다.

이게 levels[0], levels[1]로 나뉘어 집니다.



그리고 number 변수들만 min, max 등이 가능합니다. categorical 변수는 저 5개를 다 사용하는 것이 불가능합니다.

그리고 앞서 설명드린 것처럼 levels[0]과 levels[1]에 따라서 컬럼이 나뉘어져 있습니다.

levels[0]는 기존 컬럼들이고 levels[1]은 count, mean, max, min, sum이 되겠습니다.

이렇게 컬럼이 되면 매우 불편하기 때문에 columns라는 배열에다가 이중 for문을 돌면서 기존 컬럼+level1 컬럼이름으로 새롭게 컬럼명을 만들고 append를 통해 넣어줍니다.



그리고 bureau_agg.columns = columns를 이용해서 컬럼명을 새롭게 셋팅해줍니다!

이후 train = train.merge(bureau_agg, on = 'SK_ID_CURR', how = 'left')로 train에 합쳐줍니다.



이로써 train에는 183개의 컬럼이 만들어집니다.

새롭게 만들어진 컬럼으로 corr 값을 보고 싶으시면  corr = trian['TARGET'].corr(train[col])을 이용하시면 됩니다.

이렇게 하면 target의 corr을 보는데 train[col]과 target의 corr을 보게 됩니다.



연관성이 높은 값대로 sort를 하고 싶으면 new_corrs = sorted(new_corrs, key = lambda x : abs(x[1]), reverse = True)를 이용하시면 됩니다.


자! 이제 여기까지를 통해서 numeric 데이터를 count, mean, max, min, sum을 추가하는 과정을 살펴보았습니다.

이제 이 과정을 하나의 함수로 만들어봅니다!



이렇게 하시면 됩니다. 만약 col이 gruop_var가 아닌데, SK_ID라는 글자가 col에 있으면 drop해줍니다.

그리고 grup_ids = df[group_var]를 해주는데요

group_var는 SK_ID_CURR 또는 SK_ID_BUREAU가 오게 될 것입니다.

numeric_df = df.select_dtypes('number')를 사용하시면 dtype가 number인 애들을 가져오게 됩니다.

그리고 group_ids를 numeric_df[group_var]에 넣어줍니다.


이제 아까 진행했던 df.groupby(group_var).agg(['count', 'mean', 'max', 'min', 'sum']).reset_index를 진행하고

컬럼을 새롭게 셋팅해주고

그 값을 return 해줍니다.




이렇게 하면 똑같이 나오게 됩니다.

즉 처음에 했던 과정을 함수로 만들어준 것이죠



그리고 corr을 구했던 과정도 마찬가지로 함수로 만들어줍니다.



이제 numerical 데이터가 아닌 categorical 데이터를 보겠습니다.

categorical 데이터는 자주 사용하는 것이 one-hot-encoding이죠. 여기서는 get_dummies를 이용합니다.

그리고 카테고리 데이터는 sum과 mean만 가져옵니다.

sum은 나온 횟수이고 mean은 일종의 normalization입니다.



여기도 마찬가지로 columns의 level이 나뉘어집니다.



그리고 컬럼명은 numerical data에서와 비슷하게 categorical.columns.levels[0]만큼 for문을 돌면서

새롭게 컬럼을 만들어줍니다. count와 count_norm의 이름을 붙여줍니다.



그리고 train.merge(categorical_grouped, left_on = 'SK_ID"CURR', right_index = True, how='left')를 해주면 

train과 합쳐집니다. 여기서 right_index, left_on이 나오는데요.

음 제가 해본 결과 on = 'SK_ID_CURR', how = 'left'만 해주어도 (numerical에서 한 것과 똑같이 해도) 결과는 똑같이 나왔습니다.

이게 numerical과는 다르게 index 값이 SK_ID_CURR로 들어가 있어서 right_index와 left_on을 한 것 같습니다.



마찬가지로 함수로 만들어줍니다.

df.select_dtypes('object')를 뽑아내서 pd.get_dummies를 만들어줍니다.

그리고 df[group_var}을 넣어주고요

groupby(group_var).agg(['sum', 'mean']) 과정 후 columns.levels를 통해 컬럼명을 부여한 뒤

return 해줍니다.



자 여기까지 bureau.csv 데이터를 가지고 다양하게 컬럼을 추가하고 만들었습니다.

그리고 그 과정을 함수로 묶었죠.

이제 bureau_balance.csv 데이터가 하나 더 있습니다.

이 데이터는 고객이 월 별로 상환하는? 그런 데이터 인 것 같습니다.

이 데이터도 함께 이용할 것입니다.



bureau_balance.csv 데이터를 가지고 옵니다.

여기에도 categorical data가 있는데요.

아까 count_categorical함수를 만들었죠? 이함수에 bureau_balance, group_var = 'SK_ID_BUREAU', df_name = 'bureau_balance'를 넣어줘서

새롭게 컬럼을 추가해서 return 받습니다.



numeric 데이터도 있습니다. bureau_balance_agg에다가 받습니다. 아까 만들어놓은 함수를 이용합니다~



그리고 balance_agg와 balance_counts를 합쳐줍니다.

bureau_by_loan = bureau_balance_agg.merge(bureau_counts, right_index = True, left_on = 'SK_ID_BUREAU', how = 'outer')입니다.

여기서 중요한 것은 left 조인이 아니라 outer 조인입니다.

서로 가지고 있지 않는 컬럼이기 때문에 left를 하지 않습니다.

그래서 얘내를 이렇게 조인해주면 16 + 6인 22개의 컬럼이 됩니다!

그리고 각 값들은 SK_ID_BUREAU를 pk로 해서 들어가게 됩니다




이후 SK_ID_CURR을 기준으로 다시 한 번 agg_numeric을 해줍니다. SK_ID_BUREAU기준에서 SK_ID_CURR로 바꿔줍니다.


자 이 과정들이 조금 복잡하고 난해해 보일 수 있습니다.

그래서 이 커널 제작자도 그것을 의식했는지 다시 한 번 정리할 겸 진행하더라구요

함수나 이런 것은 지우지 않고, 여태것 사용했던 변수를 메모리에서 지우고 다시 불러와서 진행합니다.

import gc를 가져와서 gc.collect()를 해줍니다.




이렇게요! 이렇게 정리를 한 뒤 다시 위의 과정을 진행합니다.

차분히 다시 보시면 위 과정이 한 눈에 확 보이실 겁니다.



이렇게 진행하고

bureau_by_loan을 다시 합쳐줍니다.



이렇게 말이죠!

생각보다 되게 단순한 작업이었죠?(쓰고보니.. 할 때는 전혀 단순하지 않았습니다... 왜캐 어려운지 ㅠㅠ)

이거 이해하려고 진짜 3~4일 걸렸네요



그리고 저 데이터들을 하나씩 차례대로 train에 합쳐줍니다. SK_ID_CURR을 기준으로 합쳐주고 how = 'left'로 합쳐줍니다.



합쳐줍니다.

그리고 이제 missing value를 체크합니다.

이 missing value는 지난 포스팅에서 진행했던 것과 비슷합니다.



df.isnull().sum()과 100 * df.isnull().sum() / len(df)를 이용해서 null 데이터의 개수, percentage를 가져오고

concate으로 합쳐줍니다.

그리고 컬럼 이름을 재설정해주고, 정렬해주고 return해줍니다.


높은 것은 74%의 missing 값을 가지고 있네요.




여기 컬럼에서는 missing percentage가 90% 이상인 애들을 찾습니다.

흠... 왜 이렇게 했는지는 모르겠습니다.

저는 그냥 70% 넘으면 다 remove할 것 같은데 크흠..

그래서 test set까지 불러오고 train에서 했던 merge 과정을 거친 후 test에도 missing percent를 확인합니다.



여기에서는 68.7% 가 최대네요



그리고 90% 이상인 애들을 뽑아내서 drop시키는데, 사실상 drop할 column이 없습니다.

이제 target과 corrleation이 높은 컬럼을 확인해봅니다.



train.corr()을 가져와서 sort_values('TARGET', ascending=False)

pd.DataFrame(corrs['TARGET'].head(10))으로 확인해봅니다.



그리고 아까 만들어둔 kde_target을 이용해서 target 컬럼과 지정해놓은 컬럼과의 밀도 값을 확인해볼 수 있습니다.



이렇게요.


여기서 상관 관계가 너무 높은 애들을 제거합니다.

threshold를 0.8로 잡고 그 이상이 되는 애들은 제거하는 것이죠



corr.head를 보면 위 사진처럼 되어 있습니다. 옆에 index가 컬럼명으로 되어 있는데 저것들 중에서 0.8 이상이 도는 애들을 제거하는 것이죠



먼저 dict의 형태로 key, value 값으로 corr이 높은 애들을 넣어줍니다.

key는 컬럼명이고 value는 해당 컬럼과 corr값이 0.8 이상인 애들의 값들입니다.

above_threshold_vars[col] = list(corrs.index[corrs[col] > threshold])

근데 같은 컬럼끼리는 corr이 1이 되니까 같은 컬럼 값이 들어가게 됩니다. 이 중복되는 값들은 빼줘야 하겠죠



이렇게 지워줍니다.

col_seen은 들어가는 key 값 list입니다. 

cols_to_remove_pair는 딱히 의미가 없습니다.

cols_to_remove가 실제로 지우는 리스트입니다.

for문을 돌면서 key, value를 도는데요. for문마다 key를 cols_seen에 넣어줍니다.

다음 for문은 value에 대한 for문인데 만약 x가 key가 같다면 넘어갑니다. 그리고 그게 아니더라도 x가 cols_seen에 없으면 remove 대상에 추가합니다. corr의 특성 상 각 컬럼이 1:1 매칭이 되어 있기 때문에 이렇게 한 것 같습니다.

그리고 cols_to_remove에 또 중복되는 값들이 분명 있을 수 있기 때문에 set을 통해서 중복을 제거합니다.



train.drop(columns = cols_to_remove)를 이용해서 이제 컬럼을 전부 drop 시켜줍니다.

test도 마찬가지구요

드디어 이제 model 단계에 접근했습니다.


아이고 블로그에 쓰기도 힘드네요 ㅠㅠ.. 



sklearn.model_selection에서 KFold를 가져오고 metrics에서 roc_auc_curve를 가져옵니다.

이 model도 함수로 만들어서 진행합니다.

encoding에 따라서 ohe냐 le냐 아니냐에 따라 방법이 달라집니다.




train, test_ids에다가는 SK_ID_CURR을 각각 넣어줍니다.

lebel은 test에 없으니 train에서만 TARGET을 뽑아내고

불필요한 컬럼은 제거합니다.

이제 encoding에 따라서 바뀌게 되는데요. 사실 encoding == 'le'일 때는 거의 사용하지 않습니다.

(한 번도 안오더라구요)

그냥 ohe만 사용한다고 보시면 됩니다

ohe는 들어온 features를 get_dummies를 만들고 align 해주는 것으로 하고 넘어갑니다.



다음으로 feature를 뽑아내주고



k_fold = KFold(n_splits = n_folds, shuffle = False, random_state)를 가지고 KFold를 만들어줍니다.

그리고 이후에 특성 중요도를 그래프로 그릴 것이기 때문에 feature_importance_values를 np.zeros(len(feature_names))만큼 만들어줍니다.

test_predictions = np.zeros(test_features.shape[0])은 test를 예측했을 때 넣을 배열 값이고

out_of_fold = np.zeros(features.shape[0])은 validation을 예측했을 때 넣을 값입니다.

이제 for문을 돌면서 train_indices, valid_indices in k_fold.split(features):를 돌며 train, validation idx를 가져옵니다.



train, validation의 index를 뽑아서 넣은 뒤 model을 만들어줍니다.

그리고 훈련을 시키는데 eval_metric이 auc이고 eval_name에 valid, train으로 넣어줍니다.

그래서 eval_set에 valid와 train을 튜플로 구분해서 넣어주는 것이죠.



그리고 매 for문을 돌 떄마다 fit을 통해 훈련을 시키면서 나오는 feature_importance를 넣어주는데

fot문 마다 반복되니까 n_splits 개수만큼 나눠줍니다.

또한, test_prediction도 매 훈련마다 predict_proba(test_features, num_iteration = best_iteration)[:, 1]으로 예측을 하는데 마찬가지로

for문 마다 반복되니까 나눠줍니다.

valid는 kfold할 때마다 out_of_fold[valid_indices]에 들어가므로 나눠줄 필요가 없습니다.

그리고 valid, train에서 model.best_score_['valid']['auc]로 최고 좋은 성적을 뽑아낸 후 append로 넣어줍니다.

이 for문이 끝나면 submission 변수에 dataframe으로 test 값에 대한 예측을 넣어줍니다



그리고 특성 중요도도 dataframe으로 넣어주고

out_of_fold를 이용한 auc 점수를 구해서 valid_score에 마지막으로 넣어줍니다. 또한 train_score에도 train_score의 평균을 마지막으로 넣어주고 metric에 dataframe으로 만들어 준 뒤 return해줍니다.



위 함수는 feature_importace에 대한 특성 중요도를 그려주는 함수입니다.



이렇게 마무리를 해주고 

먼저 테스트로 오리지널 데이터를 가지고 단순히 훈련을 해봅니다



이렇게 넘겨줍니다. 잘 되는 것을 확인할 수 있습니다.



이렇게 특성 중요도의 그림도 뽑아낼 수 있습니다

original 데이터를 가지고 훈련하니까 0.745가 나왔다고 합니다.

이제 저희가 여태껏 만든 data set을 가지고 훈련을 해보죠

먼저 아까 corr에 관련해서 drop을 시키지 않은 데이터로 훈련을 해봅니다



0.758 정도 점수가 나왔다고 합니다.



그리고 drop 시킨 것으로 진행을 해보면




이것도 대략 0.753 정도 나왔다고 합니다.

어떠신가요? 꽤나 쉽지 않죠?

블로그에 올리는 것도 힘드네요.. 2시간 걸렸습니다 ㅠㅠ

근데 저도 올리면서 다시 한 번 복습할 수 있어서 참 좋지만

설명이 많이 부실하게 되네요.. 쓰다보면 지쳐서


이제 다음주는 제가 여태껏 했던 데이터 타이타닉, porto, home credit을 한 번더 복습하는 기간을 가지려고 합니다.

그래서 6주차까진 매주 올렸지만 다음주는 올리지 못할 것 같습니다.

올릴 수 있으면 올리겠습니다.


그리고 원래 계획은 다음 커널은 anomaly detection(이상 탐지)인 credict card 데이터로 진행하려고 했는데요

지금 고민이 많습니다. 일단 자연어 처리(nlp)쪽이 제가 관심이 많아서

바로 nlp로 넘어갈 지, anomaly detection을 한 번 하고 nlp를 할 지 고민이 많네요


아무튼 다음 글로 뵙겠습니다.


반응형
그리드형
Comments