Language_Study/Python
[Python] 10-1.데이터전처리
Godwony
2021. 1. 19. 21:25
반응형
데이터 가공
- 패키지가 제공하는 데이터는
load_dataset('데이터이름')
을 이용하면 데이터프레임이나 패키지에서 제공하는 별도의 클래스 타입으로 데이터가 다운로드 된다.- 인터넷이 안되면 데이터를 사용할 수 없습니다.
대기업이나 금융기관은 인터넷은 되지만 데이터는 함부로 다운로드 받거나 설치할 수 없도록 설정된 경우가 있다.
이런 경우에도 데이터는 다운로드가 안된다.
1. 셀의 데이터 수정
- replace 이용
- 첫번째 매개변수로 원본 데이터를 대입하고 두번째 매개변수로 수정할 데이터를 설정
- dict로 원본데이터와 수정할 데이터를 설정
replace(1,2)
replace({1:2})
- 원본데이터에 정규식을 사용ㅎㄹ 수 있는데 이 경우에는
regex=True
를 추가- 텍스트마이닝을 하고자 하는 경우에는 정규식을 학습할 필요가 있다.
- 여러 개의 데이터를 수정하고자 하는 경우에는 list로 대입해도 된다.
2. 결측치 처리
- 결측치 : 존재하지 않는 데이터로 파이썬에서는 None이고 numpy에서는 numpy.NaN 으로 표현
- 결측치와의 연산 결과는 None 입니다.
- read_csv 메소드에는 na_values 옵션을 이용해서 None으로 처리할 list를 대입할 수 있다.
1) Dataframe의 null 관련 메소드
isnull()
: None 데이터인 경우는 True 그렇지 않으면 False- notnull 은 반대
dropna()
: NaN 값을 소유한 행을 제거하고 axis=1 옵션을 추가하면 열을 제거- how 매개변수에 all을 대입하면 모든 데이터가 NaN 경우만 제거
- thresh 매개변수에 정수를 대입하면 이 개수보다 많은 NaN 을 가진 경우만 제거
fillna()
: NaN 값을 소유한 데이터를 다른 값으로 치환하는 함수- 값을 직접 입력할 수 있고 method 매개변수에 ffill 등과 같은 값을 설정해서 앞이나 뒤의 값으로 채울수있다
2) 결측치 처리 방식
- 결측치가 많은 열의 경우는 열을 제거한다.
- 결측치가 아주 많지 않은 경우는 결측치를 가진 행만 제거한다.
- 결측치를 가진 데이터를 삭제하기 애매한경우(결측치가 몇개 안되거나 어쩔수 없이 결측치가 발생하는 경우)에는 결측치를 다른 값으로 치환을 한다.
- 다른값으로 치환하는 경우에는 최빈값(가장자주나오는값), 평균, 중간값 등을 사용하는 방식이 있고 머신러닝을 이용해서 가장 비슷한 데이터의 값으로 채우는 방법이 있다.
- 머신러닝을 이용해서 값을 채우는 것이 가장 좋지만 이 방법은 시간이 오래 걸리기 때문에 데이터가 아주 많은 경우는 사용하기가 곤란합니다.
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
[-]
# 수치연산과 선형대수 , ndarray 자료구조를 가진 패키지
import numpy as np
# Series와 Dataframe 자료구조를 가진 패키지
import pandas as pd
# 샘플 데이터와 시각화를 위한 패키지
import seaborn as sns
#titanic 데이터 가져오고 데이터형태 보기
titanic = sns.load_dataset('titanic')
print(titanic.head())
print()
print(titanic.info())
# object = string 문자열
# int64 = 정수
# category = 범주형(라디오버튼, 남여 등)
# info() 는 print 빼고 해도됨. None <- 나오는 이유
[-]
# NaN이 존재하는 지 확인
# titanic에서 앞 쪽 10개의 데이터가 NaN을 포함하는지 확인
print(titanic.head(10).isnull())
[-]
#titanic에서 NaN을 포함한 행의 개수를 파악
# Ture 는 1, False 는 0
print(titanic.isnull().sum(axis=0))
# NaN이 300개 이상인 열을 제거
# NaN이 너무 많아서 제거 ( columns 수가 14개로 1개 줄어들었음, deck )
titanic.dropna(thresh=300, axis=1, inplace=True)
print(titanic.info())
# age 열의 값이 NaN 인 행을 제거 - 아주 많지 않으면 행을 제거
titanic.dropna(subset=['age'], how='any', axis=0, inplace=True)
print(titanic.info())
3) 결측값 대치
- 사이킷 런의 SimpleImputer 클래스를 이용해서 최빈값이나 평균 및 중간값 등으로 채울수 있습니다.
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
[-]
# 수치연산과 선형대수 , ndarray 자료구조를 가진 패키지
import numpy as np
# Series와 Dataframe 자료구조를 가진 패키지
import pandas as pd
# 샘플 데이터와 시각화를 위한 패키지
import seaborn as sns
[-]
titanic = sns.load_dataset('titanic')
print(titanic['embarked'][820:830])
[-]
# 직접 NaN 값을 다른 값으로 대체 - 앞의 데이터로 채움
# 표 형태의 데이터를 가져온 경우 셀 병합이 된 경우에 사용
titanic['embarked'].fillna(method='ffill', inplace=True)
print(titanic['embarked'][820:830])
# 사이킷 런을 이용해서 결측치 채우기
features = np.array([[100],[200],[300],[500],[40],[np.NaN]])
# 중간값으로 채우는 imputer 생성
from sklearn.impute import SimpleImputer
imputers = SimpleImputer(strategy='median') # 중앙값을 NaN에 채움
features_imputed = imputers.fit_transform(features)
print(features_imputed)
4) 머신러닝을 이용한 결측값 대치
- fancyimpute 패키지 이용
# KNN(분류) 알고리즘을 이용한 결측치 채우기
# fancyimpute 설치하고 실행
# KNN을 사용하지 않고 다른 머신러닝을 분류 알고리즘을 사용해도 된다.
import numpy as np
from fancyimpute import KNN
features = np.array([[200, 300], [300, 500], [400,410],[205, np.NaN]])
features_imputed = KNN(k=5, verbose=0).fit_transform(features)
print(features_imputed)
- fancyimpute 설치 도중 아래와 같은 에러가 발생하면
google.com 에서 Visual C ++ 14.0 재배포 패키지를 검색해서 다운로드 받은 후 설치하고 다시 설치하면 됨
이 에러는 Windows에서만 발생
패키지를 만들 때 Visual C++에서 만들어서 배포를 해서 이런 현상이 발생
3. 중복 데이터 처리
- 하나의 데이터 셋에서 중복된 데이터가 존재한다면 제거하는 것이 좋다.
- 동일한 데이터가 여러 개 존재하면 머신러닝이나 딥러닝을 수행할 때 결과가 왜곡 될수있다.
- 관련 함수
duplicated()
: 데이터의 중복여부를 리턴해주는 메소드drop_duplicates()
: 중복된 데이터 제거- 기본은 모든 컬럼의 값이 같은 경우에 제거
- subset 옵션에 컬럼이름의 list를 대입하면 컬럼이름에 해당하는 값들이 같은 경우에 제거
- 첫번째 데이터를 남기고 나머지 데이터를 삭제하는데 keep 옵션을 이용하면 다른 데이터를 남길수도 있다.
- 이런 상황은 여러 개의 데이터셋을 하나로 합칠 때 많이 발생한다.
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
import pandas as pd
# 중복 데이터 처리
df = pd.DataFrame([['차범근', '크루이프','차범근'],['대한민국','네덜란드','대한민국']])
df =df.T
print(df)
# 중복된 데이터 확인
print(df.duplicated())
# 중복된 데이터 제거
df.drop_duplicates(inplace=True)
print(df)
데이터 표준화
- 여러 곳에서 수집한 데이터들은 단위나 대소문자 구분 또는 약자 활용들으로 인해 바로 사용하기 어려운 경우가 발생할 수 있습니다.
- 이런 경우에는 단위나 대소문자 구분 등을 통일할 필요가 있습니다.
1. 단위가 다른 경우에는 반드시 단위는 통일을 시켜야 한다.
2. 자료형 변환
- 데이터를 수집할 때는 자료형을 고려하지 않고 수집을 하지만 분석이나 머신러닝등에 적용을 할 때 적용하고자 하는 알고리즘에 맞게 데이터의 자료형을 수정을 해야 합니다.
- 분류를 할 때는 숫자 데이터가 아니라 범주형이 필요하고 머신러닝에서 거리를 계산하려고 하는 경우에는 문자 데이터의 경우는 거리 계산이 안되므로 숫자 데이터로 변환을 해야 한다.
- 자료형 변환은
astype('자료형')
으로 가능
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
[-]
import pandas as pd
df = pd.read_csv('../data/auto-mpg.csv', header=None)
df.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
print(df.head())
print(df.info())
[-]
# origin의 경우는 생산국가
# 1이면 미국, 2이면 유렵, 3이면 일본
# 현재 데이터는 1, 2, 3 숫자형태로 존재
# origin 데이터를 문자열로 치환하고 자료형을 category로 변환
# 자료형 변환은 astype('자료형')으로 가능
# origin이 값들을 문자열로 치환
df['origin'].replace({1:'미국', 2:'유럽', 3:'일본'}, inplace=True)
print(df.head())
print(df['origin'].dtype)
[-]
# 데이터를 범주형으로 변환
df['origin'] = df['origin'].astype('category')
print(df['origin'].dtypes)
[-]
# 문자열로 변환
df['origin'] = df['origin'].astype('str') # int, float 대입 가능함.
print(df['origin'].dtypes)
3. 연속형 데이터의 이산화
- 연속적인 데이터를 가지고 분류를 한다면 데이터 변환없이 하게 되면 그룹의 종류가 너무 많아진다.
- 이런 경우에는 연속적인 데이터를 몇 개의 구간으로 이산화를 해야 한다.
- 이를 구간분할(binning)이라고 한다.
- pandas의 cut 함수를 이용
- x 에 데이터 배열을 설정
- bins에 경계값 배열을 설정
- labels에 변환할 이산 데이터 배열을 설정
- include_lowest에 Ture를 설정하면 첫번째 경계값을 포함
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
import pandas as pd
df = pd.read_csv('../data/auto-mpg.csv', header=None)
df.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
print(df['horsepower'])
print(df['displacement'])
# displacement를 초대형, 대형, 중형, 준중형, 소형 으로 새로운 컬럼 만들기
# 5등분할 숫자 배열 만들기
import numpy as np
count, bin_dividers = np.histogram(df['displacement'], bins=5)
print(bin_dividers)
# 치환할 데이터 배열 만들기
bin_names = ['소형', '준중형','중형', '대형', '초대형']
# 치환
df['차량분류'] = pd.cut( x= df['displacement'], bins=bin_dividers, labels=bin_names, include_lowest= True)
print(df)
2) numpy의 digitize 이용
- 첫번째 매개변수는 분할할 데이터
- bins에 구간을 분할할 값의 list를 대입
- right에 구간을 포함할 지 여부를 bool로 설정
- 각 구간에 0, 1, 2 형태이 일련번호를 배정해서 그 결과를 배열로 리턴
import numpy as np
# numpy digitze 이용
age = np.array([[30], [40], [29], [50], [60]])
# 40을 기준으로 분할 - 경계값이 다음 그룹으로 분할
print(np.digitize(age, bins=[40]))
print()
print(np.digitize(age, bins=[30, 50]))
print()
# right를 이용하면 경계값이 아래 그룹으로 분할
print(np.digitize(age, bins=[30, 50], right=True))
3) 여러개의 열로 구성된 데이터를 이산화해야 하는 경우
- 군집 분석 알고리즘을 이용
# 여러 개의 열로 구성된 데이터의 이산화
sample = np.array([[20,30],[40,70],[30, 60],[25,34],[43,75],[38, 63]])
df = pd.DataFrame(sample)
print(df)
# KMeans 군집 분석을 위한 라이브러리
from sklearn.cluster import KMeans
# 군집분석 객체 생성
cluster = KMeans(3, random_state=0)
# 데이터를 가지고 훈련
cluster.fit(sample)
# 예측 - 군집
df['group'] = cluster.predict(sample)
print(df)
범주형 데이터의 사용
1. 원 핫 인코딩
범주형 데이터를 수치화 하는 작업
- 카테고리를 나타내는 문자열 형태의 데이터는 머신러닝에 바로 사용할 수 없습니다.
- 머신러닝의 대다수 알고리즘은 숫자 데이터에서만 동작을 수행하기 때문이다.
- 카테고리 형태의 데이터를 특성의 소유 여부만을 나타내는 0과 1로 변환하는 작업을 원핫인코딩이라고 한다.
- pandas의
get_dummies()
라는 함수를 이용해서 원핫인코딩을 할 수 있는데 컬럼에 나올 수 있는 모든 값들을 조사해서 새로운 더미변수(열)을 만들고 속성을 소유하고 있으면 1, 없으면 0으로 표기
Jupyter Server: local
Python 3.7.4 64-bit ('anaconda3': conda): Idle
import pandas as pd
import numpy as np
df = pd.read_csv('../data/auto-mpg.csv', header=None)
df.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
print(df['horsepower'])
[-]
# 데이터 형변환
# df['horsepower'] = df['horsepower'].astype('float')
# object 형식을 float로 치환할려고 하려니 아래와 같은 에러가 발생한다.
# 데이터중에 ? 가 있나보다.
# ValueError: could not convert string to float: '?'
# ?인 데이터를 NaN으로 치환하고 NaN인 데이터를 제거
df['horsepower'].replace('?', np.NaN, inplace=True)
df.dropna(subset=['horsepower'], inplace=True, axis=0)
# 데이터 형변환
df['horsepower'] = df['horsepower'].astype('float')
[-]
# horsepower를 저출력, 보통출력, 고출력으로 구간분할
# 범주형 목록을 생성
bin_names = ['저출력', '보통출력','고출력']
# 3개로 나눌 경계값을 생성
count, bin_dividers = np.histogram(df['horsepower'], bins=3)
print(bin_dividers)
[-]
# 구간 분할
df['hp_bin'] = pd.cut(x=df['horsepower'], bins=bin_dividers, labels=bin_names, include_lowest=True)
print(df['hp_bin'])
[-]
2. 사이킷 런의 원핫 인코딩
preprocessing.LabelBinarizer
: 하나의 특성을 원 핫 인코딩fit_transform
메소드에 데이터를 대입하면 원 핫 인코딩을 해주고 그 결과를 가지고inverse_transform
메소드에 대입하면 원래의 데이터로 환원이 됩니다.classes_
속성을 확인하면 인코딩 순서를 확인할 수 있습니다.
preprocessing.MultiLabelBinarizer
: 여러 개의 특성을 원 핫 인코딩preprocessing.LabelEncoder
: 0부터 시작하는 정수로 변환, 일차원 배열preprocessing.OneHotEncoder
: 결과를 희소행렬로 리턴해주는 sparse=False 를 설정하면 밀집행렬로 리턴
get_dummies() 로 컬럼이 15개짜리의 데이터를 보자
아래와 같이 데이터가 나올텐데
000000000100000 # 메모리를 많이 잡아먹는다.
그래서 희소행열(10, 1)로 변환시킨다. # 10번째 자리에 1이 있다.
압축을 한거라 생각하면됨.(0이 아닌것만 표현)
LabelEncoder 는 고출력0, 보통출력 1, 저출력 2 로 설정해서 리스트로 나열한다. 거리계산 안됨.
LabelBinarizer 는 [1 0 0] [0 0 1] 식으로 거리계산이 가능하다.
# 위 코드에 이어서
# hp_bin 을 원핫인코딩 - 3개의 컬럼이 생성되고
# 컬럼의 이름은 저출력, 보통출력, 고출력이 된다.
# 자신의 값과 일치하는 컬럼에만 1이 되고 나머지 컬럼에는 0이 대입
# 저출력 보통출력 고출력
# 0 1 0
# 컬럼이름이 있음
dummy = pd.get_dummies(df['hp_bin'])
print(dummy)
[-]
# 사이킷 런을 이용한 원 핫 인코딩
from sklearn.preprocessing import LabelBinarizer
# 컬럼이름이 없다.
# [[0 1 0 ]]
one_hot = LabelBinarizer()
print(one_hot.fit_transform(df['hp_bin']))
[-]
# sort를 해버려서 class가 뭔지 확인을 해야한다.
# 데이터를 정렬하기 때문에 순서를 확인해야 한다.
print(one_hot.classes_)
[-]
print(one_hot.inverse_transform(one_hot.fit_transform(df['hp_bin'])))
[-]
# 여러개이 특성을 원 핫 인코딩
# 한개의 데이터가 여러 개의 특성을 갖는 경우 - Tuple, List 등
# 문장의 유사도 측정, 상품 추천 할 때 사용
from sklearn.preprocessing import MultiLabelBinarizer
features = [('Java', 'C++'), ('Java', 'Python'), ('C#', 'R'), ('Python', 'R')]
one_hot = MultiLabelBinarizer()
print(one_hot.fit_transform(features))
print(one_hot.classes_)
[-]
# get_dummies는 하나의 특성을 하나의 컬럼으로 생성
# 값의 종류가 15가지이면 15개의 컬럼이 생성
# 컬럼을 1개만 만들고 0부터 일련번호 형태로 값을 설정
from sklearn.preprocessing import LabelEncoder
one_hot = LabelEncoder()
print(one_hot.fit_transform(df['hp_bin']))
[-]
3. 순서가 있는 범주형 인코딩
- 순서가 있는 경우에는 replace 메소드를 이용해서 수치값으로 변환
- 일반적으로 일련번호처럼 숫자를 부여하지만 특별한 경우에는 일정 비율을 연산해서 부여하기도 합니다.
- 이와 유사한 기능을 sklearn의 OrdinalEncoder를 이용할 수 있음
# 위 코드 이어서
# sklearn 의 인코더들은 문자열을 기준으로 정렬을 한 후 수치를 부여함.
# 원하는 수치값으로 부여할수가 없음
# 범주형 데이터에 원하는 수치값을 부여해서 인코딩할 때는 replace 메소드나 OrdinalEncoder 이용
# 이상태에서 인코딩하면 보통->우수->저조 순서입니다.
df = pd.DataFrame({'Score':['저조', '우수','보통', '저조']})
print(df)
mapper = {'저조':0, '보통' :1, '우수':2}
# 저조:0, 보통:1, 우수:2
df['encoder'] = df['Score'].replace(mapper)
print(df)
# 순서가 있는 범주형 인코딩
from sklearn.preprocessing import OrdinalEncoder
features = np.array([['대한민국', 30], ['미국', 10],['뉴질랜드', 25],['캐나다', 20]])
# 각 컬럼의 데이터를 정렬하고 순서대로 가중치를 부여
# 가나다 순, 숫자 순으로 정렬
encoder = OrdinalEncoder()
result = encoder.fit_transform(features)
print(result)
4. 범주형 데이터에서 누락된 값 대체
- 가장 자주 등장하는 값을 누락된 값에 대체
- 머신러닝 알고리즘을 이용해서 구한 값으로 대체
# 머신러닝 알고리즘을 이용한 누락된 값 대체
from sklearn.neighbors import KNeighborsClassifier
# 훈련할 데이터
X = np.array([[0, 2.10, 1.45],
[1, 1.22, 4.34],
[0, 2.34, 1.98],
[3, -1.19, -0.30]])
# NaN을 가진 데이터
X_with_nan = np.array([[np.NaN, 0.33, 0.22],
[np.NaN, -3.22, -1.45]])
# 분류기를 생성
clf = KNeighborsClassifier(3, weights='distance')
# 훈련 모델
trained_model = clf.fit(X[:,1:], X[:,0])
# 데이터 예측
imputed_values = trained_model.predict(X_with_nan[:,1:])
print(imputed_values)
# 갑과의 거리가 짧은 값으로 리턴
반응형