빅데이터 분석기사 실기 4회차 작업형 기출 문제 풀이
빅데이터 분석기사 실기 기출 작업형 문제 4회차 풀이
개요¶
빅데이터 분석기사 실기 준비를 위한 4회차 시험 작업형 기출 문제 답 정리. 퇴근후딴짓님의 복원을 참고함(출처)
작업형 1유형¶
문제 1-1¶
age 컬럼의 3사분위수와 1사분위수의 차를 절대값으로 구하고, 소수점 버려서, 정수로 출력
import pandas as pd
df = pd.read_csv('data/4th/basic1.csv')
res = int(abs(df['age'].quantile(0.75) - df['age'].quantile(0.25)))
print(res)
문제 1-2¶
(loves 반응 + wows 반응) / (reactions 반응) 비율이 0.4보다 크고 0.5보다 작으면서, status_type이 'video'인 데이터의 개수
import pandas as pd
df = pd.read_csv('data/4th/fb.csv')
ratio = (df['loves'] + df['wows']) / df['reactions']
cond_1 = ratio > 0.4
cond_2 = ratio < 0.5
cond_3 = df['type'] == 'video'
res = len(df.loc[cond_1 & cond_2 & cond_3])
print(res)
문제 1-3¶
date_added가 2018년 1월 이면서 country가 United Kingdom 단독 제작인 데이터의 개수
import pandas as pd
df = pd.read_csv('data/4th/nf.csv')
df['date_added'] = pd.to_datetime(df['date_added'])
cond_1 = df['country'] == "United Kingdom"
cond_2 = df['date_added'].dt.year == 2018
cond_3 = df['date_added'].dt.month == 1
res = len(df.loc[cond_1 & cond_2 & cond_3])
print(res)
작업형 2유형¶
기존 고객 분류 자료를 바탕으로 신규 고객이 어떤 분류에 속할지 예측
- 예측할 값(y): "Segmentation" (1,2,3,4)
제출 형식
풀이¶
데이터 읽기
import pandas as pd
train = pd.read_csv('data/4th/train.csv')
test = pd.read_csv('data/4th/test.csv')
EDA
Standard Out
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6665 entries, 0 to 6664
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 6665 non-null int64
1 Gender 6665 non-null object
2 Ever_Married 6665 non-null object
3 Age 6665 non-null int64
4 Graduated 6665 non-null object
5 Profession 6665 non-null object
6 Work_Experience 6665 non-null float64
7 Spending_Score 6665 non-null object
8 Family_Size 6665 non-null float64
9 Var_1 6665 non-null object
10 Segmentation 6665 non-null int64
dtypes: float64(2), int64(3), object(6)
memory usage: 572.9+ KB
Standard Out
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2154 entries, 0 to 2153
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 2154 non-null int64
1 Gender 2154 non-null object
2 Ever_Married 2154 non-null object
3 Age 2154 non-null int64
4 Graduated 2154 non-null object
5 Profession 2154 non-null object
6 Work_Experience 2154 non-null float64
7 Spending_Score 2154 non-null object
8 Family_Size 2154 non-null float64
9 Var_1 2154 non-null object
dtypes: float64(2), int64(2), object(6)
memory usage: 168.4+ KB
문제의 종류를 확인하기 위해 target 칼럼인 Segmentation 칼럼에 존재하는 unique한 데이터를 확인하고 multiple classfication인 것을 확인한다.
테이블 병합
학습용 데이터와 검증용 데이터를 합쳐버리면 여러 가지 전처리를 한번에 처리하는 꼼수를 사용할 수 있다.
전처리를 위해 명목변수와 수치형변수를 분리해준다.
cols_obj = df.select_dtypes(include='object').columns
cols_num = [col for col in df.columns if col not in cols_obj and col not in ['ID', 'Segmentation']]
Index(['Gender', 'Ever_Married', 'Graduated', 'Profession', 'Spending_Score', 'Var_1'], dtype='object')
수치형 변수를 확인해보면, 명목변수가 One-Hot Encoding이 되어 있는 형태이거나, 정규화가 크게 필요하지는 않은 데이터인 것을 확인할 수 있다.
test 데이터 셋의 id 항목은 결과 제출 시에 재활용해야하니 따로 저장해둔다.
Label Encoding
범주가 두 개만 있거나 서열형 변수로 취급할만한 항목들은 Label Encoding으로 변환해주는게 좋다.
Gender=['Male' 'Female']
Ever_Married=['No' 'Yes']
Graduated=['No' 'Yes']
Profession=['Healthcare' 'Engineer' 'Lawyer' 'Artist' 'Doctor' 'Homemaker' 'Entertainment' 'Marketing' 'Executive']
Spending_Score=['Low' 'High' 'Average']
Var_1=['Cat_4' 'Cat_6' 'Cat_7' 'Cat_3' 'Cat_1' 'Cat_2' 'Cat_5']
반복문으로 처리하기 위해 범주가 두 개인 칼럼들의 목록을 만들고, 서열형으로 취급할 변수의 순서를 만들어준다.
cols_binary = ['Gender', 'Ever_Married', 'Graduated']
col_spend = ['Low', 'Average', 'High']
col_var = ['Cat_1', 'Cat_2', 'Cat_3', 'Cat_4', 'Cat_5', 'Cat_6', 'Cat_7']
Label Encoding을 진행해준다. 만약 데이터셋을 합쳐두지 않았다면 'Spending_Score', 'Var_1' 칼럼에 적용한 것처럼 Label Encoding을 적용할 각 칼럼 전용의 encoder를 선언해서 fit 정보를 저장해줘야 재활용할 수 있다.
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
for col in cols_binary:
df[col] = encoder.fit_transform(df[col])
encoder_spend = LabelEncoder()
encoder_spend.fit(col_spend)
df['Spending_Score'] = encoder_spend.transform(df['Spending_Score'])
encoder_var = LabelEncoder()
encoder_var.fit(col_var)
df['Var_1'] = encoder_var.transform(df['Var_1'])
One-Hot Encoding
남은 범주형 변수 칼럼에 대한 One-Hot Encoding을 진행한다. 위에서 label encoding을 진행하여 현재 상태에서는 모든 object 타입 칼럼을 대상으로 one-hot encoding을 해주면 되기 때문에 columns 파라미터를 사용하지 않아도 되지만 연습 겸 적용해본다. columns 파라미터에 인자를 입력해주면 해당 칼럼들에 대해서만 One-Hot Encoding을 진행한다.
Standard Out
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8819 entries, 0 to 2153
Data columns (total 19 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 8819 non-null int64
1 Gender 8819 non-null int32
2 Ever_Married 8819 non-null int32
3 Age 8819 non-null int64
4 Graduated 8819 non-null int32
5 Work_Experience 8819 non-null float64
6 Spending_Score 8819 non-null int32
7 Family_Size 8819 non-null float64
8 Var_1 8819 non-null int32
9 Segmentation 6665 non-null float64
10 Profession_Artist 8819 non-null uint8
11 Profession_Doctor 8819 non-null uint8
12 Profession_Engineer 8819 non-null uint8
13 Profession_Entertainment 8819 non-null uint8
14 Profession_Executive 8819 non-null uint8
15 Profession_Healthcare 8819 non-null uint8
16 Profession_Homemaker 8819 non-null uint8
17 Profession_Lawyer 8819 non-null uint8
18 Profession_Marketing 8819 non-null uint8
dtypes: float64(3), int32(5), int64(2), uint8(9)
memory usage: 663.1 KB
테이블 분리
전처리를 쉽게 처리하기 위해 합쳐두었던 train set 데이터와 test set 데이터를 다시 분리하고, 모델로 test set 데이터를 inference하는데 불필요한 칼럼을 제거해준다.
train = df.iloc[:train.shape[0]]
test = df.iloc[train.shape[0]:]
test.drop(columns=['Segmentation', 'ID'], inplace=True)
독립변수/종속변수 분리
모델의 학습을 위해 독립변수와 종속변수를 분리해준다.
import numpy as np
endog = np.array(train['Segmentation']).reshape(-1, 1)
exog = train.drop(columns=['Segmentation', 'ID'])
모델 생성/학습/평가
다중 분류를 예측하는 문제이므로, KNN 알고리즘을 사용하기로 한다. 종속변수는 사실 그대로 넣어도 자동으로 적용 되는데, scikit-learn에서 (1, -1) 형태로 바꿔달라는 경고가 떠서 flatten 함수를 통해 1차원으로 변환시켜줬다.
그리고 모델의 교차 검증(cross validation)을 위해 cross_val_score API를 사용했는데, 공식 문서를 보면 cv 옵션이 정수일 경우 자동으로 StratifiedKFold를 적용해준다고 한다.
KNN 알고리즘은 예측할 데이터의 위치 주변의 다른 데이터들을 통해 어느 집단에 속할지를 예측하는 알고리즘인데, n_neighbors는 가장 가까운 몇 개의 데이터를 볼 지 결정하는 옵션이다. 하이퍼파라미터 튜닝을 위해 n_neighbors의 값을 적절히 변화를 주면서 확인해본다.
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier as KNN
endog = endog.flatten()
score = [{'i': i, 'score_mean': cross_val_score(KNN(n_neighbors=i), X=exog, y=endog, cv=5).mean()} for i in range(10, 500, 10)]
score = pd.DataFrame(score).sort_values(by='score_mean', ascending=False)
print(score.head())
Note
공식 문서에 따르면 cross_val_score API는 scoring 파라미터를 입력하지 않으면 분류 문제일 경우 기본값으로 accuracy를 사용한다고 한다.
결과치가 큰 차이가 있지는 않지만, 어쨌든 가장 평균 점수가 높게 나온 n_neighbors 값으로 모델을 학습시켜준다.
학습된 모델을 활용한 확률 예측
문제에서 어떤 분류에 속할지를 예측하라고 했으므로 predict 메서드를 이용해서 test set 데이터에 적용해준다.
참고로 각 분류에 속할 확률을 예측하고 싶다면 아래와 같이 predict_proba 메서드를 적용하면 된다.
[[0.36666667 0.3 0.13333333 0.2 ]
[0.23333333 0.3 0.4 0.06666667]
[0.1 0.36666667 0.53333333 0. ]
...
[0.26666667 0.43333333 0.06666667 0.23333333]
[0.16666667 0.33333333 0.43333333 0.06666667]
[0.26666667 0.2 0.3 0.23333333]]
저장해둔 id와 출력된 predict의 개수가 처음 데이터를 가져온 test set 데이터의 개수와 동일한 것을 확인해준다.
결과 데이터 생성
id와 예측 결과를 이용해서 제출용 결과를 만들고, 정상적으로 만들었는지 확인해준다. 제출 예시에 예측 결과를 정수형 자료로 제출하도록 제시되었기 때문에 실수형 데이터로 저장된 결과를 정수형으로 바꿔주어야 한다.
result = pd.DataFrame({'ID': id, 'Segmentation': predict})
result['Segmentation'] = result['Segmentation'].astype(int)
print(result.head())
제출용 파일 저장
답안 제출용 파일을 저장해준다. 제시된 제출 결과와 동일한 양식으로 만들어주어야 한다.
통합 코드 및 주석¶
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier as KNN
# data load
train = pd.read_csv('data/4th/train.csv')
test = pd.read_csv('data/4th/test.csv')
# EDA
print(train.info())
print(test.info())
print(train['Segmentation'].unique())
# table concat for preprocessing
df = pd.concat([train, test])
cols_obj = df.select_dtypes(include='object').columns
cols_num = [col for col in df.columns if col not in cols_obj and col not in ['ID', 'Segmentation']]
print(cols_obj)
print(cols_num)
# save id of test set data
id = test.iloc[:, 0]
# label encoding
[print(f'{col}={df[col].unique()}') for col in cols_obj]
cols_binary = ['Gender', 'Ever_Married', 'Graduated']
col_spend = ['Low', 'Average', 'High']
col_var = ['Cat_1', 'Cat_2', 'Cat_3', 'Cat_4', 'Cat_5', 'Cat_6', 'Cat_7']
encoder = LabelEncoder()
for col in cols_binary:
df[col] = encoder.fit_transform(df[col])
encoder_spend = LabelEncoder()
encoder_spend.fit(col_spend)
df['Spending_Score'] = encoder_spend.transform(df['Spending_Score'])
encoder_var = LabelEncoder()
encoder_var.fit(col_var)
df['Var_1'] = encoder_var.transform(df['Var_1'])
# one-hot encoding
df = pd.get_dummies(df)
# train/test split
train = df.iloc[:train.shape[0]]
test = df.iloc[train.shape[0]:]
test.drop(columns=['Segmentation', 'ID'], inplace=True)
# separate dependent/independent variable from train set
endog = np.array(train['Segmentation']).reshape(-1, 1)
exog = train.drop(columns=['Segmentation', 'ID'])
# calculate cross validation score for hyperparameter tuning
endog = endog.flatten()
score = [{'i': i, 'score_mean': cross_val_score(KNN(n_neighbors=i), X=exog, y=endog, cv=5).mean()} for i in range(10, 500, 10)]
score = pd.DataFrame(score).sort_values(by='score_mean', ascending=False)
print(score.head())
# load model and fit model
model = KNN(n_neighbors=score.iloc[0, 0])
model.fit(X=exog, y=endog)
print(model.classes_)
# inference test set with fitted model
predict = model.predict(test)
print(predict)
# make result table to make csv file
result = pd.DataFrame({'ID': id, 'Segmentation': predict})
result['Segmentation'] = result['Segmentation'].astype(int)
print(result.head())
# save csv file
result.to_csv('result.csv', index=False)