데이터 포인트의 빈도수로 이상치를 제거할 수 있을까?

데이터의 이상치 제거를 위한 특이한 시도 방법을 해보려 합니다.

시계열 데이터가 있다고 가정합시다.

데이터는 (X, Y) 라는 위치를 가지고 있겠지요?

여기서 X는 시간, Y는 특정한 Value 입니다.

일정 부분 튀는 값들을 완전히 삭제하고 싶은데, 이를 위해서 Y값들의 빈도를 모으고 

빈도수가 낮은 Y값들에 해당하는 포인트를 삭제하면 데이터가 어떻게 될지 시도를 해보겠습니다. 

원본 데이터의 구성

				
					import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 테스트를 위한 데이터 로드
path = r"VitalStudy\Data\TestSet.csv"
df = pd.read_csv(path)  


#분석을 하고자 하는 시계열 데이터의 데이터입니다.
data = df["data"].tolist()

#데이터의 길이를 N으로 지정합니다.
N = len(data)

# time 과 data 를 가지고 먼저 그림을 그려보자.
plt.title("test")
plt.plot(np.linspace(0, 1, N), data, c ="k")
plt.show()

				
			

우선은 위와 같은 코드로 간단하게 데이터의 전체 흐름이 어떻게 되어 있는지 확인을 해봅시다.

test 데이터는 이런 식으로 구성이 되어 있네요.

이 데이터에서 척 봐도 튀어나온 구간이 있지요?

저는 저 구간을 깔끔하게 삭제를 하고 싶습니다. 

그리고 이를 위해서 시도할 수 있는 방법이 아주 많이 있겠지만, 이번에는 빈도수를 이용한 방법론을 사용해보겠습니다. 

내 데이터의 값들은 어떻게 분포하고 있는가?

				
					import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 테스트를 위한 데이터 로드
path = r"VitalStudy\Data\TestSet.csv"
df = pd.read_csv(path)

# 분석을 하고자 하는 시계열 데이터의 데이터입니다.
data = df["data"].tolist()

# 데이터의 길이를 N으로 지정합니다.
N = len(data)

# data 내에 있는 값의 분포도를 확인할 것입니다.
frequency = {}

# 데이터 분포도를 계산합니다.
for i in data:
    i = int(i)
    if i in frequency:
        frequency[i] += 1
    else:
        frequency[i] = 1

# frequency 딕셔너리를 시각화합니다.
x = list(frequency.keys())
y = list(frequency.values())

plt.figure(figsize=(10, 5))
plt.bar(x, y, width=0.8)
plt.xlabel("Data Values")
plt.ylabel("Frequency")
plt.title("Frequency Distribution of Data")
plt.show()

				
			

우선은 위와 같이 데이터의 값들을 순회하며 어떤 값들이 몇 개나 전체 데이터에서 분포를 하는지 살폈습니다. 

이렇게 나타난 결과를 한 번 볼까요?

대강 위와 같습니다.

특정한 값에 해당하는 경우 굉장한 빈도수를 보이네요..ㅋㅋ

이제 제거하기 위해 대충 기준을 잡아볼까요?

빈도수를 기준으로 데이터 클리닝

				
					import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 테스트를 위한 데이터 로드
path = r"VitalStudy\Data\TestSet.csv"
df = pd.read_csv(path)

# 분석을 하고자 하는 시계열 데이터의 데이터입니다.
data = df["data"].tolist()

# 데이터의 길이를 N으로 지정합니다.
N = len(data)

# data 내에 있는 값의 분포도를 확인할 것입니다.
frequency = {}

# 데이터 분포도를 계산합니다.
for i in data:
    i = int(i)
    if i in frequency:
        frequency[i] += 1
    else:
        frequency[i] = 1

# 빈도수 기준을 설정합니다
threshold_freq = 5

