이상치: 명확한 기준은 없지만 일반적인 데이터들과 달리 매우 크거나 작은 값
결측치: 데이터 수집 과정에서 측정되지 않거나 누락된 데이터
결측치 처리에는 두가지 방법이 있다.
제거 or 대체. 그렇다면 제거하는 것이 좋을까? 대체하는 것이 좋을까?
결측치를 갖고 있는 데이터는 일반적으로 제거하고 분석하는 것이 좋다. 다른 값으로 함부로 대체한다면 데이터 왜곡으로 다른 분석 결과가 나올 수 있기에 제거하는 것이 가장 바람직하다.
결측치 확인: isnull()
df3.isnull().sum()

열 제거하기: drop()
df.drop('컬럼명', axis=1)
결측치가 있는 행, 열 제거: dropna()
# 결측치가 있는 행 모두 제거
# axis=0 이 기본값.
df.dropna(axis=0, how='any') # 행 전체가 결측인 경우로 하려면 how='all'
# 결측치가 있는 열 모두 제거
df.dropna(axis=1)
# 결측치 제거 후 바로 저장
df.dropna(inplace=True)
df['sw'].fillna(method='bfill')mode()를 사용하기 전에 1차적으로 최빈값을 검증해 볼 수 있다.
컬럼을 groupby해서 개수를 세는 것이다. 이때 결측치는 카운트되지 않는다.df3.groupby('Interaction type').count()
fillna()를 통해 Interaction type 컬럼의 최반값으로 null 값을 채우면 결과는 아래와 같다.df3 = df3['Interaction type'].fillna(df3['Interaction type'].mode()[0])
그런데 못생기지 않았는가?
여기에서 조금 더 보기 편하게 보려면 reset_index()를 사용하면 된다. 나는 reset_index()를 보통 데이터프레임 수정 후 다시 제대로 정렬할 때만 사용했는데 시리즈 형태의 결과를 보기 편하게 보려고 활용 하기도 하는 것 같다.

마지막 데이터 프레임 확인

데이터를 다루다보면 Series 형태의 데이터를 만나게 된다. 이때 str을 통해서 각 행의 데이터를 조금 더 손쉽게 다룰 수 있는 방법이 있다.
결측치 평균 값으로 대체하기
1) 데이터 타입 변환:
.str.split()과.to_numeric()사용pandas series는 split()을 바로 사용할 수가 없어 str을 통해 전체 series에 접근할 수 있게함.
df['Shipping Weight'].str.split().str[0]
str.split()를 통해 행별로 문자열을 공백 기준으로 나눈 리스트를 추출하고.str[0]을 통해 각 리스트의 0번째 값을 가져옴.# string to float, 에러무시 df['sw'] = pd.to_numeric(df['sw'] , errors='coerce')2) 평균값으로 대체
df['sw'] = df['sw'].fillna(df['sw'].mean())
groupby()와 transform()을 활용한 대체
transform()은 각 그룹에 연산을 적용한 뒤에 원래 행의 길이와 동일한 개수로 결과를 돌려주는 기능을 한다.
아래 이미지를 보면 'Is Amazon Seller'라는 컬럼은 'Y'와 'N'이라는 두 종류의 데이터가 있다.
Is Amazon Seller로 그룹을 나누고 각 그룹의 sw 컬럼의 중앙값을 구한다. 그리고 그 값들을 각 그룹에 그대로 뿌려줘그룹별로 중앙값을 한번에 구하고 적용할 수 있다.# Is Amazon Seller 를 기준으로 중앙값 계산하여 대체 df['sw'] = df['sw'].fillna(df.groupby('Is Amazon Seller')['sw'].transform('median'))
정답이 없는(비지도 학습) 경우에는 계산에 의한 이상치만 갖고 이상치로 판단하는 것이 아니라 분석가의 주관적인 조건과 함께 결합해서 이상치를 판단하는 것이 필수다.
from sklearn.preprocessing import StandardScaler # z-score 를 적용할 컬럼 선정 df1 = df[['sw']] scale_df = StandardScaler().fit_transform(df1)
백분위 수를 구하는 quantile() 함수를 통해 쉽게 구할 수 있으며 데이터프레임 전체에 대해서 구하면 각 열에 대한 값이 나오며 특정 열만 슬라이싱해서 구할수도 있다.
q3 = df1['sw'].quantile(0.75)
q1 = df1['sw'].quantile(0.25)
iqr = q3 - q1
q3, q1, iqr
이러한 형태로 사용할 수 있음.
# 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 '이상치아님'
# apply 함수를 통하여 각 값의 이상치 여부를 찾고 새로운 열에 결과 저장
df1['이상치여부'] = df1.apply(is_outlier, axis = 1) # axis = 1 지정 필수
