데이터 결측치 처리 – 결측치 처리방법

데이터 결측치 처리 – 결측치 처리방법 에 대해서 알아보겠습니다.

결측치 처리 전 결측치를 확인하는 방법은 아래 글에 있으니 참조하시기 바랍니다.

1. 데이터 결측치 처리 - 결측치란 무엇인가
2. 데이터 결측치 처리 - 결측치 확인 방법

3. 데이터 결측치 처리 – 결측치 처리 방법

결측치를 처리하는 방법은 5가지가 있습니다. 아래 5가지 방법을 알아보겠습니다.

1. 임의의 값으로 채우기
2. 앞의 값이나 뒤의 값으로 채우기
3. 보간법을 사용하기
4. 누락값이 있는 행이나 열을 삭제하기
5. 머신러닝으로 결측치를 예측하는 방법

이런 결측치를 처리하여 데이터를 알차게 만드는 방법을 “데이터 정제(Data Cleansing)”라고 합니다.

데이터 정제(Data Cleansing)은 데이터 분석 목적에 부합하는 데이터 품질을 확보하기 위한 데이터 작업을 의미합니다. 이런 작업을 하는 이유는 데이터 분석 작업에 소요되는 시간의 약 80% 정도가 데이터 수집과 전처리 작업이기 때문입니다.

3.1 데이터 결측치 처리 – 임의의 값으로 채우기

데이터 가 큰 경우에 많이 사용하는 방법입니다.특정 칼럼에 대해서 임의의 값을 채웁니다.

모든 칼럼에 임의의 값을 채우고자 할 경우는 칼럼을 지정하지 않고 사용합니다.

  • 문법
    • 임의의 칼럼에 적용: df[column].fillna(임의의 값)
    • 전체 칼럼에 적용 : df.fillna(임의의 값)
    • 예.
  • age 칼럼을 0 으로 채우기
# 채우기 전

print(df['age'].isnull())
print('-- null ', df['age'].isnull().sum(), ' 개')
채우기 전
# 채우기 후

print(df['age'].fillna(0))
print('-- null ', df['age'].fillna(0).isnull().sum(), ' 개')
채우기 후
  • 888번 레코드가 0 으로 바뀌었고, null 건수가 0건이 되었습니다.

  • 이번에는 평균 나이로 채워 보겠습니다.
# age 평균은 29.69911764705882 이므로 소수점 아래 1자리에서 반올림하였

print(df['age'].mean())
print(round(df['age'].mean(), 1))            # 평균나이를 소수점 한자리까지

df['age'].fillna(round(df['age'].mean(),1), inplace=True)
print('-- null ', df['age'].fillna(0).isnull().sum(), ' 개')
age 평균

==> null은 처리가 되어 0 건이 나옴

  • 기존 데이터가 잘 변경 되었는지 확인
# 데이터 확인하기 : 888번의 값이 29.7로 변경 되었습니다.

print(df['age'].tail())
데이터 확인하기

==> 평균나이로 잘 바뀌어 있습니다.

  • 이번에는 문자를 채워보겠습니다. 문자는 숫자 칼럼이 아닌 문자 칼럼에 처리를 해야겠죠.
  • 문자열 칼럼의 dtype은 object 입니다.
# null 건수와 위치 index를 알아봅니다.

print('embark_town null count:', df['embark_town'].isnull().sum())
print('--- embark_town null index ---')
print(df[df['embark_town'].isnull()]['embark_town'])
null 건수와 위치 index 알아보기

==> 2개가 NaN 입니다. null 2건 있으며, 61, 829 Index 가 결측치 입니다.

  • 결측치에 ‘Southampton’ 으로 채우겠습니다.
# -- 'embark_town' 각 값의 개수 확인하기
print('--- embark_town의 각 값의 개수 확인 ---')
print(df['embark_town'].value_counts(dropna=False))

# -- 'embark_town'의 null에 'Southampton' 채우기
df['embark_town'].fillna('Southampton', inplace=True)

# -- 'embark_town' null 개수 확인하기
print('embark_town null count:', df['embark_town'].isnull().sum())

# 62, 829 인덱스 'embark_town'값 확인하기
df.loc[[61, 829]]['embark_town']
각 값의 개수 확인과 결과 확인하기

==> NaN이 2건 있었으며, 그 2건의 값을 Southampton 으로 저장하였음

위와 같이 직접적인 값이나, 평균, 중간값 등의 통계값을 적용할 수도 있습니다.

3.2 데이터 결측치 처리 – 앞의 값이나 뒤의 값으로 채우기

  • 앞과 같이 fillna() 함수에 옵션으로 method를 추가하면 됩니다.
  • 메소드 ‘ffill’은 앞의 값으로, ‘bfill’은 뒤의 값으로 채워집니다.

  • age 칼럼의 결측 데이터 확인하기
# 결측치가 있는 age 칼럼의 위치를 확인
df.loc[190:200]['age']
age 칼럼의 결측 데이터 확인

==> 196, 198 위치에 결측치가 있음

  • method = ‘ffill’ 적용해 보기
# method='ffill'로 값을 채우기
df['age'].fillna(method='ffill').loc[190:200]
method='ffill'로 값을 채우기

==> 196,198 행이 앞의 값을 그대로 가지고 있다.

  • method = ‘ffill’ 적용하기
# method='bfill'로 값을 채우기
df['age'].fillna(method='bfill').loc[190:200]
method = 'ffill' 적용하기

