데이터이야기

DB 노하우, 데이터직무, 다양한 인터뷰를 만나보세요.

4화- 파이선 사물인터넷 데이터 분석

데이터 이야기
작성자
dataonair
작성일
2015-07-14 00:00
조회
5965


파이선 사물인터넷 데이터 분석



지영민 (yangsamy@gmail.com) - 분당 야탑 영장산 산자락에 자리 잡은 전자부품연구원에서 마늘과 쑥을 먹으면서 곰에서 사람으로 환골탈태하여 세상 밖으로 나가기 위해 사물 인터넷 빅데이터 연구에 인고의 시간을 보내고 있다. 마음은 자유로운 영혼이나 자본주의의 노예에서 벗어나지 못함을 한탄하며 찾아올지 모르는 미래의 풍류낭 같은 인생을 꿈꾸며 오늘도 연구에 매진하고 있다.



우선 오랜 시간동안 칼럼을 업데이트 하지 못한 점을 사과드립니다. 개인적으로 연구소에서 여러가지 일들이 겹치다 보니, 글 보다는 개인의 먹고 사는 문제에 더 치중할 수밖에 없었습니다. 이제부터 다시 시작한다는 마음로 4번째 연재를 이어가 보려고 합니다. 이전 글에서는 파이선을 이용한 데이터 분석 중 간단한 사물인터넷 데이터를 그래프로 가시화하고 축을 변경하여 서로간의 스케일을 맞추는 작업을 하였습니다. 데이터는 비교를 하려면, 데이터의 변동 폭을 맞춰 비교해야 하는데, 이 때 사용하는 방법을 정규화라고 합니다.



사물인터넷 데이터 정규화(Normalization)

사물인터넷 데이터는 주로 센서로 부터 읽어 드린 값을 시간 순서로 저장합니다. 이 때 센서로 부터 입력되는 다양한 값들은 여러가지 스케일을 가지고 있기 때문에 평균 및 편차가 다 달라 값을 비교하기 어렵습니다. 이 때 정규화 과정을 거처 다양한 센서에서 나오는 시계열 데이터의 특성만을 남기고 스케일을 동일하게 만들어 데이터를 가공합니다. 이러면 모든 값의 스케일이 같아 지기 때문에 데이터 특성을 쉽게 비교할 수 있습니다. 정규화는 각 센서의 특징만을 남기고 스케일을 똑같이 분포시키는 것을 의미합니다.

새로운 값 = (현재 값 - 최소값) / (최대값 - 최소값)

위와 같은 정규화를 최대-최소 정규화(min-max normalization)이라고 합니다. 다양한 방식의 정규화 기법들이 있지만 본 예에서는 최대-최소 정규화를 사용합니다. 위 식과 같이 아주 간단하게 데이터를 정규화시킬 수 있습니다. 시계열 데이터의 최대 값과 최소 값을 알고 있으면 입력으로 들어온 현재 값을 가지고 정규화시켜 새로운 값으로 바로 연산할 수 있습니다. 그럼 앞서 살펴본 예제를 조금 변경하여 CO2와 온도 값을 정규화 시켜 보도록 하겠습니다.



#-*- coding:utf-8 -*-from numpy import *
import operator
import matplotlib.pyplot as plt#파일 데이터 추출 함수
def file2matrix(filename) :
file = open(filename)
lines = len(file.readlines())
matrix = zeros((lines, 2)) file = open(filename)
index = 0
for line in file.readlines():
line = line.strip()
list = line.split(' ')
print list
matrix[index,:] = list[0:2]
index += 1
return matrix#정규화 함수
def normalization(mat) :
norm = (mat[:,1] - mat[:,1].min())/(mat[:,1].max() - mat[:,1].min())
return norm#파일 읽어서 매트릭스에 저장하기
mat_co2 = file2matrix('co2.log')
mat_temp = file2matrix('temp.log')#파일에서 읽어온 CO2 데이터 값을 정규화 적용하고 매트릭스에 추가
nor_co2 = normalization(mat_co2)
mat_co2 = insert(mat_co2, 1, nor_co2, axis=1)#파일에서 읽어온 온도 데이터 값을 정규화 적용하고 매트릭스에 추가
nor_temp = normalization(mat_temp)
mat_temp = insert(mat_temp, 1, nor_temp, axis=1)#그래프 그리기
plt.plot(mat_co2[:,0], mat_co2[:,1], 'r', mat_temp[:,0], mat_temp[:,1], 'b')
plt.xlabel('unix time')
plt.ylabel('CO2 ppm / temperature')



