센서를 이용해 측정한 데이터들은 정확도나 신뢰성이 제조사로부터 검증된 만큼, 높은 신뢰성과 정확도를 보여주기도 하지만, 때로는 주변환경에 영향을 받기도 합니다. 가령, 미세먼지 측정기의 경우 측정기 주변에서 흡연을 하는 경우, 혹은 추운 겨울철 보일러 가동에 따른 온도차로 인하여 발생된 수증기를 미세먼지로 오인해 잘못 측정되어 수치가 단시간에 급격히 증가하는 경우와 비슷한 상황들이 존재할 수 밖에 없으며, 카메라의 CMOS 센서 역시도 제조사마다 보정 기준이 상이하므로 픽셀 데이터의 명암비 균일도를 측정하는 과정에서 하나의 픽셀이 주변의 다른 픽셀에 비해 상대적으로 떨어지는 데이터를 보정하는 기능은 데이터 처리 과정에 있어 필수적이라고 해도 과언이 아닙니다.
앞에서 언급한 측정 오차 문제를 해결하기 위해서는 여러가지 방법들이 있겠지만, 쉽게 접근할 수 있는 방법 중 하나가 중간 값 계산 방식을 이용해 측정 데이터 셋 중 큰 차이가 나는 측정 결과 값들에 대한 편차를 줄여서 계산한 값들을 원소로 새로운 리스트나 배열 (Array) 을 생성해 보정하는 방식이라고 할 수 있겠습니다.
이러한 방식과 유사하게 Microsoft Excel (마이크로소프트 엑셀) 에서 제공하는 Chart (차트) 의 부가적 기능으로서, [Add Trendline (추세선 추가)] 항목 하위의 [Running Average (이동 평균)] 라는 기능으로도 제공되고 있는데요. 기능이나 방식은 유사하다고 할 수 있겠지만, 평균을 기준으로 계산된 계산 결과 값들을 원소로 갖느냐, 중간 값을 기준으로 계산된 계산 결과 값들을 원소로 갖느냐의 차이점 정도만 존재합니다.
평균이란, 가장 많이 사용하는 대표 값으로, 데이터 표본이 정규 분포를 따르는 경우 평균을 활용하는 것에 문제가 없으나, 표본에 이상치 (Outliers) 가 존재하는 경우 평균 값에 오류가 발생하게 됩니다.
중간값이란, 중앙값은 관측값을 작은 수부터 큰 수까지 순서대로 나열한 다음 가운데 중앙에 있는 값을 말합니다. 관측개수가 홀수이면 (n + 1) ÷ 2 번째 관측값이 중앙값이며 관측개수가 짝수이면 n ÷ 2 번째와 n ÷ 2 + 1 번째가 관측값의 평균입니다.
중앙값은 순서대로 나열하여 가운데 있는 값을 대표값으로 선정하기 때문에 이상치의 영향을 크게 받지 않는다는 장점이 있습니다.

계산하는 방식에 대해 보다 자세하게 알아보면, 각각의 리스트를 구성하는 숫자를 중심으로 근접한 연속되는 5 개 (혹은 상황에 따라 3 개, 4 개 등 유동적으로 지정 가능.) 의 숫자를 이용해 중간 값 또는 평균을 계산한 다음 각각의 계산된 결과를 새로운 리스트나 배열 (Array) 에 할당하는 방식으로 적용될 수 있습니다.
두 가지 중 어떠한 방식이 차이가 큰 오차 값에 대해 민감하게 반응하는지에 대한 여부는 아래의 첨부된 그래프를 통해 확실하게, 평균 값 도출을 이용한 Moving Average (이동 평균) 방식이 중간 값 도출 방식의 Moving Median (이동 중위 값) 방식에 비해 오차값에 대한 영향에 보다 민감하게 변화한다는 사실을 확인하실 수 있습니다. 이는, 다시 말해 평균 값 도출 방식을 이용해 결과 값을 보정할 때와 비교해 중간 값 도출 방식을 이용해 결과 값을 보정하는 경우에 신뢰값이나 정확도가 상대적으로 높다는 것으로 정리될 수 있습니다.
연두색 선이 측정기의 미세먼지 센서를 통해 측정한 원본 데이터입니다.
하늘색 점선이 Excel 에서 제공하는 Running Average (이동 평균) 방식으로 오차를 보정한 데이터이며,
그리고 빨간색 선이 직접 작성한 Running Median (이동 중위 값) 방식으로 직접 작성한 소스 코드를 통해 오차를 보정한 데이터입니다.

