Post

[사전학습] 3.1 결측 데이터 처리

3. 데이터 전처리 이해와 실무

3.1 데이터 정제 : 결측 데이터 처리

결측치(Missing Value)

  • 데이터가 수집되지 않거나 누락되어 정보(값)가 필요하지 않음을 의미
    => 모델 훈련을 위해 결측치 처리 필요

    결측치 발생 원인

    대부분 수집 및 관리 과정에서 결측치 발생

  • 미수집 : 미 입력된 데이터를 수집 및 저장
  • 시스템 오류 : 오류에 의해 누락되어 수집 및 저장
  • 신규 항목 : 새롭게 수집되는 항목

    결측치 처리 방안

  • 제거하기 : 가장 쉬운 방안, 엄청난 데이터 손실 발생
  • 대체하기 : 최대한 많은 데이터 활용, 편향(Bias) 발생 가능

결측치 다루기

  1. 결측치 제거하기
    • Listwise : 결측치가 존재하는 행 삭제
    • Pairwise : 모든 변수가 결측치로만 존재하는 행 삭제
  2. 결측치 대체하기
    • 정보의 손실을 방지하거나 변수 특성(평균, 상관관계 등)에 영향 발생
    • 일정 값 대체 : 결측치를 각 변수의 평균값으로 대체
    • 선형 값 대체 : 선형 함수 기반 앞 뒤 관측치 활용 대체

실습

데이터 불러오기

1
2
import numpy as np
import pandas as pd
1
2
cancer = pd.read_csv("./data/wdbc.data", header=None)
cancer
0123456789...22232425262728293031
0842302M17.9910.38122.801001.00.118400.277600.300100.14710...25.38017.33184.602019.00.162200.665600.71190.26540.46010.11890
1842517M20.5717.77132.901326.00.084740.078640.086900.07017...24.99023.41158.801956.00.123800.186600.24160.18600.27500.08902
284300903M19.6921.25130.001203.00.109600.159900.197400.12790...23.57025.53152.501709.00.144400.424500.45040.24300.36130.08758
384348301M11.4220.3877.58386.10.142500.283900.241400.10520...14.91026.5098.87567.70.209800.866300.68690.25750.66380.17300
484358402M20.2914.34135.101297.00.100300.132800.198000.10430...22.54016.67152.201575.00.137400.205000.40000.16250.23640.07678
..................................................................
564926424M21.5622.39142.001479.00.111000.115900.243900.13890...25.45026.40166.102027.00.141000.211300.41070.22160.20600.07115
565926682M20.1328.25131.201261.00.097800.103400.144000.09791...23.69038.25155.001731.00.116600.192200.32150.16280.25720.06637
566926954M16.6028.08108.30858.10.084550.102300.092510.05302...18.98034.12126.701124.00.113900.309400.34030.14180.22180.07820
567927241M20.6029.33140.101265.00.117800.277000.351400.15200...25.74039.42184.601821.00.165000.868100.93870.26500.40870.12400
56892751B7.7624.5447.92181.00.052630.043620.000000.00000...9.45630.3759.16268.60.089960.064440.00000.00000.28710.07039

569 rows × 32 columns

  • 링크 에서 다운로드 가능
1
2
3
4
5
6
7
8
9
10
11
12
# 데이터 컬럼명 지정
cancer.columns = [
    "id", "diagnosis", "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean", "compactness_mean", 
    "concavity_mean", "concave_points_mean", "symmetry_mean", "fractal_dimension_mean", "radius_se", "texture_se",
    "perimeter_se", "texture_worst", "smoothness_se", "compactness_se", "concavity_se", "concave_points_se", "symmetry_se",
    "fractal_dimension_se", "radius_worst", "texture_worst", "perimeter_worst", "area_worst", "smoothness_worst", "compactness_worst", 
    "concavity_worst", "concave_points_worst", "symmetry_worst", "fractal_dimension_worst"
]

