본문 바로가기
IT/Data Analysis

[전처리] MaskedArray + np.where (np.ma.where)

by 한동두 2024. 11. 12.
반응형

좌표 데이터를 다루는 도중에 이해가 잘 안가는 부분을 발견해서 정리하려고한다.

NetCDF 파일을 열어 데이터를 가져와서 nan 처리하다가 생각과는 다른 결과가 나왔던 코드이다.

 

 

 


1. 상황

netCDF의 특정 값만 가지고 와서 np.where로 결측치 처리를 일괄로 수행하려고 했으나 오류 발생

(나는 3차원 데이터였지만 차원이랑 전혀 상관없음)

 


2. 코드

import netCDF4 import Dataset
import numpy as np

data = Dataset(yourFilePath)

#특정 값만 사용
data_value = data[yourValue][:]

# 값 변경 전에 print (2차원이면 data_value[0,0])
print(data_value[0,0,0])
#결측 값으로 표시해둔 것들을(-9990) nan값으로 변경
data_value_nan = np.where(data_value == -9990, np.nan, data_value)
# 값 변경 후에 print
print(data_value[0,0,0])

 

결과로는

--
-9990

아래와 같이 나온다. 

결측 처리가 안됐다.

 

 

 


3. 문제

일단 np.where에 대해서 설명하자면

np.where(np데이터== 특정값, True 시 바꿀 값, False시 바꿀 값)

 

 

즉, 아래 코드에서 보면

np.where(data_value == -9990, np.nan, data_value)

data_value라는 np.array에 있는 값이 -9990인 경우  : np.nan으로 값 변경

data_value라는 np.array에 있는 값이 -9990이 아닌경우 : data_value값 그대로 유지

이런 뜻이다.

 

 

그런데 보면 여기서 

data_value[0,0,0]일때 어떤 -- 라는 값이 나오는데

왜 np.where들어가서 nan이든 --(값 유지)이든 둘중에 하나로 안나오고 뜬금없이 -9990으로 떨어지는건지

이해가 안됐다.

 


4. 해결 및 수정

NetCDF로부터 가져온 데이터가 마스킹된 데이터라서 생긴 문제다.

마스킹은 값은 있지만 사용하지 못하게 하도록 별도로 mask처리를 해놓은 것이다.

 

마스킹된 데이터 확인 여부는 아래 코드로 할 수 있다.

print(isinstance(data_value, np.ma.MaskedArray))
#True: masked array
#False: numpy array

True라면 masked array이고 False이면 그냥 일반 array이다.

 

 

만약 True가 나와서 내 데이터가 masked array라고 확인 이 된 경우, 

예를들어 2d np.array에서

[[10, 20, 21, --],
[15, --, --, 20]]

이렇게 있다면,

 

[10, 20, 21, -9990],
[15, -9990, -9990, 20]]


[[False, False, False, True],
[False, True, True, False]]

위 숫자에 아래 마스크가 씌워져있는 것이다.

내 데이터는 -9990을 nan으로 취급하기 때문에, 이를 사용하지 못하도록 -9990값을 갖는 곳에 마스크를 씌워 --로 만든것이다.

 

따라서 이 상태는 값은 있지만, 사용하지 못하도록 마스킹 되어있는 상태다.

그래서 마스킹 된 값에 대해서 np.where를 하면,

np.where(data_value == -9990, np.nan, data_value)

data_value값이 --일때, -9990이 아니기 때문에 nan이 되지 않고 data_value로 대체된다.

이때 마스킹 된 값이 아니라, 원래 데이터 값을 뱉어낸다고 보면된다.

그래서 --가 아닌, 마스크로 가려졌던 원래 값인 -9990이 나오는 것이다.

(정확한 해석은 아닌 것 같지만, 이렇게 이해하는게 맞을 듯 하다.)

 

 

 

그래서 내가 의도한 -9990을 가지고있는 부분을 nan으로 바꾸려면 아래와같은 방법을 사용하면 된다.

# 1
# -- -> -9990 -> np.nan
data_value = np.where(data_value == -9990, np.nan, data_value)
data_value = np.where(data_value == -9990, np.nan, data_value)

아까 했던 것 처럼 마스킹 된 --을 한번 벗겨내서 -9990으로 만들고

다시한번 np.where로 -9990을 nan값으로 대체해서 결측치를 처리 할 수 있다.

 

 

# 2
# masked data -> np.nan
data_value = np.ma.where(np.ma.getmaskarray(data_value), np.nan, data_value)

이번에는 np.ma.where를 이용해서 maskedArray용 where문인 np.ma.where을 이용하는 것이다.

np.ma.getmaskarray(data_value)가 마스크 처리된 값이면 True, 아니면 False를 반환하기 때문에

마스킹 된 값만 결측치 처리가 가능해진다.

+)

data_value = np.ma.where(data_value='--', np.nan, data_value)는 안된다.

data_value값이 --가 아니기 때문. --는 마스킹 되어있다는 표시일 뿐이다.

 

# 3
# masked data -> np.nan
data_value = data_value.filled(np.nan)

가장 쉬운방법이라고 볼 수 있는 코드이다.

마스크 처리 된 데이터에 np.nan으로 채워넣는다고 생각하면 된다.

 

 

 


결론

MaskedArray를 처리할 때에는 np.ma.where을 사용하거나 

단순하게 값을 넣는 용도라면 .filled를 이용하도록 하자.

 

나는 주로 마스킹 할때에는 값을 강제로 바꾸기만 했었는데
기존 값을 건들이지 않으면서 마스킹 할 수 있다는 것을 알게 되는 계기가 되었다.

꽤나 유용하게 사용할 수 있을지도 모르겠다!

 

문제도 해결하고 새로운 개념도 알게되어 기분이 좋다.

혹시 모르겠거나 이해가 안되는 부분이 있으면 댓글 달아주세요!.

최대한 설명 드리도록 하겠습니다~

반응형