IT/Analysis

[Kaggle] 케글 필사 - Porto Seguro’s safe driver prediction

무지개는 환상 2024. 2. 23. 00:02

<서론>

이번에는 새로운 케글필사를 진행하였다.

Porto Seguro 보험회사에서 진행한 보험 청구 예측 대회이다.

타겟 열의 보험 계약자에 대한 보험 청구가 되었는지 여부를 맞추는 대회였다.

https://www.kaggle.com/c/porto-seguro-safe-driver-prediction

 

Porto Seguro’s Safe Driver Prediction | Kaggle

 

www.kaggle.com

 

이번 대회는 지난번 타이타닉과 좀 다른 점이 있었다.

1. Null 값은 -1로 표시되었다.

2. 컬럼명이 무엇이다라는걸 지칭하는것이 아닌

값이 이진 특성이라면 bin, 범주형 특성이라면 cat 같은 접미사로만 표현되었다.

생각해보니 제출자 입장에서는 회사 보안을 위해 테이블을 정확히 공개할 수는 없기 때문에

충분히 그렇게 제출 할 수 도 있다는 생각을 하였다.

데이터 설명을 읽고 위와 같은 특성을 가진 문제는 어떻게 접근하는지 궁금하였다.

 

 

<느낀 부분>

1. 메타 데이터 이용

우선 메타데이터란 데이터에 대한 구조화된 데이터로 다른 데이터를 설명해 주는 데이터이다.

해당 필사에서 위에 말한것 같이 컬럼명을 알려준 것이 아닌 데이터의 특성을 통한 접미사만 알려주었다.

그래서 bin, cat, int, float등의 데이터의 특성을 기준하여 Dataframe에 메타정보를 저장한 후 진행하였다.

 

data= []
for f in train.columns:
  # Defining the role
  if f == 'target':
    role = 'target'
  elif f == 'id':
    role = 'id'
  else:
    role = 'input'

  # Defining the level
  if 'bin' in f or f == 'target':
    level = 'binary'
  elif 'cat' in f or f == 'id':
    level = 'nominal'
  elif train[f].dtype == float:
    level = 'interval'
  elif train[f].dtype == int:
    level = 'ordinal'

  # Initialize keep to True for all variables except for id
  keep = True
  if f == 'id':
    keep = False

  #Defining the data type
  dtype = train[f].dtype

  # Creating a Dic that contains all the metadata for the variable
  f_dict = {
      'varname': f,
      'role': role,
      'level': level,
      'keep': keep,
      'dtype': dtype
  }
  data.append(f_dict)

meta = pd.DataFrame(data, columns=['varname', 'role', 'level', 'keep', 'dtype'])
meta.set_index('varname', inplace=True)

 

 

2. 오버샘플링 or 언더샘플링

target 컬럼의 평균값을 구하니 약 0.03645이었다.

이 뜻은 3.645% 정도의 운전자만 보험 청구를 한다는 뜻이다. (target 컬럼은 1,0의 이진 컬럼)

이럴때는 두 타겟값의 비중을 어느정도 맞추기 위해

target = 1 값을 오버샘플링 하거나 target = 0 값을 언더 샘플링 해야 한다고 설명하였다.

( 대부분의 필사에서 이미 데이터가 많아 0을 언더 샘플링 하였다.

desired_apriori=0.10

# Get the indices per target value
idx_0 = train[train.target == 0].index
idx_1 = train[train.target == 1].index

# Get original number of records per target value
nb_0 = len(train.loc[idx_0])
nb_1 = len(train.loc[idx_1])

# Calculate the undersampling rate and resulting number of records with target=0
undersampling_rate = ((1-desired_apriori) * nb_1) / (nb_0 * desired_apriori)
undersampled_nb_0 = int(undersampling_rate * nb_0)
print('Rate to undersample records with target=0: {}'.format(undersampling_rate))
print('Number of records with target=0 after undersampling: {}'.format(undersampled_nb_0))

# Randomly select records with target=0 to get at the desired a priori
undersampled_idx = shuffle(idx_0, random_state=37, n_samples = undersampled_nb_0)

# Construct list with remaining indices
idx_list = list(undersampled_idx) + list(idx_1)

# Return undersample data frame
train = train.loc[idx_list].reset_index(drop=True)

 

3. 결측값(-1) 처리

위와 비슷한 개념인데 각 데이터의 성질에 따라 결측값에 대해 처리하였다.

1) 너무 많은 결측값이 있는 변수라면 삭제