위 소스코드는 시계열 데이터를 정규화 시키는 함수를 추가하고 CO2와 온도 데이터를 변환하는 것을 추가한 것입니다. 정규화된 데이터를 원본 메트릭스의 새로운 컬럼으로 추가합니다. 그리고 추가된 정규화 데이터를 기반으로 새로운 그래프를 그립니다. 파이선의 numpy array는 min(), max() 와 같은 함수를 제공하기 때문에 쉽게 정규화 코드를 구현할 수 있습니다. 이 정규화된 데이터는 0~1까지의 값을 가지며, 온도 및 CO2 모두 이 범위 안에서 움직이기 때문에 두 시계열 데이터를 쉽게 비교할 수 있습니다. 아래 그림은 저번 기고에서 온도 및 CO2 값을 비교한 그래프와 정규화를 통한 스케일을 일치시킨 후 비교한 그래프 입니다. 그림과 같이 거의 유사한 결과가 나타남을 알 수 있습니다.



dbin_414.jpg

< 정규화 그래프와 스케일 변환 그래프 비교>

시계열 데이터 선형 보간법(Liner Interpolation) 적용

그럼 이제는 정규화된 데이터를 가지고 두 시계열 데이터의 패턴을 비교하기 전에 데이터의 수를 일치시키는 작업을 합니다. 온도와 CO2 데이터의 샘플 수가 틀리면 비교하기 힘들기 때문에, 두 데이터의 샘플 수를 같게 일치 시키는 작업이 필요합니다 우리는 여기서 선형 보간법(Linear Interpolation)을 사용해 데이터의 샘플수를 동일하게 만들 수 있습니다. 선형 보간법은 아래의 그림과 같이 빈 공간의 값을 유추할 때 사용합니다. 파란색 점들이 가지고 있는 값이고 그 사이의 값을 유추해 내어 값을 삽입힙니다.

좀 더 자세한 내용은 http://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html에서 확인하실 수 있습니다. dbin_415.jpg

<선형 보간법 예>

파이선 numpy 에는 이런 선형 보간법을 쉽게 구현할 수 있는 interp라는 함수를 제공합니다. 이 함수를 사용하여 우리가 가지고 있는 CO2와 온도 데이터의 샘플 수를 일치 시켜 보겠습니다. 우선 각 데이터의 샘플 수를 확인해야 합니다. 다음의 코드를 적용하여 얼마의 샘플이 각각 존재하는지 확인합니다.



……..mat_temp = insert(mat_temp, 1, nor_temp, axis=1)print "CO2 size : " + str(len(mat_co2[:,2]))
print "Temperature size : " + str(len(mat_temp[:,2]))
…….



결과를 보면 아래와 같습니다.

dbin_416.jpg

CO2의 경우 10만개의 샘플이 존재하고 온도의 경우는 2만 3천개의 샘플이 존재하는 것을 알 수 있습니다. 그럼 이 데이터를 interp 함수를 사용하여 선형 보간법을 적용하여 온도 데이터의 샘플 수를 CO2 샘플 수와 동일하게 맞춰 보도록 하겠습니다. 다음과 같이 간단한 코드를 삽입하여 선형보간법을 적용할 수 있습니다.



…….print "CO2 size : " + str(len(mat_co2[:,2]))
print "Temperature size : " + str(len(mat_temp[:,2]))#co2 데이터를 기준으로 온도 데이터의 선형 보간법 적용
temp = interp(mat_co2[:,0], mat_temp[:,0], mat_temp[:,1])#빈 메트릭스 행렬 생성 (co2 데이터 수 만큼)
interp_temp = zeros((len(mat_co2[:,1]),2))
interp_temp = insert(interp_temp, 0, mat_co2[:,0], axis=1)
interp_temp = insert(interp_temp, 1, temp, axis=1)#그래프 그리기
plt.plot(mat_co2[:,0], mat_co2[:,1], 'r', interp_temp[:,0], interp_temp[:,1], 'b')
plt.xlabel('unix time')
plt.ylabel('CO2 ppm / temperature')
plt.title('Internet of Things Data Analysis')
plt.show()



Interp 함수를 사용해서 온도 시간 x축(mat_temp[:,0])축의 온도 값 y축(mat_temp[:,2])의 데이터를 CO2 시간축 빈도로 맞추는 선형 보간법을 적용하는 것입니다. 그리고 새로운 시간 축에 맞는 빈 매트릭스를 생성하여 보간법이 적용된 데이터와 시간을 밀어넣고 이를 다시 그래프로 출력하면 아래와 같이 위와 동일 한 그래프를 볼 수 있습니다.

