대체로 머신러닝 모델의 입력값으로 결측값을 사용할 수 없음.
1) 결측값 존재하는 샘플 삭제 -> 결측값이 있는 row 전체를 삭제
2) 결측값이 많이 존재하는 변수 삭제 -> 결측값이 많다고 판단되는 column 전체를 삭제
3) 결측값을 다른 값으로 대체 -> 결측값이 많지 않을 때 해당 column의 평균값(제일 많이 사용)이나 중앙값, 머신러닝으로 예측한 값 등으로 대체하여 사용
import pandas as pd
from elice_utils import EliceUtils
elice_utils = EliceUtils()
# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
# 변수 별 데이터 수를 확인하여 결측 값이 어디에 많은지 확인합니다.
print(titanic.info(),'\n')
"""
1. Cabin 변수를 제거합니다.
"""
titanic_1 = titanic.drop(columns=['Cabin'])
# Cabin 변수를 제거 후 결측값이 어디에 남아 있는지 확인합니다.
print('Cabin 변수 제거')
print(titanic_1.info(),'\n')
"""
2. 결측값이 존재하는 샘플 제거합니다.
"""
titanic_2 = titanic_1.dropna()
# 결측값이 존재하는지 확인합니다.
print('결측값이 존재하는 샘플 제거')
print(titanic_2.info())
일반적으로 전처리과정에서 제거.
이상치가 있으면 모델의 성능을 저하할 수 있음.
어떤 값이 이상치인지 판단하는 기준이 중요함!
1) 통계 지표(카이제곱 검정, IQR 지표 등)를 사용하여 판단
2) 데이터 분포를 보고 직접 판단
3) 머신러닝 기법을 사용하여 이상치 분류
import pandas as pd
import numpy as np
from elice_utils import EliceUtils
elice_utils = EliceUtils()
# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
# Cabin 변수를 제거합니다.
titanic_1 = titanic.drop(columns=['Cabin'])
# 결측값이 존재하는 샘플 제거합니다.
titanic_2 = titanic_1.dropna()
# (Age 값 - 내림 Age 값) 0 보다 크다면 소수점을 갖는 데이터로 분류합니다.
outlier = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) > 0 ]['Age']
print('소수점을 갖는 Age 변수 이상치')
print(outlier)
print('이상치 처리 전 샘플 개수: %d' %(len(titanic_2)))
print('이상치 개수: %d' %(len(outlier)))
"""
1. 이상치를 처리합니다.
"""
titanic_3 = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) == 0 ]['Age']
print('이상치 처리 후 샘플 개수: %d' %(len(titanic_3)))
데이터 분리는 왜 필요?
머신러닝 모델을 평가하기 위해서는 학습에 사용하지 않은 평가용 데이터 필요
약 7:3~8:2 비율로 학습용, 평가용 데이터를 분리함
(학습용 데이터를 최대한 확보해야 머신러닝 성능이 보장될 가능성이 높음)
지도학습 데이터 분리
지도학습의 경우 feature 데이터와 label 데이터를 분리하여 저장합니다.
Feature 데이터: label을 예측하기 위한 입력 값
Label 데이터: 예측해야할 대상이 되는 데이터
공부시간 / 시험점수 데이터가 주어진다면
공부시간 대비 시험점수 예측 ->
시험점수가 예측할 대상이므로 Label 데이터!
이것을 예측하기 위해 필요한 데이터로 쓰는 것이 여기서는 공부시간! Feature Data
타이타닉 데이터를 바탕으로 생존자를 예측한다면?
생존여부 판단. survived => 생존여부 데이터가 Label 데이터 (예측하려는 값)
나머지가 Feature 데이터. Feature 데이터를 통해 Label 데이터를 예측하려고 함.
이 두 데이터를 분리하는 것이 전처리 중 하나.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from elice_utils import EliceUtils
elice_utils = EliceUtils()
# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
# Cabin 변수를 제거합니다.
titanic_1 = titanic.drop(columns=['Cabin'])
# 결측값이 존재하는 샘플 제거합니다.
titanic_2 = titanic_1.dropna()
# 이상치를 처리합니다.
titanic_3 = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) == 0 ]
print('전체 샘플 데이터 개수: %d' %(len(titanic_3)))
"""
1. feature 데이터와 label 데이터를 분리합니다.
"""
X = titanic_3.drop(columns=['Survived'])
y = titanic_3.['Survived']
print('X 데이터 개수: %d' %(len(X)))
print('y 데이터 개수: %d' %(len(y)))
"""
2. 학습용, 평가용 데이터로 분리합니다.
"""
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 분리한 데이터의 개수를 출력합니다.
print('학습용 데이터 개수: %d' %(len(X_train)))
print('평가용 데이터 개수: %d' %(len(X_test)))
# elice-utils
# maintainer: Suin Kim (suin@elicer.com) and Jungkook Park (jk@elicer.com)
import base64
import mimetypes
import os
import urllib.parse
import urllib.request
class EliceUtils(object):
def __init__(self):
self._execution_token = os.getenv('EXECUTION_TOKEN')
self._executor_ip = os.getenv('EXECUTOR_IP')
self._executor_com_port = os.getenv('EXECUTOR_COM_PORT')
self._otp_key = None
self._local_mode = False
if not all((self._execution_token, self._executor_ip, self._executor_com_port)):
self._local_mode = True
print('=== NON-ELICE ENVIRONMENT ===')
print('Warning: This script is running on the non-elice environment. '
'All outputs will be redirected to standard output.')
print('=============================')
def _send(self, url, data):
if self._local_mode:
msg_type = data['type']
msg_data = data['data']
if msg_type in ['grader', 'score']:
print('[%s] %s' % (msg_type, msg_data), end='')
else:
print('[%s]' % msg_type, end='')
return
data_encoded = urllib.parse.urlencode(data)
q = urllib.request.Request(url,
data=data_encoded.encode('utf-8'))
try:
urllib.request.urlopen(q)
except Exception:
raise Exception('Failed to send message to elice.')
def _handle_image(self, filepath):
mtype, _ = mimetypes.guess_type(filepath)
if mtype is None or not mtype.startswith('image/'):
raise ValueError('Invalid image filepath.')
with open(filepath, 'rb') as f:
data = 'data:%s;base64,%s' % (
mtype,
base64.b64encode(f.read()).decode('utf-8')
)
return data
def _handle_file(self, filepath):
mtype, _ = mimetypes.guess_type(filepath)
with open(filepath, 'rb') as f:
data = '%s;data:%s;base64,%s' % (
os.path.basename(filepath),
mtype or 'application/octet-stream',
base64.b64encode(f.read()).decode('utf-8')
)
return data
def send(self, msg_type, msg_data):
self._send(
'http://%s:%s/comm/send/%s' % (self._executor_ip,
self._executor_com_port,
self._execution_token),
{'type': msg_type, 'data': msg_data}
)
def send_image(self, filepath):
self.send('image', self._handle_image(filepath))
def send_file(self, filepath):
self.send('file', self._handle_file(filepath))
def secure_init(self):
if self._local_mode:
return
try:
r = urllib.request.urlopen(
'http://%s:%s/comm/secure/init/%s' % (self._executor_ip,
self._executor_com_port,
self._execution_token)
)
except Exception:
raise Exception('Failed to initialize elice util secure channel.')
self._otp_key = r.read().decode('utf-8')
def secure_send(self, msg_type, msg_data):
self._send(
'http://%s:%s/comm/secure/send/%s/%s' % (self._executor_ip,
self._executor_com_port,
self._execution_token,
self._otp_key),
{'type': msg_type, 'data': msg_data}
)
def secure_send_image(self, filepath):
self.secure_send('image', self._handle_image(filepath))
def secure_send_file(self, filepath):
self.secure_send('file', self._handle_file(filepath))
def secure_send_grader(self, msg):
self.secure_send('grader', msg)
def secure_send_score(self, score):
self.secure_send('score', score)