**데이터 전처리
1.단위 확산
2.자료형 변환
-> astype('변환하고자 하는 자료형')
-> 데이터 가져오기
mpg = pd.read_csv('./data/auto-mpg.csv', header=None)
mpg.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight',
'acceleration', 'model year', 'origin', 'name']
#horsepower는 마력으로 숫자 데이터 인데 현재는 object
#origin은 현재 1,2,3으로 만들어진 숫자인데 실제 의미는 USA, EU, JPA을 나타냄
#model year는 제조년도를 나타내는데 현재는 숫자로 되어있음.
-> horsepower를 숫자 자료형으로 변경
#horsepower의 모든 종류의 값을 조회
print(mpg['horsepower'].unique())
#이 방법은 데이터가 아주 많거나 데이터의 종류가 연속형 형태면 쉽지 않습니다.
#데이터 많을 떄는 먼저 변경 - 예외가 발생하면 그 예외를 확인하면 이유를 알 수 있음
#?을 NaN치환
mpg['horsepower'].replace('?',np.nan,inplace=True)
#NaN제거
mpg.dropna(subset=['horsepower'],axis=0,inplace=True)
#자료형 변환
mpg['horsepower']=mpg['horsepower'].astype('float')
mpg.info()
=>origin 의 데이터를 1->USA , 2->EU, 3->JAPAN 으로 치환
#범주형 데이터를 의미를 갖는 문자열로 변환: 출력이 목적
#replace 에 dict을 대입하면 key를 찾아 value로 치환
mpg['origin'].replace({1:'USA',2:'EU',3:'JAPAN'},inplace=True)
print(mpg['origin'].head())
=>model year 컬럼의 값을 범주형(category- 특정 값만을 가져야 하는 자료형) 으로 치환
#문자열 이나 숫자 자료형은 원핫 -인코딩이 안되는 경우가 발생할 수 있음
#원핫 인코딩을 문자열을 숫자형으로 바꿈
mpg['model year']=mpg['model year'].astype('category')
mpg.info()
3.Standardization(표준화)
1)개요
=>다변량 분석(2개 이상의 컬럼을 같이 사용해서 수행하는 분석)에서는 각 컬럼에 들어있는 숫자 데이터의 상대적 크기 차이 떄문에 분석 결과가 왜곡될 수 있음
=>상대적 크기 차이를 동일하게 맞추는 작업이 표준화 또는 Scailing
=>방법은 여러가지가 존재하는데 일반적으로0~1이나 -1~ 1로 만듭니다
=>방법을 달리하는 가장 큰 이유는 이상치 여부 때문
2)표준값과 편차 값
=>모든 값들의 표준 값을 정해서 그 값을 기준으로 차이를 구해서 비교하는 방법
=>표준 값:(데이터-평균)/표준 편차
=>편차 값:표준값*10 + 50
=>해석
표준 값들의 평균은 0 이고 표준 편차는 1(정규 분포와 비교하기 쉽도록 하기 위해서)
편차 값은 평균이 50이고 표준 편차는 10
3)scikit-learn의 표준화 클래스
=>표준화 클래tm
=>StandardScaler(데이터)
-평균이 0 표준편차가 1이 되도록 변환
-주성분 분석등에서 많이 사용
=>MinMaxScaler(데이터)
-최대값이 1 그리고 최소값이 0이 되도록 변환
-(데이터-최소값)/(최대값-최소값)
-신경망에서 주로 이용
=>RobustScaler(데이터)
Robustic:이상치에 덜 민감하다는 뜻
-중앙값(median)이 0 그리고 IQR(Interquartile Range)이 1이 되도록 변환
-데이터에 이상치가 많으면 평균과 표준편차 영향을 미쳐서 위의 방식이 부정적
-중간 값과 사분위 범위를 사용하여 스케일 조정
-(데이터-중간값)/(75%값-25%값)
=>MaxAbScaler(데이터)
-0을 기준으로 절대값이 가장 큰 수가 1 또는 -1이 되도록 변환
-절대값이 0~1 사이가 되도록 매핑
-각 열 별로 절대값이 가장 큰 값으로 나누는 것
-양수 데이터로만 구성된 경우에는 MinMax 와 유사하면 이상치에 민감
-음수 자체가 어떤 의미를 지닌 경우 사용
=>QuantileTransformer
-데이터를 1000개 분위로 나누어 0~1 사이에 고르게 분포시키는 방식
-이상치에 영향을 덜 받음
=>표준화 과정
-데이터를 입력으로 해서 fit 메서드를 호출해서 분포 모수를 객체 내에 저장하고 데이터를 입력으로 해서 transform 메서드를 호출하면 표준화된 결과를 리턴
2개의 과정을 합친 fit_transform 메서드를 이용하는 것도 가능
-훈련 세트와 테스트 세트로 데이터를 나눈 경우 동일한 방식으로 표준화를 수행해야 함
from sklearn import preprocessing
#사이킷런은 머신러닝을 위한 패키지라서 numpy 배열을 가지고 작업을 수행
x = mpg[['horsepower']].values
#print(x)
#표준화 전의 기술 통계
print("표준화 전 기술 통계")
print("평균:",np.mean(x))
print("표준편차:",np.std(x))
print("최대:",np.max(x))
print("최소:",np.min(x))
#평균이 0이고 표준편차가 1로 만드는 Scaler
scaler=preprocessing.StandardScaler()
scaler.fit(x)
x_scaled=scaler.transform(x)
print("평균:",np.mean(x_scaled))
print("표준편차:",np.std(x_scaled))
print("최대:",np.max(x_scaled))
print("최소:",np.min(x_scaled))
-
4)Normalization(정규화)
=>데이터의 범위를 0과 1로 변환해서 데이터의 분포를 조정하는 방법
=>scikit-learn의 Normalizer 클래스의 객체를 만든 후 transform 함수에 데이터를 대입하면 정규화한 결과를 리턴합니다.
이 때 norm 옵션에 l1이나 l2 을 설정할 수 있는데 l1은 맨해튼거리로 계산하고 l2 를 설정하면 유클리드 거리를 이용합니다
=>하나의 속성이 아니라 여러 개의 속성을 한꺼번에 스케일링
=>행 단위로 스케일링
=>여러 개의 컬럼을 가진 데이터(텍스트)에서 주로 이용
컬럼들이 독립적이지 않은 경우 사용
=>유클리드 거리:대각선 거리- 각 좌표를 뺸 다음 제곱해서 더한 후 제곱
from sklearn.preprocessing import Normalizer
features=np.array([[1,2],[2,3],[4,3]])
#정규화 객체를 생성
normalizer=Normalizer(norm="l2")
result=normalizer.transform(features)
print(result)
=>맨해튼 거리:각 좌표 거리의 합
from sklearn.preprocessing import Normalizer
features=np.array([[1,2],[2,3],[4,3]])
#정규화 객체를 생성
normalizer=Normalizer(norm="l1")
result=normalizer.transform(features)
print(result)
5)다항 특성 과 교차항 특성
=>컬럼들을 제곱하고 곱해서 데이터를 추가하는 것
=>특성과 타겟 사이에 비선형 관계가 존재하는 경우 다항 특성을 생성
=>scikit-learn의 PolynomialFeatures 클래스를 이용
옵션으로 degree를 설정할 수 있는데 몇 차항까지생성할 것인지를 설정
include_bias 옵션으로 첫번쨰 항으로 상수 1을 추가할지 여부를 설정
interaction_only 옵션에 True 를 설정하면 교차항 특성만 생성
생성된 객체의 get_feature_names_out 함수를 이용하면 변환식을 이름으로 반환
from sklearn.preprocessing import PolynomialFeatures
features=np.array([[2,3],[3,4],[1,5]])
#다항 ㅁ및 교차항을 생성해주는 객체 생성
polynomial_interaction = PolynomialFeatures(degree=2,include_bias=False)
result= polynomial_interaction.fit_transform(features)
print(result)
6)특성 변환
=>컬럼에 함수를 적용해서 데이터를 변경하는 작업
=>pandas 의 apply 함수를 이용해서 수행하는 것이 가능
=>scikit-learn에서는 FunctionTransformer 와 ColumnTransformer API를 제공
FunctionTransformer 는 모든 열에 동일한 함수를 적용하고자 할 때 사용-pandas 의 apply
ColumnTransformer는 각 열에 다른 함수를 적용하고자 할 때 사용하는데 함수와 변환기 그리고 컬럼 이름의 리스트로 이루어진 튜플의 리스트를 매개변수로 받습니다
=>pandas 는 데이터가 Series 나 DataFrame이고 scikit-learn 은 데이터가 numpy의 ndarray이고 특별한 경우가 아니면 2차원 배열
from sklearn.preprocessing import FunctionTransformer
features=np.array([[1,3],[3,6]])
def add_one(x:int) -> int:
return x+1
one_transformer=FunctionTransformer(add_one)
result=one_transformer.transform(features)
print(result)
df=pd.DataFrame(features,columns=['f1','f2'])
print(df)
print(df.apply(add_one))
7)숫자 데이터의 이산화
=>데이터를 분석할 때 연속된 데이터를 그대로 사용하기 보다는 일정한 구간으로 나누어서 분석하는 것이 효율적인 경우가 있음
=>가격이나 비용 또는 효율, 나이 등 연속적인 값을 일정한 수준이나 정도를 나타내는 이산적인 값으로 나타내어 구간별 차이를 드러나게 함
=>연속형 데이터를 일정한 구간으로 나누고 각 구간을 범주형 이산 변수로 치환하는 과정을 binning(구간 분할)이라고 합니다.
=>pandas의 cut 함수를 이용하면 연속형 데이터를 여러 구간으로 분할해서 범주형 데이터로 변환하는 것이 가능
=>구간을 분할 할 때 반드시 일정한 간격을 만들 필요는 없습니다.
=>pandas의 cut 함수 이용
#연속형 데이터를 간격 단위로 분할해서 값을 할당하는 작업을 이산화
#구간 생성
#3개의 구간으로 분할
#최소값 부터 최대값 까지 3개로 분할하고 데이터의 개수 와 경계값을 리턴해 줍니다.
count, bin_dividers = np.histogram(mpg['horsepower'], bins = 3)
print(count)
print(bin_dividers)
#각 그룹에 할당할 데이터를 생성
bin_names = ["저출력", "보통출력", "고출력"]
#이산화
#x는 분할할 데이터, bins는 구간의 경계값 리스트, labels는 구간에 할당할 데이터 목록
#include_lowest는 첫 경계값 포함 여부
mpg['hp_bin'] = pd.cut(x = mpg['horsepower'], bins = bin_dividers,
labels=bin_names, include_lowest=True)
print(mpg[['horsepower', 'hp_bin']].head(20))
=>numpy의 digitize 함수를 이용해서 구간 분할이 가능
첫번째 데이터는 이산화 할 데이터
bins 매개변수에는 이산화 할 경계값 리스트
right 매개변수에는 경계값을 포함할 지 여부를 설정
- numpy의 digitize를 이용한 구간 분할
age = np.array([[13], [30], [67], [36], [64], [24]])
#pandas의 메서드들은 DataFrame 이나 Series를 이용해서 작업
#machine learning 관련된 메서드들은 2차원 이상의 ndarray를 가지고 작업을 수행
#30이 안되면 0 30이상이면 1로 변환
#bins에는 여러 개의 데이터 설정이 가능
result = np.digitize(age, bins=[30])
print(result)
=>scikit-learn 의 Binarizer 와 KBinsDiscretizer
- Binarizer: 생성할 때 하나의 경계값을 대입해서 생성하고 fit_transform을 호출하면 경계값보다 작은 경우는 0 그렇지 않은 경우는 1을 할당해서 리턴
- KBinsDiscretizer
첫번째 매개변수로 구간의 개수를 설정하고 encode 매개변수에 ordinal을 설정하면 각 구간을 하나의 컬럼에 일련번호 형태로 리턴하고 onehot으로 설정하면 희소 행렬을 리턴하고 onehot-dense를 설정하면 밀집 행렬의 형태로 리턴
strategy 매개변수에 quantile을 설정하면 일정한 비율로 uniform을 설정하면 구간이 폭이 일정하도록 생성
각 구간의 값을 알고자 하는 경우는 bin_edges_ 속성을 확인
- Label Encoding
- OneHot Encoding
- Sparse Matrix(희소 행렬)
- Dense Matrix(밀집 행렬)
=>Binarizer 를 이용한 구간 분할
from sklearn.preprocessing import Binarizer
#threshold(임계값)
#흑백 이미지 데이터에서 뚜렷한 구분하기 위해서 임계값을 설정해서
#임계값 아래 와 위를 구분합니다.
binarizer = Binarizer(threshold=30.0)
print(binarizer.transform(age))
=>KBinsDiscretizer를 이용한 구간 분할
from sklearn.preprocessing import KBinsDiscretizer
#균등한 분포로 4분할
#ordinal: 라벨 인코딩 - 일련번호
kb = KBinsDiscretizer(4, encode='ordinal', strategy='quantile')
print(kb.fit_transform(age))
print()
#원핫인코딩을 해서 희소 행렬로 표현
kb = KBinsDiscretizer(4, encode='onehot', strategy='quantile')
print(kb.fit_transform(age))
print()
#원핫인코딩을 해서 밀집 행렬로 표현
kb = KBinsDiscretizer(4, encode='onehot-dense', strategy='quantile')
print(kb.fit_transform(age))
=>2개 이상의 컬럼의 값으로 이산화: 군집(Clustering)
- 데이터 전처리 과정에서 군집 분석을 수행해서 군집 분석한 결과를 기존 데이터의 컬럼으로 추가해서 사용하는 경우가 있음
- 수집 -> 전처리 -> 분석의 과정은 절대적인 순서의 과정이 아니며 전처리 과정에서 분석을 해서 적용하기도 하고 분석이 끝나고 난 후 다시 전처리로 돌아가서 나선형으로 수행하기도 합니다.
- 전처리의 일환으로 분석을 수행하는 경우는 높은 정확도를 요구하는 것이 아니므로 복잡한 모델(일반적으로 정확도는 높지만 시간이 오래 걸림)을 선택하는 것은 바람직하지 않음
=>KMeans 알고리즘을 이용한 군집의 결과를 컬럼으로 추가
from sklearn.cluster import KMeans
#샘플 데이터 - DataFrame에서 추출하는 것이 일반적
sample = np.array([[13, 30], [20, 30], [21, 99], [21, 33], [98, 22], [20, 87]])
dataframe = pd.DataFrame(sample, columns=['f1', 'f2'])
#print(dataframe)
clustering = KMeans(3, random_state=42)
clustering.fit(sample)
dataframe['group'] = clustering.predict(sample)
print(dataframe)
8)이상치 탐지
=>z-score 를 이용하는 방법
중앙값을 기준으로 표준 편차가 절대값 3 범위의 바깥쪽에 위치하는 데이터를 이상치로 간주
이 방식은 데이터가 12개 이하이면 이상치를 감지할 수 없음
=>z-score를 보정해서 사용하는 방법
편차의 절대값 범위를 3.5로 설정한 수 0.6745를 곱해서 사용
=>IQR(3사분위수 - 1사분위수)을 이용하는 방법
1사분위수보다 1.5IQR 이상 작은 값이나 3사분위수보다 1.5IQR 이상 큰 값을 이상치로 간주
boxplot 에서 수염바깥쪽 데이터를 이상치로 간주
=>3가지 방식을 모두 사용해서 2가지 경우 이상에서 이상치로 간주되는 데이터를 이상치로 판정
=>데이터의 특정 비율을 이상치로 간주(scikit-learn 의 covariance.EllipticEnvelope 클래스 이용)
=>수식을 이용한 이상치 탐지
#이상치 탐지를 위한 데이터 생성
features = np.array([[10000, 10, 7, 6, 4, 3, 3, 2], [20000, 10, 7, 6, 4, 3, 2, 8]])
#z-score를 이용해서 이상치를 탐지해주는 함수
def outliers_z_score(ys):
#임계값 설정
threshold=3
#평균 구하기
mean_y = np.mean(ys)
#표준편차 구하기
stdev_y = np.std(ys)
z_scores = [(y-mean_y) / stdev_y for y in ys]
print("z_score:", z_scores)
return np.where(np.abs(z_scores) > threshold)
print(outliers_z_score(features))
#z_score를 보정
def outliers_mod_z_score(ys):
#임계값 설정
threshold=3.5
#중앙값 구하기
median_y = np.median(ys)
#중위절대 편차를 구함
median_absolute_deviation_y = np.median([np.abs(y-median_y) for y in ys])
#중위 절대 편차를 가지고 계산한 후 0.6745를 곱하고 이 결과가 3.5 보다 큰 경우를 이상치로 감지
#데이터의 개수에 상관없이 이상치를 감지
modified_z_scores = [0.6745 * (y - median_y) / median_absolute_deviation_y for y in ys ]
return np.where(np.abs(modified_z_scores) > threshold)
print(outliers_mod_z_score(features))
#IQR을 이용하는 방법
#boxplot을 그렸을 때 수염의 바깥쪽에 있는 데이터를 이상치로 간주
def outliers_iqr(ys):
#iqr 구하기
quartile_1, quartile_3 = np.percentile(ys, [25, 75])
iqr = quartile_3 - quartile_1
#하한 과 상한 구하기
lower_bound = quartile_1 - (iqr * 1.5)
upper_bound = quartile_3 + (iqr * 1.5)
return np.where((ys > upper_bound) | (ys < lower_bound))
features = np.array([[10, 10, 7, 6, 8], [20000, 2, 3, 4, 5]])
print(outliers_iqr(features))
print(outliers_iqr(mpg[['horsepower']].values))
=>일정한 비율의 데이터를 이상치로 간주하는 방식
특별한 경우에는 값이 아니고 비율을 이용해서 이상치를 판정합니다.
대표적인 경우가 우리나라 학교에서 국가장학금 지급할 때 특정한 소득 기준은 없고 상위 10%를 제외한 학생에게 장학금을 지급하는 경우
#데이터를 생성해주는 API
from sklearn.datasets import make_blobs
#일정한 비율을 이상치로 간주하는 API
from sklearn.covariance import EllipticEnvelope
#2행 10행의 데이터를 랜덤하게 생성
#_는 데이터를 사용하지 않음
features, _ = make_blobs(n_samples=10, n_features=2, centers=1, random_state=42)
#이상치 생성 - 0번 데이터는 이상한 데이터
features[0,0] = 10000
features[0,1] = 1000
print(features)
#이상치 탐지 객체 - 10%는 이상치로 간주
outlier_detector = EllipticEnvelope(contamination=0.1)
outlier_detector.fit(features)
#-1 이면 이상치 1이면 정상적인 데이터
outlier_detector.predict(features)
'Study > Data전처리 및 통계' 카테고리의 다른 글
Pandas(8)-한글 NLP (0) | 2024.02.20 |
---|---|
Pandas(7)-Outlier,Encoder,자연어처리 (0) | 2024.02.19 |
Pandas(5) (0) | 2024.02.15 |
Pandas(4) (0) | 2024.02.14 |
Pandas(3) (0) | 2024.02.13 |