dbin_417.jpg

< 선형 보간법을 적용한 그래프>

눈으로 보기에 온도 데이터의 샘플링 수가 2만개에서 10만개로 늘었음에도 불구하고, 앞서 본 그래프와 크게 차이가 나지 않습니다. 사람 눈으로 변화의 차이를 볼 수 있으려면 온도 데이터의 원본 샘플 수가 더 작아져야 할 것입니다.



시계열 데이터 간의 상관계수 살펴보기

이제 샘플 수가 일치하는 온도와 CO2 데이터를 가지고 두 시계열 데이터의 추세를 비교하는 상관계수를 구해보겠습니다. Numpy는 이를 쉽게 할 수 있는 함수를 제공하고 있는데, numpy.corrcoef 함수로 상관계수를 구할 수 있습니다. 상관계수(correlation coefficients)는 공분산 행렬(covariance matrix)의 각 상관관계를 계산하여 정규화된 결과를 보여줍니다. 이 값은 행렬로 구성이 되며 각 값은 -1에서 +1까지 값을 가지게 되며 -1이나 +1에 가까울 수록 강력한 상관관계를 가지는 것으로 파악합니다. 이와 관련된 자세한 내용은 공분산 행렬과, 상관계수를 참고하기 바랍니다.

Numpy에서 제공하는 상관계수 함수는 아래 링크를 참고.
http://docs.scipy.org/doc/numpy/reference/generated/numpy.corrcoef.html
공분산 행렬의 경우는 아래 링크를 참고.
http://docs.scipy.org/doc/numpy/reference/generated/numpy.cov.html#numpy.cov

아래와 같이 코드를 작성하여 상관계수를 계산합니다.



#-*- coding:utf-8 -*-from numpy import *
import operator
import matplotlib.pyplot as plt#파일 데이터 추출 함수
def file2matrix(filename) :
file = open(filename)
lines = len(file.readlines())
matrix = zeros((lines, 2))

file = open(filename)
index = 0
for line in file.readlines():
line = line.strip()
list = line.split(' ')
print list
matrix[index,:] = list[0:2]
index += 1
return matrix#정규화 함수
def normalization(mat) :
norm = (mat[:,1] - mat[:,1].min())/(mat[:,1].max() - mat[:,1].min())
return norm#구간별 상관계수 연산 함수
def corr(x, y):
size = len(x)
result = zeros((size/50-1,2)) #50개 데이터 단위로 끊어서 상관계수 연산
for i in range(0, (size/50)-1) :
result[i,1] = corrcoef(x[i*50:(i+1)*50,1], y[i*50:(i+1)*50,1])[0,1] #구간별 상관계수 연산
result[i,0] = x[(i+1)*50, 0] #50개 지점 마다의 시간 입력 return result#파일 읽어서 매트릭스에 저장하기
mat_co2 = file2matrix('co2.log')
mat_temp = file2matrix('temp.log')#파일에서 읽어온 CO2 데이터 값을 정규화 적용하고 매트릭스에 추가
nor_co2 = normalization(mat_co2)
mat_co2 = insert(mat_co2, 1, nor_co2, axis=1)#파일에서 읽어온 온도 데이터 값을 정규화 적용하고 매트릭스에 추가
nor_temp = normalization(mat_temp)
mat_temp = insert(mat_temp, 1, nor_temp, axis=1)#co2 데이터를 기준으로 온도 데이터의 선형 보간법 적용
temp = interp(mat_co2[:,0], mat_temp[:,0], mat_temp[:,1]) #빈 메트릭스 행렬 생성 (co2 데이터 수 만큼)
interp_temp = zeros((len(mat_co2[:,1]),2))
interp_temp = insert(interp_temp, 0, mat_co2[:,0], axis=1)
interp_temp = insert(interp_temp, 1, temp, axis=1)#상관계수 연산
corr_coef = corr(mat_co2, interp_temp)#그래프 그리기
fig, ax1 = plt.subplots()
ax1.plot(mat_co2[:,0], mat_co2[:,1], 'r', interp_temp[:,0], interp_temp[:,1], 'b')
ax1.set_xlabel('unix time')
ax1.set_ylabel('CO2 / temperature')ax2 = ax1.twinx()
ax2.plot(corr_coef[:,0], corr_coef[:,1], 'g')
ax2.set_ylabel('correlation coefficent')plt.title('Internet of Things Data Analysis')
plt.show()