2) 연속형 변수라면 평균으로 대체

3) 서수형 변수라면 최빈값으로 대체

# Dropping the variables with too many missing values
vars_to_drop = ['ps_car_03_cat', 'ps_car_05_cat']
train.drop(vars_to_drop, inplace = True, axis=1)
meta.loc[(vars_to_drop), 'keep'] = False # Updating the meta

#Imputing with the mean or mode
mean_imp = SimpleImputer(missing_values= -1, strategy='mean')
mode_imp = SimpleImputer(missing_values= -1, strategy= 'most_frequent')
train['ps_reg_03'] = mean_imp.fit_transform(train[['ps_reg_03']]).ravel()
train['ps_car_12'] = mean_imp.fit_transform(train[['ps_car_12']]).ravel()
train['ps_car_14'] = mean_imp.fit_transform(train[['ps_car_14']]).ravel()
train['ps_car_11'] = mean_imp.fit_transform(train[['ps_car_11']]).ravel()

 

4. 강한 상관관계를 갖는 변수별 회귀 분석

변수별 상관관계 함수를 그린 후 상관관계가 높은 변수끼리 회귀 분석을 진행해보니

선형 관계가 있는 것을 발견하게 되었다.

변수의 차원을 줄이기 위해 PCA를 수행할 수도 있었으나 해당 필사에서는 실행하지 않았다.

 

5. 변수 선정

타이타닉과는 다르게 변수의 값 자체가 적은 특성들은 아예 삭제하였다.

분류를 함에 있어 큰 의미를 갖기 보다는 오차를 주기 때문에 실행한것 같다. (VarianceThreshold 사용)

selector = VarianceThreshold(threshold=.01)
selector.fit(train.drop(['id', 'target'], axis = 1)) # Fit to train without id and target variables

f = np.vectorize(lambda x : not x) # Function to toggle boolean array elemtns

v = train.drop(['id', 'target'], axis = 1).columns[f(selector.get_support())]
print('{} variables have too low variance.'.format(len(v)))
print('These variables are {}'.format(list(v)))

 

또한 랜덤 포레스트를 기반으로 중요하지 않은 변수의 수를 선정하였다. (Sklearn SelectFromModel 사용)

X_train = train.drop(['id', 'target'], axis = 1)
y_train = train['target']

feat_labels = X_train.columns

rf = RandomForestClassifier(n_estimators = 1000, random_state = 0, n_jobs=-1)
rf.fit(X_train, y_train)
importances = rf.feature_importances_

indices = np.argsort(rf.feature_importances_)[::-1]

for f in range(X_train.shape[1]):
  print("%2d) %-*s %f" % (f + 1, 30, feat_labels[indices[f]], importances[indices[f]]))

 

 

<후기>

이제 어느정도 시각화 라이브러리는 크게 어색하지 않았다.

하지만 메타데이터를 이용한 접근이 처음이라 무척 당황스러웠다.

그렇지만 앞으로도 메타데이터를 이용한 접근은 더 많이 나올 것 같아 공부해야겠다!

또한 단순히 패키지를 불러와 이용하는 것이 아닌

좀 더 EDA나 피처 엔지니어링 하는 단계에서 패키지 문서들도 직접 보고 사용해야겠다.

 

작성자의 설명들을 보면서 가끔 머신러닝 or 딥러닝 관련 단어들이 튀어나오는데 듣고, 공부도 해봤지만

헷갈리는 부분들을 찾아보았다. (Ex. 이 분석이 뭐였고, 왜하는 거였지??)

한가지 지식에 대해 중복적 탐색을 줄이기 위한 방안을 좀 더 고려하며 블로그를 작성해야겠다..

 

 

나의 필사

https://colab.research.google.com/drive/1YP_WAFbEUqPABk4f3ui4EI77TywWYi15?usp=sharing

 

Porto Seguro’s safe driver prediction

Colaboratory notebook

colab.research.google.com