🡆 데이터를 분석을 위한 EDA 시 필수적으로 이상치, 결측치 처리가 진행되어야 함
과목 | 점수 | |
---|---|---|
0 | 국어 | 50 |
1 | 국어 | 50 |
2 | 국어 | 50 |
3 | 국어 | 50 |
4 | 국어 | 1000 |
🡆 Data EDA 과정 중 하나인 "Data Cleaning" 또는 "데이터 정제"
🡆 과정을 보면 누락이 발생할 수밖에 없다! 누락된 데이터들을 알맞게 처리하는 과정을 반드시 익혀야 함
# 컬럼별 결측치 식별
df3.isnull().sum()
# 결측치 제거1 - 열 제거하기
df3 = df3.drop('Unnamed: 4', axis=1)
# 결측치 제거2 - 결측치가 있는 행들을 모두 제거 ★
df3.dropna()
# 같은 표현
df3.dropna(axis=0, how='any')
이름 | 나이 | LOCATION | CATEGORY |
---|---|---|---|
NULL | NULL | NULL | NULL |
농담곰 | 10 | 나고야 | 곰 |
퍼그씨 | 31 | 나고야 | 퍼그 |
두더지고로케 | NULL | 오사카 | 고로케 |
🡆 df3.dropna() 🡆
이름 | 나이 | LOCATION | CATEGORY |
---|---|---|---|
농담곰 | 10 | 나고야 | 곰 |
퍼그씨 | 31 | 나고야 | 퍼그 |
# 결측치 제거3 - 결측치가 있는 열을 모두 제거
# 열로 제거하면 컬럼이 제거되는 현상이 발생하므로 매우 위험합니다.
# df3.dropna(axis=1)
이름 | 나이 | LOCATION | CATEGORY |
---|---|---|---|
농담곰 | 10 | 나고야 | 곰 |
퍼그씨 | 31 | 나고야 | 퍼그 |
두더지고로케 | NULL | 오사카 | 고로케 |
🡆 df3.dropna(axis=1) 🡆
|이름|LOCATION|CATEGORY|
|--|--|--|
|농담곰|나고야|곰|
|퍼그씨|나고야|퍼그|
|두더지고로케|오사카|고로케|
→ 나이 컬럼이 아예 없어짐
# 결측치 제거4 - 전체 행이 결측값인 경우만 삭제하고 싶은 경우
# how='all'을 사용해줍니다.
df3.dropna(how='all')
이름 | 나이 | LOCATION | CATEGORY |
---|---|---|---|
NULL | NULL | NULL | NULL |
농담곰 | 10 | 나고야 | 곰 |
퍼그씨 | 31 | 나고야 | 퍼그 |
두더지고로케 | NULL | 오사카 | 고로케 |
🡆 df3.dropna() 🡆
이름 | 나이 | LOCATION | CATEGORY |
---|---|---|---|
농담곰 | 10 | 나고야 | 곰 |
퍼그씨 | 31 | 나고야 | 퍼그 |
두더지고로케 | NULL | 오사카 | 고로케 |
# 결측치 제거5 - 결측치 제거 후 결과를 바로 저장하고 싶을 때
# inplace=True 조건을 넣어줍니다.
df3.dropna(inplace=True)
# drop 이후 결측치가 잘 제거되었는지 체크가 필요하겠죠?
df3.isnull().sum()
# 결측치 대체: 최빈값
# mode 는 최빈값을 의미
# df3 의 Interaction type 컬럼을 fillna함수를 이용하여 채워주되, mode() 함수를 사용하여 최빈값으로 넣어줌
# mode 함수는 시리즈를 output으로 가집니다.
# 따라서,[0]을 통해 시리즈 중 단일값을 가져와야 합니다.
df3 = df3['Interaction type'].fillna(df3['Interaction type'].mode()[0])
# 결측치 대체: 평균값
df['sw'] = df['sw'].fillna(df['sw'].mean())
df.isnull().sum()
# 결측치 대체: 중간값
# inplace=True 로 하면 원본 데이터가 바뀌게 됩니다.
df['sw'] = df['sw'].fillna(df['sw'].median())
df.isnull().sum()
# 결측치 대체: 바로 위 값으로 대체
df['sw'] = df['sw'].fillna(method='ffill')
df.isnull().sum()
# 결측치 대체: 바로 아래 값으로 대체
df['sw'] = df['sw'].fillna(method='bfill')
#df.isnull().sum()
# 결측치 대체: group by 값으로 대체
# 사전 데이터 확인
df.groupby('Is Amazon Seller')['sw'].median()
df['sw'] = df['sw'].fillna(df.groupby('Is Amazon Seller')['sw'].transform('median'))
df.isnull().sum()
→ df['sw'].fillna(df.groupby(기준 컬럼)[계산할 컬럼].transform(계산 방식))
🡆 통용되는 몇 가지 방식을 사용하여 이상치 처리에 접근하기: 방식에 따라, 이상치의 개수가 달라질 수 있음
특히나 정답이 없는 데이터(비지도학습)를 가지고 데이터 분석을 진행할 때는 통계적 기법 + 데이터분석가의 주관이 적절히 조화를 이뤄야 합니다.
정규분포?
전국 학생 n명에 대한 과학시험 점수 데이터가 위와 같이 있다고 가정하고 이를 ‘점수가 나올 확률’에 대한 그래프로 그리게 되면 평균에서 가장 그 확률이 높고, 평균을 중심으로 멀어지면서 그 수가 적어지게 되는데요. 이와 같은 종 모양의 분포를 정규 분포라고 합니다.
→ 면적의 합은 1
# df 의 Shipping Weight를 기준으로 다양한 이상치 감지 기법을 적용해 보겠습니다.
df = pd.read_csv("p.csv")
# string -> float -> int
df['sw'] = df['Shipping Weight'].str.split().str[0]
df['sw'] = pd.to_numeric(df['sw'] , errors='coerce').fillna(0.0).astype(int)
# z-score 를 적용할 컬럼 선정
df1 = df[['sw']]
# 표준화 진행
# 표준화 : 평균을 0으로, 표준 편차를 1로
# 데이터를 0을 중심으로 양쪽으로 데이터를 분포시키는 방법
# 표준화를 하게 되면 각 데이터들은 평균을 기준으로 얼마나 떨여져 있는지를 나타내는 값으로 변환
scale_df = StandardScaler().fit_transform(df1)
merge_df = pd.concat([df1, pd.DataFrame(StandardScaler().fit_transform(df1))],axis=1)
merge_df.columns = ['Shipping Weight', 'zscore']
# 이상치 감지
# Z-SCORE 기반, -3 보다 작거나 3보다 큰 경우를 이상치로 판별
mask = ((merge_df['zscore']<-3) | (merge_df['zscore']>3))
# mask 메소드 사용
strange_df = merge_df[mask]
# 총 55 건 탐지
strange_df.count()
🡆 Box Plot!
: IQR를 그림으로 그린 것이 Box Plot → IQR 밖의 데이터 포인트는 이상치로 표시
# df 의 Shipping Weight를 기준으로 다양한 이상치 감지 기법을 적용해 보겠습니다.
df = pd.read_csv("p.csv")
# string -> float -> int
df['sw'] = df['Shipping Weight'].str.split().str[0]
df['sw'] = pd.to_numeric(df['sw'] , errors='coerce').fillna(0.0).astype(int)
# 이상치를 감지할 컬럼 선정
df1 = df[['sw']]
# Q3, Q1, IQR 값 구하기
# 백분위수를 구해주는 quantile 함수를 적용하여 쉽게 구할 수 있음
# 데이터프레임 전체 혹은 특정 열에 대하여 모두 적용이 가능
q3 = df1['sw'].quantile(0.75)
q1 = df1['sw'].quantile(0.25)
→ quantile(): 괄호 안의 숫자는 고정입니다.
iqr = q3 - q1
q3, q1, iqr
# 이상치 판별 및 dataframe 저장
# Q3 : 100개의 데이터로 가정 시, 25번째로 높은 값에 해당합니다.
# Q1 : 100개의 데이터로 가정 시, 75번째로 높은 값에 해당합니다.
# IQR : Q3 - Q1의 차이를 의미합니다.
# 이상치 : Q3 + 1.5 * IQR보다 높거나 Q1 - 1.5 * IQR보다 낮은 값을 의미
def is_outlier(df1):
score = df1['sw']
if score > 7 + (1.5 * 6) or score < 1 - (1.5 * 6):
return '이상치'
else:
return '이상치아님'
→ if문 조건도 고정입니다.
# apply 함수를 통하여 각 값의 이상치 여부를 찾고 새로운 열에 결과 저장
df1['이상치여부'] = df1.apply(is_outlier, axis = 1) # axis = 1 지정 필수
# IQR 방식으로 구한 이상치 개수는 349 개
df1.groupby('이상치여부').count()
※ 개념만 익혀두기
※ 개념만 익혀두기