제2유형 은 머신러닝 에 대한 문제이며, 1문제이지만 배점이 40점으로 가장 높습니다.
주어진 라이브러리와 데이터를 불러온 뒤, 데이터 전처리를 거쳐 모델을 학습시키고 평가한 결과를 CSV 코드 형식으로 제출하는 유형입니다.
문제의 난이도가 다소 높은 유형이지만, 전반적인 풀이 방식 이 어느 정도 정형화 되어 있어,
풀이 방식을 암기 하여 충분히 맞출 수 있습니다.
자, 그럼 문제를 풀이 단계별로 확인해 볼까요?
구분
문항 수
답안 제출
점수
제1유형
3문항
전처리 결과 제출
30점
제2유형
1문항
CSV 코드 제출
40점
제3유형
2문항
답안 제출
30점
합계
6문항
-
100점
[0단계] 머신러닝 - 지도학습(분류, 회귀) / 비지도학습(군집, 차원축소) / 강화학습 ① 문제 정의 및 데이터 불러오기: 분류/회귀, 예측 컬럼/결과, 평가방식, 최종파일
Train 학습.검증용, Test 평가(예측)용 ② 탐색적 데이터 분석(EDA): 데이터 크기, 타입, 분류 비율 등 데이터 확인 ③ 데이터 전처리: 결측치 처리, 이상치 처리 ④ 피처 엔지니어링: 수치형(min-max 스케일링, 표준화), 범주형(라벨 인코딩, 원핫 인코딩) ⑤ 모델(훈련, 평가 등): 분류 모델, 회귀 모델 ⑥ 예측: Test 데이터로 평가해서 예측모델 제출
[1단계] 라이브러리 및 데이터 불러오기
1) 데이터 확인
import pandas as pd
X_train = pd.read_csv( " ~ " )
y_train = pd.read_csv(" ~ " )
X_test = pd.read_csv( " ~ " )
X_train.head() # 상위 5개 추출
X_train.tail() # 하위 5개 추출
X_train.sample( 3 ) # 랜덤 3개 추출
X_train.shape # 행열 개수 추출
X_train.info() # 데이터 타입 확인
X_train.describe() # 수치형 데이터 통계값 확인
X_train.describe(include= 'object' ) # 범주형 데이터 통계값 확인
X_train.isnull(). sum () # 결측치 확인
y_train[ 'income' ].value_counts() # 라벨별 개수 확인
X_train.corr(numeric_only= True ) # 상관관계
train = pd.read_csv(" ~ " )
test = pd.read_csv( " ~ " )
train.shape, test.shape # 데이터 크기 확인
top = train [ 'income' ] == ">50K" # 상위 소득
bottom = train [ 'income' ] == "<=50K" # 하위 소득
female = train [ 'sex' ] == "Female"
male = train [ 'sex' ] == "Male"
print ( len ( train [ male ] ) , len ( train [ female ] ) ) # 남성과 여성의 수
print ( len ( train [ male&top ] ) , len ( train [ male&bottom ] ) ) # 남성 중 상위/하위소득 인원 수
print ( len ( train [ male&top ] ) / len ( train [ male ] ) , len ( train [ male&bottom ] ) / len ( train [ male ] ) ) # 남성 중 상위/하위소득 비율
19578 9726
5976 13602
0.3052405761569108 0.6947594238430892
2) 데이터 합치기
print ( train.shape, X_train.shape, y_train.shape )
df = pd.concat ( [ X_train, y_train [ 'income' ] ] , axis= 1 ) # income 컬럼만 합치기
df.shape
(29304, 16) (29304, 15) (29304, 2)
3) 데이터 분리하기
X_tr = train.iloc [ :,: -1 ] .copy ( ) # 마지막 컬럼을 분리
y_tr = train.iloc [ :, [ 0 , -1 ] ] .copy ( ) # 첫번째와 마지막 컬럼만 추출
X_tr.shape, y_tr.shape
((29304, 15), (29304, 2))
[2단계] 데이터 전처리
test 데이터는 절대 삭제하면 안됨
train 데이터는 데이터가 많을 경우, 소수 데이터를 삭제해도 무방함
test/train 컬럼은 삭제/추가 가능 (단, 컬럼수/컬럼명은 일치해야 함)
1) 중복값/결측치 처리
X_train[ 'workclass' ].value_counts() # workclass 값 분포
df = X_train.dropna() # 결측치 있는 데이터(행) 전체 삭제
df = X_train.dropna(subset=[ 'workclass' , 'native.country' ]) # 특정 컬럼의 결측치 데이터(행) 삭제
df = X_train.dropna(axis= 1 ) # 결측치가 있는 컬럼 모두 삭제 (axis=1은 컬럼)
df = X_train.drop([ 'workclass' ], axis= 1 ) # 특정 컬럼 삭제
df = X_train.drop_duplicates() # 중복값 제거
df = X_train.drop_duplicates(subset=[ 'workclass' , 'native.country' ], keep= 'last' ) # 특정 컬럼만 중복값 제거 (last는 뒤에 값을 살림)
X_train[ 'workclass' ] = X_train[ 'workclass' ].fillna(X_train[ 'workclass' ].mode()[ 0 ]) # 최빈값으로 결측치 채우기
X_train[ 'occupation' ] = X_train[ 'occupation' ].fillna( 'X' ) # 특정값으로 결측치 채우기
X_train[ 'age' ] = X_train[ 'age' ].fillna(int (X_train[ 'age' ].mean()) ) # 평균값으로 결측치 채우기
2) 이상치 제외하고 저장
print ( X_train.shape )
X_train = X_train [ X_train [ 'age' ] > 0 ] # 나이가 양수인 경우만 저장
print ( X_train.shape )
3) 컬럼별 이상치 개수 확인
cols = [ 'age' , 'fnlwgt' , 'education.num' , 'capital.gain' , 'capital.loss' , 'hours.per.week' ]
for col in cols:
Q1 = X_train [ col ] .quantile ( 0.25 )
Q3 = X_train [ col ] .quantile ( 0.75 )
IQR = Q3 - Q1
min_iqr = Q1 - ( 1.5 * IQR )
max_iqr = Q3 + ( 1.5 * IQR )
cnt = sum ( ( X_train [ col ] < min_iqr ) | ( X_train [ col ] > max_iqr ) )
print ( f ' { col } 의 이상치: { cnt } 개 입니다.' )
age의 이상치: 121개 입니다.
fnlwgt의 이상치: 892개 입니다.
education.num의 이상치: 1077개 입니다.
capital.gain의 이상치: 2459개 입니다.
capital.loss의 이상치: 1359개 입니다.
hours.per.week의 이상치: 8104개 입니다.
[3단계] 피처 엔지니어링
1) Min-Max Scaler (모든 값이 0~1)
obj_train = X_train.select_dtypes(include= 'object' ).copy() # object 유형만 저장
num_train = X_train.select_dtypes(exclude= 'object' ).copy() # object 외 유형 저장
obj_test = X_test.select_dtypes(include= 'object' ).copy()
num_test = X_test.select_dtypes(exclude= 'object' ).copy()
cols = [ 'age' , 'fnlwgt' , 'education.num' , 'capital.gain' , 'capital.loss' , 'hours.per.week' ] # num 유형만 저장
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler ( )
print ( num_train.head ( 2 ) )
num_train [ cols ] = scaler.fit_transform ( num_train [ cols ] ) # 학습과 변환 작업 진행
num_test [ cols ] = scaler.transform ( num_test [ cols ] ) # 변환 작업만 진행
print ( num_train.head ( 2 ) ) # Min-Max Sacler 적용된 데이터
id age fnlwgt education.num capital.gain capital.loss \
0 3331 34.0 177331 10 4386 0
1 19749 58.0 290661 9 0 0
hours.per.week
0 40.0
1 40.0
id age fnlwgt education.num capital.gain capital.loss \
0 3331 0.5625 0.112092 0.600000 0.04386 0.0
1 19749 0.7500 0.189060 0.533333 0.00000 0.0
hours.per.week
0 0.397959
1 0.397959
2) 표준화 (Z-score 정규화, 평균 0 표준편차 1인 표준정규분포로 변환)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler ( )
print ( num_train.head ( 2 ) )
num_train [ cols ] = scaler.fit_transform ( num_train [ cols ] ) # 학습과 변환 작업 진행
num_test [ cols ] = scaler.transform ( num_test [ cols ] ) # 변환 작업만 진행
print ( num_train.head ( 2 ) ) # 표준정규분포로 변환된 데이터
id age fnlwgt education.num capital.gain capital.loss \
0 3331 0.5625 0.112092 0.600000 0.04386 0.0
1 19749 0.7500 0.189060 0.533333 0.00000 0.0
hours.per.week
0 0.397959
1 0.397959
id age fnlwgt education.num capital.gain capital.loss \
0 3331 -0.334145 -0.117678 -0.031447 0.440284 -0.216045
1 19749 1.427220 0.956304 -0.420434 -0.146290 -0.216045
hours.per.week
0 -0.035227
1 -0.035227
3) 로버스트 스케일링 (중앙값과 사분위값 활용, 이상치 영향 최소화)
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler ( )
print ( num_train.head ( 2 ) )
num_train [ cols ] = scaler.fit_transform ( num_train [ cols ] ) # 학습과 변환 작업 진행
num_test [ cols ] = scaler.transform ( num_test [ cols ] ) # 변환 작업만 진행
print ( num_train.head ( 2 ) ) # 로버스트 스케일링 적용된 데이터
id age fnlwgt education.num capital.gain capital.loss \
0 3331 -0.334145 -0.117678 -0.031447 0.440284 -0.216045
1 19749 1.427220 0.956304 -0.420434 -0.146290 -0.216045
hours.per.week
0 -0.035227
1 -0.035227
id age fnlwgt education.num capital.gain capital.loss \
0 3331 -0.15 -0.008765 0.000000 0.586575 0.0
1 19749 1.05 0.941358 -0.333333 0.000000 0.0
hours.per.week
0 0.0
1 0.0
4) 로그 변환
import numpy as np
print (X_train[ 'fnlwgt' ][: 3 ])
print ( np.log1p(X_train[ 'fnlwgt' ])[: 3 ]) # log1p 적용
print ( np.exp(np.log1p(X_train[ 'fnlwgt' ]))) # 다시 원래값으로 변환 (1~2정도 차이는 있음)
0 177331
1 290661
2 125933
Name: fnlwgt, dtype: int64
0 12.085779
1 12.579916
2 11.743513
Name: fnlwgt, dtype: float64
0 177332.0
1 290662.0
2 125934.0
3 100314.0
4 195662.0
...
29299 47169.0
29300 231794.0
29301 201436.0
29302 137723.0
29303 406979.0
Name: fnlwgt, Length: 29304, dtype: float64
5-1) 라벨 인코딩 - 값에 라벨을 붙여서 데이터를 구성 (예, 사과=1, 배=2)
cols = [ 'workclass' , 'education' , 'marital.status' , 'occupation' , 'relationship' , 'race' , 'sex' , 'native.country' ] # obj 유형만 저장
from sklearn.preprocessing import LabelEncoder
for col in cols:
le = LabelEncoder()
obj_train[col] = le.fit_transform(obj_train[col])
obj_test[col] = le.transform(obj_test[col])
obj_train.head()
5-2) 원핫 인코딩 - 값을 컬럼으로 생성하여, 데이터는 0(비해당) or 1(해당)로 구성 (예, 사과 컬럼=0, 1, 0, 0)
obj_train.head()
obj_train = pd.get_dummies(obj_train[cols])
obj_test = pd.get_dummies(obj_test[cols])
obj_train.head()
6) 데이터 합치기
# (데이터 컬럼 수가 다른 경우) train, test 합쳐서 인코딩 후 분리하기
cols = list ( X_train.columns [ X_train.dtypes == object ] ) # 문자형 데이터만 저장
print ( X_train.shape, X_test.shape )
all_df = pd.concat ( [ X_train, X_test ] ) # 두 데이터를 위 아래로 합침 (asis=0, 기본값)
all_df = pd.get_dummies ( all_df [ cols ] ) # 원핫 인코딩
line = int ( X_train.shape [ 0 ] ) # train, test 데이터가 연결된 지점 확인
print ( line )
X_train = all_df.iloc [ :line,: ] .copy ( ) # train 데이터만 분리
X_test = all_df.iloc [ line:,: ] .copy ( ) # test 데이터만 분리
print ( X_train.shape, X_test.shape )
(29304, 15) (3257, 15)
29304
(29304, 99) (3257, 99)
[4단계] 모델링 및 평가 (분류)
0) 데이터 전처리
cols =[ 'age' , 'fnlwgt' , 'education.num' , 'capital.gain' , 'capital.loss' , 'hours.per.week' ] # 수치형 데이터 저장
print ( X_train[cols].isnull(). sum () ) # 결측치 확인
X_train = X_train.fillna( 0 ) # 결측치 0으로 보정
X_test = X_test.fillna( 0 ) # 결측치 0으로 보정
print ( X_train[cols].isnull(). sum () ) # 결측치 확인
y = (y_train[ 'income' ] == '>50K' ).astype( int ) # <=50k →0, >50K →1로 변환/ True,False로 만든 후 정수로 변환
1) 랜덤포레스트
from sklearn.ensemble import RandomForestClassifier # 모델 불러오기
rf = RandomForestClassifier()
rf.fit(X_train[cols], y) # 훈련하기
pred = rf.predict(X_test[cols]) # 예측하기
submit = pd.DataFrame(
{
'id' :X_test[ 'id' ],
'income' :pred
}
)
submit.to_csv( "11111.csv" , index= False ) # csv 생성 (index=False, 컬럼명이 있다는 의미)
2) 정확도 모델
from sklearn.metrics import accuracy_score
ans = ( y_test [ 'income' ] == '>50K' ) .astype ( int )
accuracy_score ( ans, pred ) # 80%의 정확도를 보임
3) 학습용/검증용 데이터 분리
from sklearn.model_selection import train_test_split
y = ( y_train [ 'income' ] == '>50K' ) .astype ( int )
X_tr, X_val, y_tr, y_val = train_test_split ( X_train, y, test_size= 0.1 , random_state= 2022 )
X_tr.shape, X_val.shape, y_tr.shape, y_val.shape
((26373, 15), (2931, 15), (26373,), (2931,))
4) 의사결정나무
from sklearn.metrics import roc_auc_score
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier ( )
dt.fit ( X_tr [ cols ] , y_tr )
pred = dt.predict_proba ( X_val [ cols ] ) # predict_proba 확률값을 예측
roc_auc_score ( y_val, pred [ :, 1 ] ) # >50K의 예측 정확도
5) 랜덤포레스트
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier ( )
rf.fit ( X_tr [ cols ] , y_tr )
pred = rf.predict_proba ( X_val [ cols ] )
roc_auc_score ( y_val, pred [ :, 1 ] )
6) XGBoost
from xgboost import XGBClassifier
xgb = XGBClassifier ( )
xgb.fit ( X_tr [ cols ] , y_tr )
pred = xgb.predict_proba ( X_val [ cols ] )
roc_auc_score ( y_val, pred [ :, 1 ] )
7) 평가용 데이터 >50k일 확률값을 예측한 값을 csv로 저장
# 평가 데이터로 >50k일 확률값을 예측한 값을 csv로 저장
from sklearn.metrics import roc_auc_score
roc_auc_score(y_val, pred[:, 1 ])
pred = xgb.predict_proba(X_test[cols])
submit = pd.DataFrame(
{
'id' :X_test[ 'id' ],
'income' :pred[:, 1 ]
}
)
submit.to_csv( "22222.csv" , index= False )
[5단계] 모델링 및 평가 (회귀)
0) 데이터 전처리
# 원핫 인코딩
cols = train.select_dtypes(include= 'object' ).columns
train = pd.get_dummies(train, columns=cols)
test = pd.get_dummies(test, columns=cols)
from sklearn.model_selection import train_test_split
X_tr, X_val, y_tr, y_val = train_test_split(train.drop( 'charges' ,axis= 1 ), train[ 'charges' ], test_size= 0.15 , random_state = 2022 )
X_tr.shape, X_val.shape, y_tr.shape, y_val.shape
# 평가 수식
from sklearn.metrics import mean_squared_error
import numpy as np
def rmse ( y_test , pred ): # 실제값, 예측값
return np.sqrt(mean_squared_error(y_test, pred))
1) LinearRegression → 결과: 5855
from sklearn.linear_model import LinearRegression
model = LinearRegression ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
2) StandardScaler / LinearRegression → 결과: 5855 (차이가 없음)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler ( )
cols = [ 'age' , 'bmi' ]
train [ cols ] = scaler.fit_transform ( train [ cols ] )
test [ cols ] = scaler.transform ( test [ cols ] )
from sklearn.linear_model import LinearRegression
model = LinearRegression ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
3) RandomForestReggressor → 결과: 4671
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
4) StandardScaler / RandomForestReggressor → 결과: 4732 (안 좋아짐)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler ( )
cols = [ 'age' , 'bmi' ]
train [ cols ] = scaler.fit_transform ( train [ cols ] )
test [ cols ] = scaler.transform ( test [ cols ] )
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
5) MinMaxScaler / RandomForestReggressor → 결과: 4672 (안 좋아짐)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler ( )
cols = [ 'age' , 'bmi' ]
train [ cols ] = scaler.fit_transform ( train [ cols ] )
test [ cols ] = scaler.transform ( test [ cols ] )
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
6) MinMaxScaler & bmi만 적용/ RandomForestReggressor → 결과: 4712 (안 좋아짐)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler ( )
cols = [ 'bmi' ]
train [ cols ] = scaler.fit_transform ( train [ cols ] )
test [ cols ] = scaler.transform ( test [ cols ] )
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
7) log 변환 / RandomForestReggressor → 결과: 4605 (가장 좋음)
# log변환
import numpy as np
train [ 'charges' ] = np.log1p ( train [ 'charges' ] )
# 검증 데이터 분리
from sklearn.model_selection import train_test_split
X_tr, X_val, y_tr, y_val = train_test_split ( train.drop ( 'charges' ,axis= 1 ) , train [ 'charges' ] , test_size= 0.15 , random_state = 2022 )
X_tr.shape, X_val.shape, y_tr.shape, y_val.shape
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor ( )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( np.exp ( y_val ) , np.exp ( pred ) )
8) XGBRegressor → 결과: 4903 (가장 안 좋음)
from xgboost import XGBRegressor
model = XGBRegressor ( objective= 'reg:squarederror' )
model.fit ( X_tr, y_tr )
pred = model.predict ( X_val )
rmse ( y_val, pred )
9) csv 파일 생성
pred = model.predict(test) # 데이터 예측
submit = pd.DataFrame(
{
'id' :test[ 'id' ],
'charges' :pred
}
)
submit.to_csv( '33333.csv' , index= False ) # csv파일 생성