직접 구현한 Moving Median (이동 중위 값) 방식에 대한 계산 코드는 아래의 코드로 구현되며, 중간 값을 구하기 위해 전체 데이터를 5 개씩 분할하여 Quick Sort (빠른 정렬) 방식으로 정렬한 후 최대 값과 최소 값을 도출한 후에 중간 값을 계산하는 방향으로 작성했습니다.
Sub AllMedian()
Dim tempList(4) As Double
Dim iMin As Integer
Dim iMax As Integer
For i As Integer = 0 To sourceList.Count - 1
iMin = IIf(i < 2, 0, i - 2)
iMax = IIf(i > sourceList.Count - 3, sourceList.Count - 1, i + 2)
Dim k As Integer = 0
For j As Integer = iMin To iMax
tempList(k) = sourceList(j)
k += 1
Next
Quicksort(tempList, 0, 4)
medianList.Add(tempList(2))
Next
End Sub
Public Sub Quicksort(ByVal list() As Double, ByVal min As Integer, ByVal max As Integer)
Dim random_number As New Random
Dim med_value As Double
Dim hi As Integer
Dim lo As Integer
Dim i As Integer
If min >= max Then Exit Sub
i = random_number.Next(min, max + 1)
med_value = list(i)
list(i) = list(min)
lo = min
hi = max
Do
Do While list(hi) >= med_value
hi = hi - 1
If hi <= lo Then Exit Do
Loop
If hi <= lo Then
list(lo) = med_value
Exit Do
End If
list(lo) = list(hi)
lo = lo + 1
Do While list(lo) < med_value
lo = lo + 1
If lo >= hi Then Exit Do
Loop
If lo >= hi Then
lo = hi
list(hi) = med_value
Exit Do
End If
list(hi) = list(lo)
Loop
Quicksort(list, min, lo - 1)
Quicksort(list, lo + 1, max)
End Sub
다만, 이렇게 데이터를 보정하는 방식에 있어 공통적인 단점이 존재한다면 기준이 되는 샘플 데이터가 부족한 맨 초반에 배치된 데이터 2 개와 맨 마지막에 배치된 데이터 2 개에 해당되는 값에 대한 신뢰도가 상대적으로 떨어진다는 점인데요.
이는 주변의 인접 데이터 부족으로 인한 문제로서, 초기 2 개의 항목과 맨 마지막 2 개의 항목에 대해서는 별도의 계산 과정이나 후처리 과정 없이 원본 데이터를 그대로 사용하는 방향으로 별도의 코드를 작성해 보았습니다. 이에 대한 방식의 단점은 초반에 큰 오차 값이 발생했을 때에 보정된 값의 신뢰도가 상대적으로 떨어진다는 단점을 지니는 반면, 연속된 항목의 부재에 따른 중간값을 계산할 경우 기대할 수 있는 기준 수치에 비해 데이터 리스트의 시작점 기준, 5 개의 항목과 맨 마지막 5 개의 항목 값이 모두 동일한 수치로 보정되던 단점에 대해서 보완 가능하다는 장점이 존재한다는 결론에 도달할 수 있었습니다.
Sub MiddleMedian()
medianList.Add(sourceList(0))
medianList.Add(sourceList(1))
Dim tempList(4) As Double
For i As Double = 2 To sourceList.Count - 3
Dim k As Integer = 0
For j As Double = i - 2 To i + 2
tempList(k) = sourceList(j)
k += 1
Next
Quicksort(tempList, 0, 4)
medianList.Add(tempList(2))
Next
medianList.Add(sourceList(sourceList.Count - 2))
medianList.Add(sourceList(sourceList.Count - 1))
End Sub
연구한 결과로 작성된 샘플 앱 실행 파일과 소스 코드는 오픈소스 형태로 참고하실 수 있도록 GitHub 에도 공개해두었으니 참고부탁드립니다.
오늘 이야기는 여기까지 입니다.
고맙습니다.
헉 아직 VB.NET을 사용하는 곳이 있군요..
LikeLiked by 1 person
안녕하세요. 우선 블로그 방문해주시고 댓글 남겨주심에 감사드립니다.
C# 관련 예제는 아래의 게시물을 참고하실 수 있도록 준비해두었으니 참고부탁드리며, 현재 현업에서 많이 사용되고 있는 언어는 C# 이지만, 제가 개인적으로 좋아하는 언어로서, VB.NET 관련 자료는 갈수록 찾기 어려워지고 있는 추세라 VB.NET 자료도 함께 공유해두고 있다는 점 참고해주시면 되겠습니다.
좋은 하루 보내세요!
LikeLike
닷넷을 좋아하는 입장으로서 MS가 VB.NET 지원을 공식 종료 후 조금 아쉬운 마음이 있던 중 최근 자료를 보게되어 반가웠습니다.
저는 C# ASP.NET Core를 주로 공부고 있지만 IoT에도 관심이 많아 블로그 들어오게 됐습니다.
서비스 까지 배포하는 거 보니 상당한 실력자신 것 같습니다.
종종 배우러 방문하겠습니다!
LikeLiked by 1 person