즐겁게!! 자신있게!! 살아보세!!

재밌는 인생을 위하여! 영촤!

Language_Study/Python

[Python] 10-1.데이터전처리

Godwony 2021. 1. 19. 21:25
728x90
반응형

데이터 가공

  • 패키지가 제공하는 데이터는 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)

# 갑과의 거리가 짧은 값으로 리턴 
728x90
반응형

'Language_Study > Python' 카테고리의 다른 글

[Python] 11-1.DataFrame응용과 시각화  (0) 2021.01.19
[Python] 10-2.데이터전처리  (0) 2021.01.19
[Python] 9.시각화  (0) 2021.01.19
[Python] 8.pandas  (0) 2021.01.19
[Python] 7.numpy  (0) 2021.01.19