Python Library/Pandas

[Pandas - Python] Handling Missing Data (누락된 데이터 처리)

바보1 2022. 6. 13. 23:29

1. Handling Missing Data - isnull()

 

데이터를 분석할 때, 데이터가 누락되는 것은 매우 흔한 일입니다.

Pandas에서는 누락된 데이터를 처리하는 것이 간단하고, 편합니다.

우선 어떤 데이터가 누락되었는지 알려주는 isnull() 함수를 사용하겠습니다.

import pandas as pd
import numpy as np

string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data

0     aardvark
1    artichoke
2          NaN
3      avocado
dtype: object
string_data.isnull()

0    False
1    False
2     True
3    False
dtype: bool

만약 데이터를 바꾼다면,

string_data[0] = None
string_data.isnull()

0     True
1    False
2     True
3    False
dtype: bool

isnull()이 제대로 된 값을 출력합니다.

 


2. Filtering Out Missing Data - dropna()

 

누락된 데이터를 처리하는 여러 방법이 있지만, 제일 간단한 건 그냥 없애는 방법입니다.

 

from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()

0    1.0
2    3.5
4    7.0
dtype: float64

dropna()는 아래의 방법과 같습니다.

data[data.notnull()]

0    1.0
2    3.5
4    7.0
dtype: float64

 

지금까지는 Series로 했지만, DataFrame을 기준으로 알아보겠습니다.

rows 혹은 columns를 기준으로 데이터를 없애고 싶을 때 어떻게 해야 하는지 알아보겠습니다.

 

아래의 예제를 보면 알 수 있지만, dropna()의 default는 any row containig a missing value입니다.

 

data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
                     [NA, NA, NA], [NA, 6.5, 3.]])
cleaned = data.dropna()
data

	0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
2	NaN	NaN	NaN
3	NaN	6.5	3.0

 

cleaned


	0	1	2
0	1.0	6.5	3.0

 

만약 any row가 아니라 all row로 바꾸고 싶다면, how = 'all'을 사용하면 됩니다.

(결측치가 하나라도 있는 행이 아닌, 모든 데이터가 결측치인 행)

data.dropna(how='all')

	0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
3	NaN	6.5	3.0

2번째 행만 사라진 것을 볼 수 있습니다.

 

data[4] = NA

data

	
	0	1	2	4
0	1.0	6.5	3.0	NaN
1	1.0	NaN	NaN	NaN
2	NaN	NaN	NaN	NaN
3	NaN	6.5	3.0	NaN

data의 4번 column을 추가하고, 모두 NaN으로 넣었습니다.

 

이때 row가 아닌, column을 기준으로 결측치를 제거하고 싶다면, axis = 1로 설정하면 됩니다.

data.dropna(axis=1, how='all')

	0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
2	NaN	NaN	NaN
3	NaN	6.5	3.0

column을 기준으로, 모든 데이터가 결측치인 열을 삭제했습니다.

 

새로운 DataFrame으로 다시 실험해보겠습니다.

df = pd.DataFrame(np.random.randn(7, 3))
df.iloc[:4, 1] = NA
df.iloc[:2, 2] = NA
df

	0		1		2
0	0.476985	NaN	NaN
1	-0.577087	NaN	NaN
2	0.523772	NaN	1.343810
3	-0.713544	NaN	-2.370232
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543
df.dropna()

	0		1		2
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543

결측치가 하나라도 있는 행이 모두 삭제 되었습니다.

 

만약에 난 결측치가 2개인 행만 삭제하고 싶다! 라면 thresh 파라미터를 넘기면 됩니다.

df.dropna(thresh=2)

	0		1		2
2	0.523772	NaN	1.343810
3	-0.713544	NaN	-2.370232
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543

3. Filling Missing Data - fillna()

 

fillna() 함수는 결측치를 채워주는 함수입니다.

위의 df를 재활용했습니다.

 

fillna(0)은 결측치를 모두 0으로 만듧니다.

df.fillna(0)


	0		1		2
0	0.476985	0.000000	0.000000
1	-0.577087	0.000000	0.000000
2	0.523772	0.000000	1.343810
3	-0.713544	0.000000	-2.370232
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543

 

만약 Column마다 다르게 데이터를 넣고 싶다면 딕셔너리 형태로 파라미터를 넣으면 됩니다.

df.fillna({1: 0.5, 2: 0})

	0		1		2
0	0.476985	0.500000	0.000000
1	-0.577087	0.500000	0.000000
2	0.523772	0.500000	1.343810
3	-0.713544	0.500000	-2.370232
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543

 

만약 즉시 df를 업데이트하고 싶다면, inplace = True를 하면 됩니다.

_ = df.fillna(0, inplace=True)
df


	0		1		2
0	0.476985	0.000000	0.000000
1	-0.577087	0.000000	0.000000
2	0.523772	0.000000	1.343810
3	-0.713544	0.000000	-2.370232
4	-1.860761	-0.860757	0.560145
5	-1.265934	0.119827	-1.063512
6	0.332883	-2.359419	-0.199543

 

다시 새로운 DataFrame으로 테스트하겠습니다.

 

df = pd.DataFrame(np.random.randn(6, 3))
df.iloc[2:, 1] = NA
df.iloc[4:, 2] = NA
df

	0		1		2
0	-1.541996	-0.970736	-1.307030
1	0.286350	0.377984	-0.753887
2	0.331286	NaN	0.069877
3	0.246674	NaN	1.004812
4	1.327195	NaN	NaN
5	0.022185	NaN	NaN

2행 이후, 1열을 모두 NaN, 4행 이후, 2열을 모두 NaN 처리했습니다.

 

이때 method = 'ffill'을 사용하면, 위의 데이터와 똑같이 집어넣습니다.

df.fillna(method='ffill')

	0		1		2
0	-1.541996	-0.970736	-1.307030
1	0.286350	0.377984	-0.753887
2	0.331286	0.377984	0.069877
3	0.246674	0.377984	1.004812
4	1.327195	0.377984	1.004812
5	0.022185	0.377984	1.004812

 

이때도 limit = 2를 하면, 최대 2개까지만 보간합니다.

df.fillna(method='ffill', limit=2)

	0		1		2
0	-1.541996	-0.970736	-1.307030
1	0.286350	0.377984	-0.753887
2	0.331286	0.377984	0.069877
3	0.246674	0.377984	1.004812
4	1.327195	NaN	1.004812
5	0.022185	NaN	1.004812

 

이렇게 기존에 있는 값으로 채우는 것이 싫다면, 평균을 이용하여 채워넣는 방법도 있습니다.

data.mean() 값을 넣어주면 됩니다.

data = pd.Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean())

0    1.000000
1    3.833333
2    3.500000
3    3.833333
4    7.000000
dtype: float64

이렇게 되면 데이터의 평균값으로 채워 넣게 됩니다.


4. Conclusion

 

isnull() - 어떤 데이터가 결측 되었는지 True False로 알려줌

dropna() - 기본 설정은 행의 어떤 데이터라도 결측 되었으면 없앰

    how = 'all', axis = 1, thresh = 2 파라미터로 조절 가능

fillna() - 안에 들어가는 숫자로 결측치를 채움

    딕셔너리 형태로 넣으면 key에 해당하는 column을 value로 바꿈

    method = 'ffill'로 하면 위에 있는 값들로 채워 넣음