==> 뒤의 값을 받아와서 적용되었음

  • ffill 과 bfill 사용 시 고려할 사항
    • ffill의 경우 1행이 NaN 이면 참조할 값이 없기에 적용이 안됨
    • 반면 bfill의 경우 마지막 행이 NaN 이면 참조할 값이 없기에 적용이 안됨

3.3 데이터 결측치 처리 – 보간법을 사용하기

  • 누락값의 양쪽에 있는 값의 중간값을 결측치로 채우는 방법
  • 수치형 데이터에만 적용할 수 있음
  • 1번 행이나 마지막 행이 NaN일 경우 중간값을 구하지 못하므로 ‘NaN’으로 결과가 나옴

# 결측치가 있는 age 칼럼의 위치를 확인
df.loc[40:50][['sex', 'age']]
결측치가 있는 age 칼럼의 위치를 확인

==> 40~50 사이 데이터에서 결측을 확인함

  • 결측치를 보간법을 적용하여 값을 배정함
# 보간법 적용
df['age'].interpolate().loc[40:50]
보간법 적용

==> 결측치인 42행은 앞뒤 값의 가운데 값이 적용되었으며,
45~48 행은 44행과 49행의 값 사이를 분할하여 적용되었음

3.4 데이터 결측치 처리 – 누락값이 있는 행이나 열을 삭제하기

  • dropna() 메소드를 사용하는데, 특정 칼럼에 대해서 사용은 불가능 (무조건 전체 행이 기준임)
  • 옵션 how로 조절하며 다음 2가지 있음
    • how = ‘any’ : 하나의 셀이라고 NaN 이 있다면 그 행을 삭제
    • how = ‘all’ : 행 전체가 NaN 이라면 그 행을 삭제
  • 이 삭제 방법은 해당하는 행을 삭제하므로 데이터 수에 영향을 주게 된다.
    • 결측치가 많으면 활용할 데이터가 급격히 적어지거나
    • 데이터가 줄어들면서 편향된 데이터가 될 수도 있음
  • 결측치가 많아 데이터가 급격히 줄어 들 경우는 이 방법이 답이 아닐 수 있다.

  • 데이터 건수 확인하기
# df 건수 확인, deck 칼럼의 결측치 수 확인
print('df전체 건수: ', df.shape)
print('desk 칼럼의 결측치 수: ', df['deck'].isnull().sum())

==> 전체 891 행이 있고, 칼럼 ‘deck’의 결측치가 688건임

  • how = ‘any’를 적용한 후 건수 확인하기
# how = 'any'를 적용한 후 건수 확인하기
print(df.dropna(how='any').shape)

==> 182 으로 축소되었음. 이는 칼럼 ‘deck’에 결측치가 688건이 있었기 때문임

  • how = ‘all’를 적용한 후 건수 확인하기
# how = 'all'를 적용한 후 건수 확인하기
print(df.dropna(how='all').shape)

==> 891 행 그대로 유지되었음

  • 전체 칼럼에 null 이 있는 경우는 아주 드문 경우로 위와 같은 경우의 발생은 거의 없음
  • dropna()는 디폴트로 행을 기준으로 삭제합니다. 옵션에 ‘axis=1’을 넣으면 칼럼을 기준으로 삭제가 이루어 지게 됩니다.

3.5 머신러닝으로 결측치를 예측하는 방법

–> 이 부분은 다른 칼럼의 값을 응용하는 방법입니다. 별도 포스팅 하겠습니다.

4. 마무리

데이터 분석 작업을 해보면 결측치가 자주 있습니다. 일반적인 방법으로 평균이나 중앙값 또는 임시값을 적용하거나 삭제를 합니다.

그런 데이터 보정이 전체 데이터의 균형을 잃게 하는 경우가 있습니다.

예를 들어 칼럼 10개 있는 1만건의 데이터를 가정해 보겠습니다. 칼럼 3개에서 각각 10개, 200개, 1500개의 결측치가 있습니다. 10건의 결측치가 있는 칼럼은 전체에서 0.1% 이기에 분석 결과에 영향을 적습니다. 하지만 1500건이 있는 칼럼을 분석하게 되면 15%라는 결측 규모에 영향을 받게 됩니다.

결측치는 단순히 평균이나 중앙값, 임의값으로 적용하기 보다는 다른 칼럼과의 관계를 보면서 가장 근접한 값을 적용해 주는 노력이 필요합니다.

age 의 경운 평균 나이 29.7세 보다는 who, adult_male, fare등 다른 속성간의 관계로 추정하면 보다 정확한 나이를 얻을 수 있습니다.

who 칼럼에는 ‘man, woman, child’값이 있습니다. 만약 who=’child’ 인데 age칼럼이 결측치라고 하여 평균 나이인 29.7 세를 적용한다면 오류를 가지고 있는 상황이 될 것입니다. 그것보다는 who=’child’ 인 데이터의 age 평균을 적용하는 게 더 정확할 것입니다.

이렇게 데이터의 결측치 처리는 단일 칼럼만을 생각하기 보다는 복합적으로 분석하여 적용하는 노력이 필요합니다.

이렇게 데이터의 결측치를 확인하고 처리하는 방법을 알아봤습니다. 기본적인 사항이지만 응용하여 적용한다면 좋은 결과를 볼 수 있을 것입니다.

>> 같이 보기

(끝)

댓글 남기기