파이썬

Python으로 결측치 확인 후 보간(내삽)하기

이석사 중 2024. 1. 24. 17:09
728x90

오늘은 파이썬으로 결측치의 개수를 확인하고 내삽까지 해보도록 하겠습니다


기상 데이터를 받아서 사용하다보면 결측치가 존재하는 경우가 자주 있습니다

 

과거 데이터의 경우에는 3시간 간격으로 기입했기 때문이고

 

최근의 자료들에서는 관측이 제대로 안됐거나 잠시 기기 이상이 있었던 경우 결측치가 존재합니다

 

하지만 모델에 데이터를 사용하기 위해서는 데이터의 양이 충분히 많다면 

 

결측치들을 0으로 처리해도 그 영향이 매우 미미하기 때문에 상관이 없지만

 

데이터가 충분하지 않을 경우는 영향을 주기 때문에 결측치를 0이 아닌 다른 방법으로 채워주어야합니다

 

pandas 라이브러리에서는 내장함수로 interpolate를 지원합니다

 

보간 방식은 많은 보간 방식을 지원하지만 자주 사용되는 것들은 아래 5개 정도인 것 같습니

 

'linear', 'time', 'nearest', 'spline', polynomial' 

 

default는 linear로 선형적으로 결측치를 채워줍니다

 

예시로 한 번 확인해보겠습니다


import pandas as pd
import numpy as np

df = pd.DataFrame({'A' : [1, 3, np.nan, 7, np.nan],
                   'B' : [1, np.nan, 9, np.nan, 17]})

print(df)
print('\n -----interpolating----- \n')
print(df.interpolate())

 

위의 코드처럼 중간에 결측값을 입력시켜서 dataframe을 하나 만들었습니다

 

그 후에 interpolate를 진행하는데 method를 따로 지정해주지 않았습니다

 

A컬럼의 경우 1, 3, ?, 7, ? 구성이기 때문에 2 간격이라는 것을 예상할 수 있습니다

 

그렇기 때문에 선형적으로 내삽이 이뤄질 경우 5와 9가 생겨야 하고

 

B컬럼 마찬가지로 4 간격이라는 것을 예상할 수 있기 때문에 5와 13이라는 값이 채워지면 됩니다

 

결과를 확인해보면 예상한대로 결측값이 처리된 것을 볼 수 있습니다

     A     B
0  1.0   1.0
1  3.0   NaN
2  NaN   9.0
3  7.0   NaN
4  NaN  17.0

 -----interpolating-----

     A     B
0  1.0   1.0
1  3.0   5.0
2  5.0   9.0
3  7.0  13.0
4  7.0  17.0

 

물론 없는 값을 채우는 만큼 오차가 발생하겠지만 

 

제가 실제로 보간을 진행할 데이터의 양이 매우 많지만 결측값의 양은 매우 적기 때문에

 

기본값인 linear로 사용할 예정입니다


제가 보간을 진행할 자료는 저번 포스팅에서 합쳤던  2000년부터 2020년까지 ASOS 1분 단위 자료입니다

 

자료 상세 정보는 아래 표와 같습니다

 

지점 강릉(105)
관측  ASOS
기간 2000-01-01 00:00:00 ~ 2021-01-01 00:00:00
기상변수 기온, 누적 강수량, 풍향, 풍속, 현지기압, 해면기압, 습도,
일사, 일조

 

 

이렇게 잘 열렸습니다

 

총 열 개수는 10,977,143개 입니다

 

과거에는 일시를 시간으로 써서 일시와 시간이 따로 존재하는데 이는 전처리 과정에서 사용하지 않기 때문에

 

크게 상관하지 않았습니다

 

ndf = pd.DataFrame({'T' : df['기온(°C)'].values, 'CP' : df['누적강수량(mm)'].values, 'WD' : df['풍향(deg)'].values,
                    'WS' : df['풍속(m/s)'].values, 'SP' : df['현지기압(hPa)'].values, 'P' : df['해면기압(hPa)'].values, 
                    'R' : df['습도(%)'].values})

 

 