# 빈도수가 threshold_freq 이하인 값들을 NaN으로 변경합니다
data = [np.nan if frequency[int(x)] <= threshold_freq else x for x in data]

# 시각화
plt.title("test")
plt.plot(np.linspace(0, 1, N), data, c="k")
plt.xlabel("Normalized Index")
plt.ylabel("Data Values")
plt.show()

				
			

위와 같이 데이터를 순회하며 특정 빈도수에 도달하지 못하는 값들은 NAN으로 치환하였습니다.

이렇게 되면 특정 빈도수 이하의 값들을 제거하여 데이터가 지워진 형태를 볼 수 있겠지요?

이런…

별로 좋지 못합니다.

역시 빈도수를 이용한 방법만 가지고는 쉽지 않은 걸까요?

그럼 조건을 약간 변경을 해봅시다. 

				
					import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 테스트를 위한 데이터 로드
path = r"VitalStudy\Data\TestSet.csv"
df = pd.read_csv(path)

# 분석을 하고자 하는 시계열 데이터의 데이터입니다.
data = df["data"].tolist()

# 데이터의 길이를 N으로 지정합니다.
N = len(data)

# data 내에 있는 값의 분포도를 확인할 것입니다.
frequency = {}

# 데이터 분포도를 계산합니다.
for i in data:
    i = int(i//10)
    if i in frequency:
        frequency[i] += 1
    else:
        frequency[i] = 1
        
# frequency 딕셔너리를 시각화합니다.
x = list(frequency.keys())
y = list(frequency.values())

plt.figure(figsize=(10, 5))
plt.bar(x, y, width=0.8)
plt.xlabel("Data Values")
plt.ylabel("Frequency")
plt.title("Frequency Distribution of Data")
plt.show()
				
			

10의 배수로 바꿨습니다.

그전에는 그냥 특정 정수 값이 데이터의 몇 개씩 분포하고 있었느냐고 한다면…

10이 1번 들어가는 수는 몇 개가 분포하고, 10이 2개가 들어가는 수는 몇 개가 분포하는지… 이런 식으로 빈도수 세는 조건을 변경한 겁니다. 

이런 식으로 좁혀지긴 합니다.

50 초반에서 25 근접한 값들이 사라지는 것을 알 수가 있습니다. 

이를 위해서 Threshold 를 52로 설정하여 한 번 그래프를 그려봤습니다. 

이런 식으로 나타나네요.0~60 사이 정도로 값이 균일해진 것을 볼 수 있습니다.

하지만 썩 만족스럽지는 않는 결과입니다.

왜냐하면 원본 데이터를 볼까요?

원본 데이터에서는 중간에 위로 훅 뜨는 부분을 제외하고는 대체로 0~100 정도? 사이의 값을 꾸준하게 유지하고 있습니다.

즉 해당 방법은 좋은 데이터 클리닝 방법으로 볼 수 없는 것이지요.

그럼 어떻게 정제하냐?

위와 같은 데이터를 그대로 대입하여 사용하면 빈도수로 해결이 안 되는 이유가 뭘지 고민이 필요합니다.

척 보기에는 250 부근에 있는 데이터 포인트의 개수가 100정도 되는 데이터 포인트 개수보다 적어 보이지만…

실제로 셈을 해보면 그게 아니라는 것을 알았지요.

250 정도 되는 데이터 포인트 개수가 한 구간 내부에 아주 여러 개가 밀집하고 있는 반면에

100 부근 데이터 개수는 보다 간격을 두고 퍼져 있는 형태입니다.

때문에 원본 데이터를 1차적으로 정제할 필요가 있겠습니다.

마치며...

우선은 단순히 빈도를 이용한 방법으로는 현재 데이터 정제에 적합하지 않다는 결론을 내리겠습니다.

특정 범위 내로 아~주 좁혀서 데이터를 정제하고 싶을 때는 시도해도 괜찮겠으나..

썩 좋지 않네요ㅋㅋ