# ID를 Index화
cancer = cancer.set_index('id')
cancer
diagnosisradius_meantexture_meanperimeter_meanarea_meansmoothness_meancompactness_meanconcavity_meanconcave_points_meansymmetry_mean...radius_worsttexture_worstperimeter_worstarea_worstsmoothness_worstcompactness_worstconcavity_worstconcave_points_worstsymmetry_worstfractal_dimension_worst
id
842302M17.9910.38122.801001.00.118400.277600.300100.147100.2419...25.38017.33184.602019.00.162200.665600.71190.26540.46010.11890
842517M20.5717.77132.901326.00.084740.078640.086900.070170.1812...24.99023.41158.801956.00.123800.186600.24160.18600.27500.08902
84300903M19.6921.25130.001203.00.109600.159900.197400.127900.2069...23.57025.53152.501709.00.144400.424500.45040.24300.36130.08758
84348301M11.4220.3877.58386.10.142500.283900.241400.105200.2597...14.91026.5098.87567.70.209800.866300.68690.25750.66380.17300
84358402M20.2914.34135.101297.00.100300.132800.198000.104300.1809...22.54016.67152.201575.00.137400.205000.40000.16250.23640.07678
..................................................................
926424M21.5622.39142.001479.00.111000.115900.243900.138900.1726...25.45026.40166.102027.00.141000.211300.41070.22160.20600.07115
926682M20.1328.25131.201261.00.097800.103400.144000.097910.1752...23.69038.25155.001731.00.116600.192200.32150.16280.25720.06637
926954M16.6028.08108.30858.10.084550.102300.092510.053020.1590...18.98034.12126.701124.00.113900.309400.34030.14180.22180.07820
927241M20.6029.33140.101265.00.117800.277000.351400.152000.2397...25.74039.42184.601821.00.165000.868100.93870.26500.40870.12400
92751B7.7624.5447.92181.00.052630.043620.000000.000000.1587...9.45630.3759.16268.60.089960.064440.00000.00000.28710.07039

569 rows × 31 columns

결측치 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 데이터 복사
cancer_data = cancer.copy()

# 데이터 내 결측치 생성
# 실습을 위한 일부 데이터 선택
cancer_data = cancer_data[0:30]
cancer_data = cancer_data[['diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean']]

# 결측치 생성
# 6개 record 내 결측치 생성
cancer_data.iloc[2, :] = np.nan     # 3행 내 모든 데이터 결측치 생성
cancer_data.iloc[5, 0] = np.nan     # 6행 내 1열 데이터 결측치 생성
cancer_data.iloc[10, [3, 4]] = np.nan   # 11행 내 4, 5열 데이터 결측치 생성
cancer_data.iloc[12, 2:4] = np.nan   # 13행 내 3, 4열 데이터 결측치 생성
cancer_data.iloc[15, [0, 3]] = np.nan   # 16행 내 1, 4열 데이터 결측치 생성
cancer_data.iloc[24, 4] = np.nan   # 25행 내 5열 데이터 결측치 생성

cancer_data
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.99010.38122.801001.0
842517M20.57017.77132.901326.0
84300903NaNNaNNaNNaNNaN
84348301M11.42020.3877.58386.1
84358402M20.29014.34135.101297.0
843786NaN12.45015.7082.57477.1
844359M18.25019.98119.601040.0
84458202M13.71020.8390.20577.9
844981M13.00021.8287.50519.8
84501001M12.46024.0483.97475.9
845636M16.02023.24NaNNaN
84610002M15.78017.89103.60781.0
846226M19.170NaNNaN1123.0
846381M15.85023.95103.70782.7
84667401M13.73022.6193.60578.3
84799002NaN14.54027.54NaN658.8
848406M14.68020.1394.74684.5
84862001M16.13020.68108.10798.8
849014M19.81022.15130.001260.0
8510426B13.54014.3687.46566.3
8510653B13.08015.7185.63520.0
8510824B9.50412.4460.34273.9
8511133M15.34014.26102.50704.4
851509M21.16023.04137.201404.0
852552M16.65021.38110.00NaN
852631M17.14016.40116.00912.7
852763M14.58021.5397.41644.8
852781M18.61020.25122.101094.0
852973M15.30025.27102.40732.4
853201M17.57015.05115.00955.1