corrcoef 함수를 수행하면 2x2 행렬이 리턴됩니다. 이 행렬은 각 x, y (co2, 온도)의 상관계수를 연산한 결과로 다음과 같습니다.

[[coeff(co2, co2), coeff(co2, 온도) ]
[coeff(온도, co2), coeff(온도, 온도) ]]

아래 코드 라인은 바로 위 행렬의 0,1 값을 가져와서 상관계수로 사용한다는 의미를 가지고 있습니다.



result[i,1] = corrcoef(x[i*50:(i+1)*50,1], y[i*50:(i+1)*50,1])[0,1] #구간별 상관계수 연산



그리고 이 coeff는 결국 cov(co2, 온도), 즉 공분산 행렬을 구한 값을 우리가 앞서 CO2와 온도 데이터를 정규화 시켜서 비교했던 것 처럼, 비슷한 방식으로 -1과 1의 범위를 가지도록 정규화시키는 과정을 거치게 됩니다.

dbin_418.jpg

< 상관계수 연산 결과 화면>

상관계수 연산 결과화면을 보면 편차가 복잡하여 맞는지 확인이 힘든 것을 알 수 있습니다. 온도와 CO2가 상승하는 구간에서 대체적으로 높은 상관계수 값을 가지는 것으로 보이나, 이를 좀 더 보기 쉽게 구간평균을 적용하여 살펴보겠습니다. 아래와 같이 구간 평균을 구하는 함수를 추가하고 이 함수를 상관계수 함수 바로 아래에서 호출하도록 코드를 작성하고 그래프 그리는 함수도 수정합니다.



#구간 평균 구하는 함수
def block_average(x):
size = len(x)
result = zeros((size/10-1,2)) #10개 단위로 평균
for i in range(0, (size/10)-1) :
result[i,1] = average(x[i*10:(i+1)*10,1]) #구간 평균 계산
result[i,0] = x[(i+1)*10, 0] #10개 지점 시간 입력 return result……..#상관계수 연산
corr_coef = corr(mat_co2, interp_temp)
block_avg = block_average(corr_coef)………ax2 = ax1.twinx()
ax2.plot(corr_coef[:,0], corr_coef[:,1], 'g', block_avg[:,0], block_avg[:,1], 'p')
ax2.set_ylabel('correlation coefficent')



이 코를 실행하면 다음과 같은 결과를 얻을 수 있습니다. 점으로 표시된 부분이 상관계수의 구간 평균 값을 의미합니다. 이 값이 높으면 높을 수록 온도와 CO2가 상관관계를 가지는 것을 의미하고 이 구간에는 사람이 존재하는 것으로 판단할 수 있습니다.



dbin_419.jpg

< 구간 평균 적용 코드 실행 결과 화면>

아래의 그림과 같이 온도와 CO2가 동시에 상승 또는 하강하는 구간에서는 상관계수 값이 높아지는 것을 알 수 있습니다. 여기서 노란색 선은 0을 의미하고 보통 상관계수는 이 노란색 선을 중심으로 근접해서 분포하는 것을 알 수 있습니다. 0에 가까운 것은 상관관계가 높지 않다는 것을 의미합니다. 아래 보라색 동그라미에 나타난 구간 평균 상관계수 값은 대체적으로 높은 값을 나타내고 있습니다. 이 구간 평균 값을 사용하여, 공간에 사람이 있다는 판단의 정확도를 높일 수 있습니다.

dbin_420.jpg

<구간 평균 상관계수 값이 높은 경우>

이번 글에서는 정규화, 선형 보간법, 상관계수, 구간 평균을 이용한 데이터 분석을 살펴 보았습니다. 다음에는 이를 이용하여 어떻게 사람이 있는지 없는지를 구체적으로 분별하는지를 살펴보도록 하겠습니다.

앞서 예제들은 다음 링크를 통해 다운로드 받으실 수 있습니다.
정규화 예제 : http://220.90.201.147/lecture/co2_temp_normal.py
선형 보간법 예제 : http://220.90.201.147/lecture/co2_temp_interp.py
상관계수 예제 : http://220.90.201.147/lecture/co2_temp_correlation.py
구간평균적용 예제 : http://220.90.201.147/lecture/co2_temp_corr_block.py



출처 : 한국데이터베이스진흥원

제공 : 데이터 전문가 지식포털 DBguide.net