전처리를 한 데이터 프레임은 위와 같습니다


먼저 각 컬럼 별로 총 결측치가 몇개인지 세어보겠습니다

 

코드는 간단합니다

ndf.isnull().sum()

 

이렇게 입력하고 실행해주면 

T       2293
CP    104619
WD     26014
WS     26036
SP     33344
P      33032
R       1142

 

이렇게 각 컬럼에 결측이 몇개 있는지를 세어서 알려줍니다

 

결측치에도 완전 무작위 결측, 무작위 결측, 비무작위 결측 이렇게 3가지 유형이 존재합니다

 

완전 무작위 결측은 여러 변수들이 존재하나 각 변수들의 결측치가 서로 상관 관계가 전혀 없는 경우를 말하고

 

무작위 결측은 변수들 간에 결측치가 상관은 있지만 관계 파악을 할 수 없는 경우를 말하며

 

비무작위 결측은 변수들 간에 결측치가 상관이 있고 관계파악이 가능한 경우를 말합니다

 

기상 변수의 경우는 관측 기기들이 따로 나눠져있기 때문에 상관 관계는 없다고 보는게 맞을 것 같고

 

이 경우 완전 무작위 결측이라고 추측할 수 있습니다


다음은 결측치를 어떻게 처리 할 건지를 고려해야합니다

 

결측치 처리 방법은 제거, 치환, 모델 처리 이렇게 3가지가 있습니다

 

이 처리 방법을 선택하는 기준은 전체 데이터와 결측치의 비율로 결정할 수 있습니다

 

결측치가 전체 데이터에 10% 미만일 경우 제거를 하거나 치환을 사용하고

 

10% 이상일 경우 최근접 이웃 같은 모델 처리를 해야합니다

 

제가 사용하는 데이터의 결측 비율을 계산해보면 아래 표와 같습니다

np.array(ndf.isnull().sum())/10977143 * 100

 

 T 0.02%
CP 0.95%
WD 0.23%
WS 0.23%
SP 0.3%
P 0.3%
R 0.01%

 

기준치인 10%보다도 매우 작은 값이기 때문에 어떤 방식을 사용해도 크게 무리가 없을거라 생각해서

 

pandas의 interpolate의 default 값인 linear를 사용하여 보간을 진행해보겠습니다


ndf1 = ndf.interpolate()

 

이 코드를 실행해서 결과를 보면

 

아직 T컬럼에서 결측이 존재하는 것을 확인할 수 있었습니다

 

보간이 제대로 이뤄지지 않은 이유는 간단합니다

 

선형으로 보간한다는 말은 두 점 사이에 기울기를 구해서 기울기 만큼 계속 더해나가는 것과 비슷한 개념입니다

 

하지만 T컬럼에서보면 초기 값이 없기 때문에 보간이 불가능한 구간인 것입니다

 

이런 경우 초기값을 수동으로 만들어줘야합니다

 초기값을 만들려고 보면 이전 날짜도 58분까지 밖에 없고 

 

당일도 6분부터 있기 때문에 선형적으로 직접 구해보면

[2.66, 2.63, 2.6, 2.56, 2.53]

 

해당 구간의 값을 얻을 수 있고 

 

gdf = pd.DataFrame({'v' : [2.66, 2.63, 2.6, 2.56, 2.53]})

ndf2 = ndf['T'].fillna(gdf['v'])

ndf3 = ndf.drop(labels = 'T', axis = 1)

ndf3['T'] = ndf2

 

결측치를 채운 T컬럼을 기존 T컬럼을 drop한 후에 새로 추가하면

 

 

결측치가 사라졌고 isnull로 계산해보아도 완벽하게 사라진 모습입니다

 


이렇게 오늘은 결측치 처리 방법과 개수 확인 방법, 보간 방법까지 알아봤습니다

 

이제 이 자료를 가지고 연구를 해보려고 합니다

 

긴글 읽어주셔서 감사합니다!

728x90