결측치 제거하기

  • 결측치 제거 방안
    1. listwise deletion : 데이터 내 1개 변수 값에서 N/A(결측)이 존재하는 경우, 해당 행 제거
    2. pairwise deletion : 모든 변수가 N/A(결측)이 존재하는 경우, 해당 행 제거
  • 결측치 제거 시, 온전한 데이터를 사용한다는 관점은 적용 가능하나 데이터 손실이 발생함

    listwise

1
2
3
# 데이터 개요
cancer_data.info()
# 총 6개 record에서 결측치 존재
1
2
3
4
5
6
7
8
9
10
11
12
<class 'pandas.core.frame.DataFrame'>
Int64Index: 30 entries, 842302 to 853201
Data columns (total 5 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   diagnosis       27 non-null     object 
 1   radius_mean     29 non-null     float64
 2   texture_mean    28 non-null     float64
 3   perimeter_mean  26 non-null     float64
 4   area_mean       27 non-null     float64
dtypes: float64(4), object(1)
memory usage: 1.4+ KB
1
2
3
4
5
6
7
8
9
10
# listwise deletion 수행
# 30개 record 중, 6개 record에서 결측치 존재함
cancer_copy = cancer_data.copy()
cancer_copy = cancer_copy.dropna()

# 데이터 요약 : 총 30개 record 중, 하나의 결축치라도 보유한 6개 record 삭제
print(cancer_copy.info())

# 데이터 차원 확인
print("데이터 차원 :", np.shape(cancer_copy))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<class 'pandas.core.frame.DataFrame'>
Int64Index: 24 entries, 842302 to 853201
Data columns (total 5 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   diagnosis       24 non-null     object 
 1   radius_mean     24 non-null     float64
 2   texture_mean    24 non-null     float64
 3   perimeter_mean  24 non-null     float64
 4   area_mean       24 non-null     float64
dtypes: float64(4), object(1)
memory usage: 1.1+ KB
None
데이터 차원 : (24, 5)
1
cancer_copy
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.99010.38122.801001.0
842517M20.57017.77132.901326.0
84348301M11.42020.3877.58386.1
84358402M20.29014.34135.101297.0
844359M18.25019.98119.601040.0
84458202M13.71020.8390.20577.9
844981M13.00021.8287.50519.8
84501001M12.46024.0483.97475.9
84610002M15.78017.89103.60781.0
846381M15.85023.95103.70782.7
84667401M13.73022.6193.60578.3
848406M14.68020.1394.74684.5
84862001M16.13020.68108.10798.8
849014M19.81022.15130.001260.0
8510426B13.54014.3687.46566.3
8510653B13.08015.7185.63520.0
8510824B9.50412.4460.34273.9
8511133M15.34014.26102.50704.4
851509M21.16023.04137.201404.0
852631M17.14016.40116.00912.7
852763M14.58021.5397.41644.8
852781M18.61020.25122.101094.0
852973M15.30025.27102.40732.4
853201M17.57015.05115.00955.1

pairwise

1
2
3
4
5
6
7
8
9
10
11
# pairwise deletion 수행
# 30개 record 중, 1개 record에서 모든 변수 내 결측치 존재
# 모든 결측치 존재 record만 삭제
cancer_copy = cancer_data.copy()
cancer_copy = cancer_copy.dropna(how='all')

# 데이터 요약 : 총 30개 record 중, 1개 record 삭제
print(cancer_copy.info())

# 데이터 차원 확인
print("데이터 차원 :", np.shape(cancer_copy))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<class 'pandas.core.frame.DataFrame'>
Int64Index: 29 entries, 842302 to 853201
Data columns (total 5 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   diagnosis       27 non-null     object 
 1   radius_mean     29 non-null     float64
 2   texture_mean    28 non-null     float64
 3   perimeter_mean  26 non-null     float64
 4   area_mean       27 non-null     float64
dtypes: float64(4), object(1)
memory usage: 1.4+ KB
None
데이터 차원 : (29, 5)
1
cancer_copy
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.99010.38122.801001.0
842517M20.57017.77132.901326.0
84348301M11.42020.3877.58386.1
84358402M20.29014.34135.101297.0
843786NaN12.45015.7082.57477.1
844359M18.25019.98119.601040.0
84458202M13.71020.8390.20577.9
844981M13.00021.8287.50519.8
84501001M12.46024.0483.97475.9
845636M16.02023.24NaNNaN
84610002M15.78017.89103.60781.0
846226M19.170NaNNaN1123.0
846381M15.85023.95103.70782.7
84667401M13.73022.6193.60578.3
84799002NaN14.54027.54NaN658.8
848406M14.68020.1394.74684.5
84862001M16.13020.68108.10798.8
849014M19.81022.15130.001260.0
8510426B13.54014.3687.46566.3
8510653B13.08015.7185.63520.0
8510824B9.50412.4460.34273.9
8511133M15.34014.26102.50704.4
851509M21.16023.04137.201404.0
852552M16.65021.38110.00NaN
852631M17.14016.40116.00912.7
852763M14.58021.5397.41644.8
852781M18.61020.25122.101094.0
852973M15.30025.27102.40732.4
853201M17.57015.05115.00955.1

결측치 대체하기

  • 결측치 대체 방안
    1. 일정 값 대체 : 결측치를 사전 지정 값으로 대체
    2. 선형 값 대체 : 선형 함수 기반, 앞 뒤 record 값을 활용하여 대체
  • 결측치 대체 시, 가능한 많은 데이터를 사용할 수 있다는 관점에서 유용하나, 실 데이터와의 차이가 존재할 수 있음
1
2
3
# 결측치 데이터 확인
cancer_copy = cancer_data.copy()
cancer_copy
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.99010.38122.801001.0
842517M20.57017.77132.901326.0
84300903NaNNaNNaNNaNNaN
84348301M11.42020.3877.58386.1
84358402M20.29014.34135.101297.0
843786NaN12.45015.7082.57477.1
844359M18.25019.98119.601040.0
84458202M13.71020.8390.20577.9
844981M13.00021.8287.50519.8
84501001M12.46024.0483.97475.9
845636M16.02023.24NaNNaN
84610002M15.78017.89103.60781.0
846226M19.170NaNNaN1123.0
846381M15.85023.95103.70782.7
84667401M13.73022.6193.60578.3
84799002NaN14.54027.54NaN658.8
848406M14.68020.1394.74684.5
84862001M16.13020.68108.10798.8
849014M19.81022.15130.001260.0
8510426B13.54014.3687.46566.3
8510653B13.08015.7185.63520.0
8510824B9.50412.4460.34273.9
8511133M15.34014.26102.50704.4
851509M21.16023.04137.201404.0
852552M16.65021.38110.00NaN
852631M17.14016.40116.00912.7
852763M14.58021.5397.41644.8
852781M18.61020.25122.101094.0
852973M15.30025.27102.40732.4
853201M17.57015.05115.00955.1

일정 값 대체

범주형 데이터
1
2
3
# diagnosis 컬럼 내 결측치는 C라는 범주형 값 일괄 대체
cancer_copy['diagnosis'] = cancer_copy['diagnosis'].fillna('C')
cancer_copy.head(10)
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.9910.38122.801001.0
842517M20.5717.77132.901326.0
84300903CNaNNaNNaNNaN
84348301M11.4220.3877.58386.1
84358402M20.2914.34135.101297.0
843786C12.4515.7082.57477.1
844359M18.2519.98119.601040.0
84458202M13.7120.8390.20577.9
844981M13.0021.8287.50519.8
84501001M12.4624.0483.97475.9
수치형 데이터
1
2
3
# 수치형 컬럼인 radius_mean 컬럼 내 결측치는 65라는 수치의 일정 값으로 대체
cancer_copy['radius_mean'] = cancer_copy['radius_mean'].fillna(65)
cancer_copy.head(10)
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.9910.38122.801001.0
842517M20.5717.77132.901326.0
84300903C65.00NaNNaNNaN
84348301M11.4220.3877.58386.1
84358402M20.2914.34135.101297.0
843786C12.4515.7082.57477.1
844359M18.2519.98119.601040.0
84458202M13.7120.8390.20577.9
844981M13.0021.8287.50519.8
84501001M12.4624.0483.97475.9
1
2
# 데이터 개요 확인
cancer_copy.info()
1
2
3
4
5
6
7
8
9
10
11
12
<class 'pandas.core.frame.DataFrame'>
Int64Index: 30 entries, 842302 to 853201
Data columns (total 5 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   diagnosis       30 non-null     object 
 1   radius_mean     30 non-null     float64
 2   texture_mean    28 non-null     float64
 3   perimeter_mean  26 non-null     float64
 4   area_mean       27 non-null     float64
dtypes: float64(4), object(1)
memory usage: 1.4+ KB
평균 값 대체
1
2
3
4
5
6
7
8
9
10
11
# 일정 값이 아닌, 컬럼의 평균으로 대체(평균, 중앙, 최소, 최대값 등으로 대체 가능)
# texture_mean 컬럼 내 결측치를 texture_mean 평균 값으로 대체
cancer_copy['texture_mean'] = cancer_copy['texture_mean'].replace(np.nan, cancer_copy['texture_mean'].mean())

## 동일 결과
## cancer_copy['texture_mean'] = cancer_copy['texture_mean'].fillna(cancer_copy['texture_mean'].mean())

# 대체된 값과 texture_mean 컬럼의 평균값 비교
# 3번째 record, id 84300903 확인
print(cancer_copy['texture_mean'].mean())
cancer_copy.head(10)
1
19.397142857142853
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.9910.380000122.801001.0
842517M20.5717.770000132.901326.0
84300903C65.0019.397143NaNNaN
84348301M11.4220.38000077.58386.1
84358402M20.2914.340000135.101297.0
843786C12.4515.70000082.57477.1
844359M18.2519.980000119.601040.0
84458202M13.7120.83000090.20577.9
844981M13.0021.82000087.50519.8
84501001M12.4624.04000083.97475.9
선형 값 대체
  • 데이터 앞 뒤 record 값을 기반으로 결측치 대체 (선형보간법)
  • 선형 값 대체의 경우 데이터의 연속성을 기반으로 연산되므로 신중히 사용
1
2
3
# 데이터의 선형관계를 기반 대체
cancer_copy = cancer_data.copy()
cancer_copy.head()
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.9910.38122.801001.0
842517M20.5717.77132.901326.0
84300903NaNNaNNaNNaNNaN
84348301M11.4220.3877.58386.1
84358402M20.2914.34135.101297.0
1
2
3
# 선형보간법
cancer_copy = cancer_data.interpolate()
cancer_copy.head()
diagnosisradius_meantexture_meanperimeter_meanarea_mean
id
842302M17.99010.380122.801001.00
842517M20.57017.770132.901326.00
84300903NaN15.99519.075105.24856.05
84348301M11.42020.38077.58386.10
84358402M20.29014.340135.101297.00
1
print((cancer_data.iloc[1, 1] + cancer_data.iloc[3, 1]) / 2)
1
15.995000000000001
1
1
This post is licensed under CC BY 4.0 by the author.

[사전학습] 2.5 시계열 기초

[사전학습] 3.2 이상 데이터 처리