순서 

1. 통계 - 카운팅,min,max,평균,중앙값,산포도,분산,편차,공분산,상관관계 

2. 가설과 추론 (베이지언 - 사후확률,우도) 

3. 군집화 (K-Means)

4. 연관 (Apriori)

5. 함수형으로 데이터 다루기 

6.경사하강법

7. 회귀분석

8. 은닉 마코프법 (HMM) 

9. k-NN

10. DTW 

 * 참고로 "밑바닥부터 배우는 데이터과학" 서적은 numpy,scikit-learn 등의 외부라이브러리를 활용은 배제되었습니다.



회귀분석 

Okky 싸이트에 들어오는 사람들이 Okky 싸이트에 머무는 시간과 그들의 경력에 대해서 생각해보자.

머무는 시간이 1시간이면 , 경력은 5년
머무는 시간이 2시간이면,  경력은 7년 

..

이렇게 데이터가 누적되었을때, 그 둘 간의 어떠한 상관관계가 있는지 살펴봐서 , 머무는 시간이  X 시간일때  그 사람의 경력은 Y 년이라고 예측 가능 할 것이다.(물론 확률 몇%로 이렇게 조건이 붙겠지만.)  다른 예로 , 당신이 쥬씨 가게를 열었는데, 온도가 27도일때 몇잔이 팔리고..이런 데이타가 누적되었었을때 , 내일의 날씨를 안다면 , 내일 판매 될 잔 수를 알게 될 것이고, 재료를 준비하는데 도움이 될 것이다.

이렇게 두 데이타간의 상관관계가 있을때 이것에 대한 일반적인 식을 구해서 예측의 도구로 사용하는것을 회귀분석이라 한다. 

이러한 분석은 두 데이터가 아니라 , 여러 데이터를 통해서 할 수 도 있고 (중회귀분석),  0 혹은 1로 귀결을 맺는 로지스틱회귀 (내일 LG가 이길 것인가? 질것인가?) 등도 있지만 여기서는 단순회귀분석을 해보고자 한다.



위의 그림을 보면 x 축을 1,2,3 을 10도 20도 30도라는 온도로 생각하고  , y 축을 아이스커피 판매지수라고 생각하고 차트를 보자. 분명히 온도와 판매지수는 양의 상관관계가 있어 보인다.(날씨가 더울수록 판매가 잘 되는)

이때 저 직선 (회귀식) 을 구해서 추정하고 예측하는것을 회귀분석이라 한다. 저 직선을 구하면  몇도에선 몇잔 판매할 것이라는 것이  예측할 수 있을것임을  짐작할 수 있을것이다.

이제 회귀분석의 순서를 알아보자.

회귀분석 순서 

1. 회귀식을 구할 필요가 있는지를 검토하기 위하여 독립변수와 종속변수의 점그래프를 그려본다.

2. 회귀식을 구한다.

3. 회귀식의 정도를 확인한다.

4. '회귀계수의 검정' 을 실행한다.

5. 모회귀 Ax + B 를 추정한다.

6. 예측한다.


이 중에서 우리는 회귀식에 대해서만 알아본다. 


회귀식 

저 직선을 회귀식이라고 하며 아시다시피 직선의 방정식 y = ax + b 로 나타낼 수 있다. 

회귀식이 y =  30x + 100 이라면 30도 (x : 독립변수) 일때 1000잔 (y : 종속변수) 이 판매될거란 예측을 할 수 있게 된다. 


입력 데이터 + 알고리즘  ===>  결과 데이터 

데이터 + 회귀식 ====> 결과 


보통 우리들은 입력 데이터에 어떠한 알고리즘을 가지고 결과 데이터를 얻고 싶어한다. 하지만 딥러닝같은 신경망에서는 입력데이터와 결과데이터를 가지고 알고리즘을 산출해 내는데.. 회귀분석도 마찬가지이다. 데이터들을 가지고 회귀식을 만드는 것이쥐~

만드는 방법은 다음과 같다.


 y = ax + b    (y:종속변수 , x : 독립변수 , a : 회귀계수)
여기서 a와 b의 값을 찾는 방법을 말한다.



최소제곱법

위에서 보면 포인트들이 실제 데이터이고, 파란색 선이 아직 정해지지 않은  회귀식이라고 할 때 , d1,d2,d3,d4 이 세로선 들의 거리의 제곱의 합이 최소가 되도록 a,b 를 구하는 방법을 말한다. 

그리고 이 세로선들을 잔차라고 한다.


최소제곱법 순서

1. Sxx (x의 편차의 제곱의 합) , Syy (y의 편차의 제곱의 합), Sxy(x와 y의 편차의 곱의 합) 을 구한다.

2. 잔차의 제곱의 합을 구한다. Sl 이라 하자.

3. Sl 을 각각 a 와 b 에 대해서 미분한 후 0 으로 놓는다. 

4. Step 3의 결과를 정리한다.

5. Step 4이 결과를 정리한다.

6. 회귀식을 구한다.


1번은 그냥 데이터를 가지고 x평균, y평균, 각각의 편차등을 구하는  쉬운 산수일것이다.


2번의 경우  

x , y , y' = ax + b , y - y' , (y-y')^2 이 필요하다. 여기서 y-y' 이 잔차이다. 

y 는 데이터를 통한 측정값이고 , y' 는 회귀로 알아내야 할 예측값이라고 한다.

즉 기온이 29, 커피판매수 77이면 

x = 29 

y = 77 

y' 는 =====>   a*29 + b     

(y - y') ====>   77 - (a*29+b)  

가 된다.

결국 SI = {77-(a*29+b)}^2 + ...... + {84-(a*30+b)}^2  이렇게 나타낼 수 있게 된다.


3번의 경우 

위의 SI 식을 가지고 a,b 각각에 대해서 편미분을 해준다. 

dSl / da =  2{77- (29*a + b) } * (-29) + ..............

dSI / db = 2{77-(29a+b)} * (-1) + .... 


4,5번의 경우 

3번에서 도출된 식을 양변에 1/2 곱하고, 이항해주고 어쩌고 저쩌고해서 짧게 나타내보면 

a = Sxy / Sxx    ==> 3.7 

b = y평균 - x평균 * a 라고 정리된다.


6. 회귀식 

y = 3.7x = 36.4  뭐 이런식으로 구해진다. 더 자세한 것은 레퍼런스를 참고하자.


즉 알고보니  

a =  x와 y 편차의 곱의 합  / x 의 편차의 제곱의 합 

b =  y평균 - x평균 * a  

인 것이다.



코드로 말해요


자 이제 코드로 살펴볼 시간이다.

최소제곱법은 아래와 같다. (위에 도출된 식과 비교해보라. 동일하다 ^^) 

def least_squares_fit(x,y):
beta = correlation(x, y) * standard_deviation(y) / standard_deviation(x)
alpha = mean(y) - beta * mean(x)
return alpha, beta


경사하강법을 통해서도 알파와 베타를 구할 수 있는데 
(경사하강법은 다음 포스트에서 좀 더 자세히 다룰 예정이다) 

def predict(alpha, beta, x_i):
return beta * x_i + alpha

def error(alpha, beta, x_i, y_i):
return y_i - predict(alpha, beta, x_i)
def squared_error(x_i, y_i, theta):
alpha, beta = theta
return error(alpha, beta, x_i, y_i) ** 2

def squared_error_gradient(x_i, y_i, theta):
alpha, beta = theta
return [-2 * error(alpha, beta, x_i, y_i), # alpha partial derivative
-2 * error(alpha, beta, x_i, y_i) * x_i] # beta partial derivative

theta = [ alpha, beta] 로 설정하면 경사 하강법을 통해 모델을 만들 수 있다.

random.seed(0)
theta = [random.random(), random.random()]
alpha, beta = minimize_stochastic(squared_error,
squared_error_gradient,
num_friends_good,
daily_minutes_good,
theta,
0.0001)


*경사하강법


def minimize_stochastic(target_fn, gradient_fn, x, y, theta_0, alpha_0=0.01):

data = zip(x, y)
theta = theta_0 # initial guess
alpha = alpha_0 # initial step size
min_theta, min_value = None, float("inf") # the minimum so far
iterations_with_no_improvement = 0

# if we ever go 100 iterations with no improvement, stop
while iterations_with_no_improvement < 100:
value = sum( target_fn(x_i, y_i, theta) for x_i, y_i in data )

if value < min_value:
# if we've found a new minimum, remember it
# and go back to the original step size
min_theta, min_value = theta, value
iterations_with_no_improvement = 0
alpha = alpha_0
else:
# otherwise we're not improving, so try shrinking the step size
iterations_with_no_improvement += 1
alpha *= 0.9

# and take a gradient step for each of the data points
for x_i, y_i in in_random_order(data):
gradient_i = gradient_fn(x_i, y_i, theta)
theta = vector_subtract(theta, scalar_multiply(alpha, gradient_i))

return min_theta


마지막으로 아래 동영상을 참고하시길 바란다. 노트에 손글씨로 직접 풀이를 해 보는 동영상인데 

매우 도움이 될 것이다. 

선형회귀와 Gradient Descent


전체 소스는 아래에 있다.

https://github.com/insightbook/Data-Science-from-Scratch/blob/master/code/ch14_simple_linear_regression.py



레퍼런스 :

밑바닥부터 시작하는 데이터 과학

만화로 쉽게 배우는 회귀분석  

요즘 데이터분석과 관련하여  텐서플로우와 스파크(ML)등의 머신러닝 솔루션들이 굉장히 유행하고 있습니다. 물론 저것들이 삶을 편안하게 만들어주기도 하지만 대부분의 데이터 분석은 저런 거창한 것 말고 평균,편차 같은 기본적인 개념으로 부터 시작되고 있으며 이러한 개념을 조금씩 변경해가며 더 의미있는 가치를 찾기 위해 빠르게 적용해보는 과정을 거쳐야하는데  그러기 위해서는 

1. 직접 코딩해서 기본적인 데이터분석 유틸리티 함수들을 만들어봐야한다. 

2. SQL문을 잘 다루어야한다. 

3. 엑셀을 잘 다루어야 한다. 

이 3가지는 기본이라고 저는 생각합니다. 소규모 데이터를 가지고 이리저리 반복해서 돌려보는 과정은 매우 중요하며 이런 기본적인 것들도 못하면서 하둡,텐서플로우나 깔짝대고, 데이터 분석 한다 라고 칭할 수는 없겠지요. (이것은 논쟁의 여지가 있습니다.) 그래서 이것들 중 1번에 대하여  "밑바닥부터 시작하는 데이터과학" 등의 좋은책의 내용을 통해서 살펴보는 시간을 갖도록 하겠습니다. 

통계,확률,패턴인식 분야의 내용들의 수식은 외우기가 힘듭니다. 외워도 금방 까먹어지는게 통계관련 공식인거 같습니다. (예를들어 정규분포,확률밀도라는 개념은 매우 쉽게 수긍이 가지만 , 그 식을 외우는건 좀 .;;) 또한 체득하고 나면 당연한거 아냐? 라고 느껴지는 알고리즘(수식) 인데 글로 설명하면 매우 산만해지는거 학문인거 같습니다. 하지만  우리들은 소프트웨어 개발자이기 때문에 수많은 기술변화도 따라가야하는 운명에 있는데 쓸때 없이 공식 및 수학을 외우고 있을 수는 없지 않겠습니까? 

제가 초등학교 5학년때 만(10,000) 단위 암산을 했었는데요. 주산,암산을 배워보신분은 아시겠지만 그것이 가능한것은 기억의 매개로 주판을 이용한 것 입니다. 마찬가지로 우리 개발자들은 그 기억의 매개체로 코드를 이용하면 개념과 함정(예를들어 K-Means 를 통해 군집화하면 길이차를 가지고 구분짓는 것이기 때문에 문제가 생기는 도메인 또한 많다) 에 대한 이해가 더 오래갈 것이라 보며 덤으로 가져다 사용도 할 수 있을것입니다.

* 그 상상의 매개체로써의 언어로 "파이썬" 을 선택했으며 정말 좋은 언어라고 생각합니다.

순서 

1. 통계 - 카운팅,min,max,평균,중앙값,산포도,분산,편차,공분산,상관관계 

2. 가설과 추론 (베이지언 - 사후확률,우도) 

3. 군집화 (K-Means)

4. 연관 (Apriori)

5. 함수형으로 데이터 다루기 

6. 경사하강법

7. 회귀분석

8. 은닉 마코프법 (HMM) 

9. k-NN

10. DTW 

 * 참고로 "밑바닥부터 배우는 데이터과학" 서적은 numpy,scikit-learn 등의 외부라이브러리를 활용은 배제되었습니다.


연관 되는 이벤트들이란?

이마트에 가서 장을 보는 것으로 이야기를 풀어나가 본다.
어느 날이 좋은 목요일엔  ("수박"-"기저귀"-"맥주") 를 사고 어느날 안좋은 목요일에도  ("기저귀"-"맥주") 를 샀다고하자. 그런 날이 자주 있었다고 하면 목요일과 "기저귀" 와 "맥주" 는 어떤 연관관계가 있다고 할 수 있다. 나 뿐만 아니라 수많은 남자들이 목요일날 저렇게 함께 사는 확률이 높다는것을 알아차린다면 업주는 무엇을 해야할까? 그렇다 "기저귀" 와 "맥주" 코너를 비슷한 곳에 위치시킴으로써 매출을 늘릴 수 있을 것이다.

이렇게 어떤 데이터 집합속에서 각 데이터간의 관계를 살펴서 연관 되는 분석/규칙을 찾는 알고리즘중에 하나가 Apriori 알고리즘이다. 먼저 관련 용어를 아래 표를 가지고 설명하겠다.  매우 간단하다.

 이마트 방문일

  상품

 1일

두유,상추

 5일

상추,기저귀,맥주,삼겹살 

 10일

두유,기저귀,맥주,오렌지 주스 

 15일

상추,두유,기저귀,맥주 

 20일

상추,두유,기저귀,오렌지 주스 


지지도 

위의 표로 보면 [두유] 의 지지도는 4/5 인데 이유는 전체 집합군에서 두유가 포함된 집합의 수이다.

전체에서 아이템 집합 (두유) 이 포함된 데이터 집합의 비율로 정의한다. 

[두유,기저귀] 의 지지도는 ? 그렇다. 3/5 이다.  

두유와 기저귀를 모두 포함한 데이터 집합은 3개가 있다.


신뢰도 

[기저귀]를 샀을때 [맥주]도 같이 사는 확률에 관한 이야기이다.

즉 [기저귀] ->[맥주] 로 그 둘간의 연관 규칙을 나타내는데,  

[기저귀] 를 산 모든날이 100일이라고 하면 , [기저귀] [맥주] 를 함께 산 날이 90일이라고 칠때

그 신뢰도는 9/10이다. 즉 신뢰도란 지지도({기저귀,맥주})  / 지지도({기저귀}) 가 된다.


Apriori 알고리즘이란?

위의 그림은 A 가 발생하고 나서 (A,B) (A,C) (A,D) 등이 일어날 수 있고 그 후에 (A,B,C) 가 일어날수 있음에 대한 단순한 트리이다. 이때 AB가 일어날 확률이 적었다면 , AB에서 파생되는 것들도 적을것임은 너무 자명하다. 따라서그것들에 대한 계산을 삭제해가면서 빈발(Frequent) 관계를 찾는 알고리즘이라고 보면된다. 알고리즘의 성능을 높게하기 위해서 말이다. 


소스로 말해요.

이제 알고리즘을 만들기 위해 필요한 함수들을 공부해보도록 하자. 파이썬 공부를 겸하면서~1석2조!!

[[1,3,4], [2,3,5], [1,2,3,5], [2,5]] 이런 데이타가 있다고 하자.


1. createC1 

데이터셋들을 중복되지 않게 유일한 수들로 나열시키는 함수이다.
[1,1,1,2,3,2,2,3,3,4,5,3,4] 의 결과는 [1,2,3,4,5] 로 나올것이다.

def createC1(dataSet):
C1=[]
for transaction in dataSet:
for item in transaction:
if not [item] in C1:
C1.append([item])

C1.sort()
return map(frozenset, C1)

[1,2,3,4,5] 이렇게 나온결과를 map 을 사용하여 변경해주고 있다.
map, reduce, filter, folder 과 같은 것들은 함수형 파라다임에서 기본적인 함수들이며, 많은 언어에서 지원하는 추세이다. map 의 경우 두번째 파라미터를 , 첫번째 파라미터이 함수를 적용해서 리스트를 뽑아낸다.

결국 [frozenset([1]), frozenset([2]) .....] 이런식의 최종 결과가 도출된다. 

frozenset 은 한번 정의하면 이후 add 같은 변경이 불가능한 집합을 의미 하며 나중에 딕셔너리의 키로 사용할 예정이기때문에 굳이 frozenset 을 사용하였다. 

2. scanD 

C1 은 [1,2,3,4,5] 같은 유일한 데이터의 리스트이고 (정확히는 [frozenset([1]), frozenset([2]) .....])
D 는 [set([1,3,4]), set([2,3,5]) .... 이다.

C1 = createC1(dataset)

D = map(set,dataset) # distinct 수를 뽑아냄. (1,3,3) 이면 (1,3)

# 전체 그룹중에 Ck 가 있는것. # Ck 가 1 이라면 1을 포함한 그룹들을 찾음 (지지도 생성) # Ck 가 [1,3] 이라면 [1,3] 둘다 포함한 그룹들의 지지도 찾음 def scanD(D, Ck, minSupport):

# 요소 n 은 몇개의 그룹에 포함되어 있는지 계산한다. (1은 2개) ssCnt = {}
for tid in D: # tid 에는 set([1,3,4]) 등이 담긴다.
for can in Ck: # can 에는 frozenset([1]) 등이 담긴다.
if can.issubset(tid): # 1 이 [1,3,4] 에 있으면
if not ssCnt.has_key(can): ssCnt[can] = 1
else: ssCnt[can] += 1 # 키 : 1 값 : 1을 가진 그룹의 수
    

# 지지도가 0.5보다 높은것들의 리스트를 구함. # 1의 경우 2군데 포함되었으니 2/4 = 0.5 # 2의 경우 3/4 , 3의 경우 3/4 , 4의 경우 1/4, 5의 경우 3/4
numItems = float(len(D)) # 그룹 갯수
retList = []
supportData = {}
for key , value in ssCnt.iteritems():
support = value / numItems
if support >= minSupport:
retList.insert(0,key)
supportData[key] = support

return retList, supportData

L1 = scanD(D, C1, 0.5) 

는 [frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])]  이런 결과가 나온다.

지지도가 0.5 이하인 4 빼고는 다 나오는셈~ 

이마트로 설명해보면 사람들이 장을 보는 아이템들이
도깨비는 [상추,기저귀,맥주,삼겹살]
 를 사고
은탁이는 [상추,삼겹살] 
저승사자는 [상추]
써니는 [맥주,삼겹살] 을 샀다면  

50%이상 선택되는 아이템을 찾는 로직이며, 결과는 기저귀 빼고 나머지가 
선택될 것이다.
[상추,삼겹살] 짝 또한 지지도가 50%이상 선택되어졌다. 

apriori 알고리즘 (빈발아이템 집합찾기)


# [1,3,2,5] 를 # [1,3] [2,5] [2,3] [3,5] 를 # [2,3,5] 이런식으로 만드는 함수 def aprioriGen(Lk, k):
retList = []
lenLk = len(Lk)
for i in range(lenLk):
for j in range(i+1, lenLk):
L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2]
L1.sort(); L2.sort()
if L1 == L2:
retList.append(Lk[i] | Lk[j])

return retList
# 특정 지지도 이상의 값들의 쌍을 찾음
def apriori(dataset, minSupport = 0.5):
C1 = createC1(dataset)
D = map(set, dataset)
L1 , supportData = scanD(D,C1,minSupport)
L = [L1]
k=2
while (len(L[k-2]) > 0):
Ck = aprioriGen(L[k-2],k)
Lk,supK = scanD(D,Ck,minSupport) # 후보그룹을 모두 찾는다.
supportData.update(supK)
L.append(Lk) #이게 핵심!특정 지지도 이상의 그룹들만 L에 담는다.즉 가지치기
k += 1
return L, supportData


if __name__ == "__main__":
print "apriori 알고리즘"

dataset = loadDataSet()

L, suppData = apriori(dataset)
print "L:" + str(L)
print "........................."
print "suppData:" + str(suppData)

코드에서 retList.append(Lk[i] | Lk[j]) 는 만약 frozenset([2,3]) 과 frozenset([2,5]) 가 있다면 [2,3,5] 로 만들어주는 코드이다.  예를들어 자세히 살펴보자.

Lk = [frozenset([1,2]), frozenset([1,3])]

Lk[0] | Lk[1]  는 frozenset([1,2,3])   # 합집합

Lk[0] - Lk[1]  는 frozenset([2])       # 차집합 

Lk[0] & Lk[1]  는 frozenset([1])       # 교집합 
이왕 하는 김에 set 에 대해서 좀 더 알아보자.
numbers1 = {1, 3, 5, 7}
numbers2 = {1, 3}

# # Is subset.
if numbers2.issubset(numbers1):
    print("Is a subset")

# # Is superset.
if numbers1.issuperset(numbers2):
    print("Is a superset")

# Intersection of the two sets.
   print(numbers1.intersection(numbers2))
결과)

지지도 0.5 이상의 가장 빈번한 조합들 

L:[[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])], [frozenset([2, 3, 5])], []]


조합들의 지지율 상세  

suppData:{frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([2, 3, 5]): 0.5, frozenset([1, 2]): 0.25, frozenset([1, 5]): 0.25, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([1, 3]): 0.5, frozenset([2]): 0.75}

이마트로 설명해보면  사람들이 장을 보는데 도깨비는 [상추,기저귀,맥주,삼겹살] 를 사고 은탁이는 [상추,맥주,삼겹살] 저승사자는 [상추], 써니는 [맥주,삼겹살] 을 샀다면, 그 중에서  사람들이 50%이상 선택되는 아이템을 찾는다고 할때  결과는 무엇일까? 

단일 아이템 : [상추] [맥주] [삼겹살]

2쌍 아이템: [상추,맥주]  [삼겹살,맥주]

3쌍 아이템: [상추,삼겹살,맥주] 

가 될 것이다.


apriori 알고리즘 (연관규칙찾기)

위의 함수에서는 일단 가장 빈번하게 나타나는 패턴들을 찾아서 묶어놓았다.

L:[[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])], [frozenset([2, 3, 5])], []]

이런식으로 말이다.

역시 이해하기 편하게 이마트의 경우로 생각해보자. (몇년지나 다시 읽어보니 그냥 숫자가 더 이해하기 쉬운 함정이..)

[두유, 상추] 라는 빈발집합을 찾았다고 해도  
[두유] 를 살 때 [상추] 를 사는 경우가 매우 많으면 [두유] -> [상추] 는 연관 관계에 있다고 볼 수 있지만 
[상추] 또한 [두유] 와 연관관계가 많다고는 단정 지을 수는 없다.
왜냐면 [상추] 를 샀을때 [두유] 를 같이 사는 경우보다 [갈비살] 을 사는 경우가 훨 씬 많을 수 있기 때문이다.

또한 특정 요일에 [두유,상추] 보다 [두유,빵] 을 사는 비율이 더 높다면 그 요일의 경우는 두유와 연관이 있는것은 상추보다는 빵일 수 도 있다.

소스를 통해 이해해보자. (좀 복잡하니 편집기를 놓고 하나씩 따라가면서 이해하는게 빠를 것이다.) 


def generateRules(L, supportData, minConf=0.7):
bigRuleList = []
for i in range(1, len(L)):
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet]
if i>1:
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
else:
calcConf(freqSet,H1,supportData, bigRuleList, minConf)

return bigRuleList

def calcConf(freqSet, H, supportData, br1, minConf=0.7):
prunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet-conseq]
if conf >= minConf:
print freqSet-conseq, '-->', conseq, 'conf:', conf
br1.append((freqSet-conseq, conseq, conf))
prunedH.append(conseq)
return prunedH

def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7):
m = len(H[0])
if (len(freqSet) > (m + 1)):
Hmp1 = aprioriGen(H, m+1)
Hmp1 = calcConf(freqSet, Hmp1, supportData, br1, minConf)
if (len(Hmp1) > 1):
rulesFromConseq(freqSet, Hmp1, supportData, br1, minConf)




if __name__ == "__main__":
print "apriori 알고리즘"
dataset = loadDataSet()
L, suppData = apriori(dataset)
print "L:" + str(L)
print "........................."
print "suppData:" + str(suppData)

rules = generateRules(L, suppData, minConf=0.7)

이 코드의 핵심은 conf = supportData[freqSet] / supportData[freqSet-conseq]  코드이다.
이 코드가 말하는 바는  
conf =  
(기저귀,맥주) 가 함께 나올 지지율 /  기저귀가 포함된 모든것의 지지율이다. 
이 비율이 높을때는 기저귀와 맥주는 함께 따라다닌다고 보면 된다는 것이다. 


데이터 [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]

빈번조합들 (50%이상 지지도)

L:[ [frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])],
    [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])],
    [frozenset([2, 3, 5])], []]


전체 조합들의 지지율   

suppData:{frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([2, 3, 5]): 0.5, frozenset([1, 2]): 0.25, frozenset([1, 5]): 0.25, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([1, 3]): 0.5, frozenset([2]): 0.75}


지지도 0.5 이상의 결과 :  
frozenset([3]) --> frozenset([1]) conf: 0.666666666667
frozenset([1]) --> frozenset([3]) conf: 1.0
frozenset([5]) --> frozenset([2]) conf: 1.0
frozenset([2]) --> frozenset([5]) conf: 1.0
frozenset([3]) --> frozenset([2]) conf: 0.666666666667
frozenset([2]) --> frozenset([3]) conf: 0.666666666667
frozenset([5]) --> frozenset([3]) conf: 0.666666666667
frozenset([3]) --> frozenset([5]) conf: 0.666666666667
frozenset([5]) --> frozenset([2, 3]) conf: 0.666666666667
frozenset([3]) --> frozenset([2, 5]) conf: 0.666666666667
frozenset([2]) --> frozenset([3, 5]) conf: 0.666666666667


지지도 0.7 이상의 결과 : 
frozenset([1]) --> frozenset([3]) conf: 1.0     # 1 을 할때, 3도 같이 할 확률이 100%
frozenset([5]) --> frozenset([2]) conf: 1.0     # 5 를 할때 2를 할 확률도 100%
frozenset([2]) --> frozenset([5]) conf: 1.0     # 2 를  할 때 5를 할 확률도 100%


유심히 봐야할 것은 2와 5는 서로 연관관계가 있지만 
1과 3은  단지 1이 3과 연관관계가 있는것이지 3 은 1과 연관관계가 부족하다는 것.


레퍼런스  : 머신러닝 인 액션 




요즘 데이터분석과 관련하여 신경망(특히 딥러닝) 과 스파크(ML)등의 머신러닝 솔루션들이 굉장히 유행하고 있습니다. 물론 저것들이 그 동안 해결해주지 못했던 것을 좀 더 잘 해주기 때문에 인기를 끄는 것은 당연하지만,  데이터 분석이라는 바운더리에서 저런 것 들은 아주 작은 부분인 것이 사실입니다. 대부분의 데이터 분석은 저런 거창한 것 말고 평균,편차 같은 기본적인 개념으로 부터 시작되고 있으며 이러한 개념을 조금씩 변경해가며 더 의미있는 가치를 찾기 위해 빠르게 적용해보는 과정을 거쳐야하는데  그러기 위해서는 

1. 직접 코딩해서 기본적인 데이터분석 유틸리티 함수들을 만들어봐야한다. 

2. SQL문을 잘 다루어야한다. 

3. 엑셀을 잘 다루어야 한다. 

이 3가지는 기본이라고 생각해서 항상 공부해야 한다고 생각합니다. 소규모 데이터를 가지고 이리저리 반복해서 돌려보는 과정은 매우 중요하며 이런 기본적인 것들도 못하면서 하둡,텐서플로우나 깔짝대고, 데이터 분석 한다 라고 칭할 수는 없겠지요. 그래서 이것들 중 1번에 대하여  "밑바닥부터 시작하는 데이터과학" 등의 좋은책의 내용을 통해서 살펴보는 시간을 갖도록 하겠습니다. 통계,확률,패턴인식 분야의 내용들의 수식은 외우기가 힘듭니다. 외워도 금방 까먹어지는게 통계관련 공식인거 같습니다. (예를들어 정규분포라는 개념은 매우 쉽게 수긍이 가지만 , 그 식을 외우는건 좀 .;;) 또한 체득하고 나면 당연한거 아냐? 라고 느껴지는 알고리즘(수식) 인데 글로 설명하면 매우 산만해지는거 학문인거 같습니다. 하지만  우리들은 소프트웨어 개발자이기 때문에 수많은 기술변화도 따라가야하는 운명에 있는데 쓸때 없이 공식 및 수학을 외우고 있을 수는 없지 않겠습니까? 

제가 초등학교 5학년때 만(10,000) 단위 암산을 했었는데요. 주산,암산을 배워보신분은 아시겠지만 그것이 가능한것은 기억의 매개로 주판을 이용한 것 입니다. 마찬가지로 우리 개발자들은 그 기억의 매개체로 코드를 이용하면 개념과 함정(예를들어 K-Means 를 통해 군집화하면 길이차를 가지고 구분짓는 것이기 때문에 문제가 생기는 도메인 또한 많다) 에 대한 이해가 더 오래갈 것이라 보며 덤으로 가져다 사용도 할 수 있을것입니다.

* 그 상상의 매개체로써의 언어로 "파이썬" 을 선택했으며 정말 좋은 언어라고 생각합니다.

순서 

1. 통계 - 카운팅,min,max,평균,중앙값,산포도,분산,편차,공분산,상관관계 

2. 가설과 추론 (베이지언 - 사후확률,우도) 

3. 군집화 (K-Means)

4. 연관 (Apriori)

5. 함수형으로 데이터 다루기 

6. 경사하강법

7. 회귀분석

8. 은닉 마코프법 (HMM) 

9. k-NN

10. DTW 

 * 참고로 "밑바닥부터 배우는 데이터과학" 서적은 numpy,scikit-learn 등의 외부라이브러리를 활용은 배제되었습니다.


통계

하마 스타트업을 창업한 후에 승승장구하여 꽤 많이 성장하게 되었다. 이 회사는 데이트 매칭 회사로 사용자들이 몇 명의 친구를 가지고 있고, 그것에 관해 어떤 내용들을 담고 있는지 설명해 달라고 데이터 분석팀에게 요청이 날라왔다. 해결해볼까?


1.  일단 데이터 그 자체를 1차원 리스트로 보여줄 수 있습니다.

num_friends = [100,40,30,54,25, 3, ..........]     # 100 명의 친구를 가진 사람도 있고 3명의 친구를 가진 사람도 있다.

회원 수가 몇명 안되면 이렇게 보여 줄 수 도 있지만 만명 정도 되면 이렇게 보여 주기 힘들다.


2. 동일한 친구수를 가진 사람들을 모아서 보여 줍니다. 보통 몇명의 친구들을 가지고 있는지? 

100명의 친구를 가진 사람이 4명이고  3명의 친구를 가진 사람이 3명이면 100:4, 3:3 이렇게 보여주고 싶습니다.
이럴때 코드는 다음과 같이 작성 할 수 있습니다.

import collections

num_firends = [100,40,30,54,25,3,100,100,100,3,3]
friend_counts = collections.Counter(num_firends)
print('friends:', friend_counts)

결과 : ('friends:', Counter({100: 4, 3: 3, 40: 1, 54: 1, 25: 1, 30: 1}))
Counter 를 활용하여 동일한 아이템을 카운팅해 줍니다.

즉 

friend_counts[100] 은 4 이고 
friend_counts[3] 은 3 입니다. 


3. 차트를 이용해서 가시화(visualization) 해서 보여주면 더 좋겠지요?

matplotlib 를 이용하여 손쉽게 막대그래프로 보여줍니다.

#-*- coding: utf-8 -*-

import collections
import matplotlib.pyplot as plt

num_firends = [100,40,30,30,30,30,30,30,30,30,54,54,54,54,54,54,54,54,25,3,100,100,100,3,3]
friend_counts = collections.Counter(num_firends)
print('friends:', friend_counts)

# 가시화 추가

xs = range(101)
ys = [friend_counts[x] for x in xs] # 파이썬에는 이렇게 List구축을 할 수 있습니다. list comprehension 라고 말합니다.

plt.bar(xs,ys)
plt.axis([0,101,0,25])
plt.xlabel("# of friends")
plt.ylabel("# of people")
plt.show()


결과 : 100명의 친구를 가진 사람 숫자 4명에 대한 막대등이 그려져 있습니다. 



4. 가장 많은 친구수는? 가장 적은 친구 수는?  궁금합니다.

당연하게도 이런 통계치가 가장 기본적으로 필요 합니다.   (len, max, min 함수를 사용합니다) 

num_firends = [100,40,30,30,30,30,30,30,30,30,54,54,54,54,54,54,54,54,25,3,100,100,100,3,3]
num_points = len(num_firends)
print num_points # 25

max_value = max(num_firends)
print max_value # 100 
min_value = min(num_firends)
print min_value # 3



5. 조금 변형하여  두번째혹은 세번째 많은 친구 수도 필요 할 때가 있겠지요? 

어떤 연속된 데이터에서 가장 크거나 가장 작은 값이 너무 튈 경우 문제가 생길 수 있습니다. 따라서 두번째 큰 수를 구하고 싶을때가 있는데요 이때는 정렬을 해서 사용할 수 있습니다.

num_firends = [100,40,30,30,30,30,30,30,30,30,54,54,54,54,54,54,54,54,25,3,100,100,100,3,3]

sorted_values = sorted(num_firends)    # 오름차순으로 정렬된 리스트를 반환한다 
second_smallest_value = sorted_values[1]  # 두번째로 작은 값
second_largest_value = sorted_values[-2]  # 두번째로 큰 값 , 파이선에서 -1 은 가장 뒤를 말한다  



6. 회원들의 친구 수의 평균을 알고 싶습니다. 보통 몇명의 친구를 가지고 있을까요?


from __future__ import division # 이것을 써주어야 값이 더 정확하게 계산된다네요. 

num_firends = [100,40,30,30,30,30,30,30,30,30,54,54,54,54,54,54,54,54,25,3,100,100,100,3,3]

def mean(x) :
return sum(x) / len(x)

avgOfFriends = mean(num_firends)
print avgOfFriends

#파이썬 기본 모듈에 평균을 구하는것은 없는 듯 합니다.그래서 주로 numpy를 이용합니다.


7. 친구 수의 중앙값(median)을 알고 싶다. 중앙값은 머죠? 

중간값은 보통  평균보다 더 내가 원하는 바를 잘 말해주곤 하는데요. 미국 모 대학교 지리학과의 84년 졸업생의 평균연봉은 어느 한 졸업생에 의해 크게 상승하였는데... 그 사람은 바로 마이클 조던 입니다. 

그런 특이케이스는 평균이라는 수치를 신뢰하게 어렵게 만듭니다. (회사의 누구 한사람때문에 회사연봉이 크게 올라가는것 처럼) 이럴 경우 중간값을 사용합니다. 

중간값은 최고와 최소가 얼마나 크게 차이가 나는지는 전혀 중요치 않습니다. 

#-*- coding: utf-8 -*-

from __future__ import division

num_firends = [1200,15,10,10,9,4,3,3,2,1]


def mean(x) :
return sum(x) / len(x)

avgOfFriends = mean(num_firends)
print avgOfFriends


def median(v) :
n = len(v)
sorted_v = sorted(v) # 정렬해준뒤에 
midpoint = n // 2 # // 로 나누면 int형이 됨. / 로 나누면 float 

if n % 2 == 1:
return sorted_v(midpoint) # 리스트가 홀 수면 가운데 값 
else :
lo = midpoint - 1 # 짝수면 가운데의 2개의 값의 평균 
hi = midpoint
return (sorted_v[lo]+sorted_v[hi]) / 2



medianOfFriends = median(num_firends)
print medianOfFriends
결과

평균: 125.7
중앙값: 6.5



8. 친구 수라는 데이터의 분포(산포도) 는 어떻게 될까요?  분산, 표준편차등을 알아보아요.

8-1 . 최대,최소 사이의 범위

num_firends = [100,15,10,10,9,4,3,3,2,1]

def data_range(x) :
    return max(x) - min(x)

data_range(num_firends) # 99


8-2. 이상(異常) 치를 제외하고 평범한 데이터 사이의 범위차이

상,하위 25% 사이의 차이

def quantile(x,p) :
    p_index = int(p * len(x))
    return sorted(x) [p_index]

def interquartile_range(x):
    return quantile(x,0.75) - quantile(x,0.25)


8-3. 분산 (variance)  : 데이터들 사이에 얼마나 차이가 큰가? 

import math

num_firends = [100,15,10,10,9,4,3,3,2,1]

def mean(x) :
return sum(x) / len(x)

def dot(v,w) :
return sum(v_i * w_i for v_i, w_i in zip (v,w))

def sum_of_squares(v) :
return dot(v,v)


def de_mean(x) : # 요소들과 평균의 차이
x_bar = mean(x)
return [x_i - x_bar for x_i in x]

def variance(x) : 
n = len(x)
deviations = de_mean(x)
return sum_of_squares(deviations) / (n-1) # n으로 나누기보다 n-1로 나누어야 정확하게 보정됨 (위키참조)


print variance(num_firends)


8-4. 표준편차  (standard deviation) :  분산은 데이터의 차이에 제곱을 해주기때문에 그 값을 sqrt 를 해줘서 현실적으로 바꿔줌.

def standard_deviation(x) :
  return math.sqrt(variance(x)) 



9. 상관 관계 : 두 특성사이의 연관관계를 말해줌. (친구의 수와 연봉간에 연관관계가 있을까요?)

9-1 . 공분산 (covariance) 

다른 예를들어 보면 

- 사람들의 okky 싸이트의 접속 시간의 리스트가 있다고 하자.

- 사람들의 경력기간 리스트가 있다고 하자.

위의 두가지 요소들 즉 접속시간이 길 수록 경력도 길다라는게 말이 될까?  이것을 알고 싶을때 사용하는게 공분산이다.

def covariance(x,y):
    n = len(x)
    return dot(de_mean(x), de_mean(y)) / (n-1)


covariance(connection_time, working_time)

위의 코드의 포인트는  x 요소들의 분산과 y 요소들의 분산을 내적(dot) 하는데 있다.
서로의 분산이 유사한 증가 혹은 감소 패턴을 보인다면 dot 의 크기가 매우 커질 것이다. 
즉 리턴하는 값이 클 수록 상관관계가 있다고 볼 수 있다. ( 하지만 위의 공분산도 데이터의 속성에 따라 잘못된 정보를 즐 수 있음을 유념하자) 


9-2 . 상관관계 (correleation)

def correlation(x,y) :
stdev_x = standard_deviation(x)
stdev_y = standard_deviation(y) 

if stdev_x > 0 and stdev_y > 0 :
return covariance(x,y) / stdev_x / stdev_y
else :
return 0 # 편차가 존재하지 않는다면 상관관계는 0

상관관계는 단위가 없으며, 항상 - 1 에서 1 사이의 값을 갖는다. 예를들어 , 상관관계가 0.25 라면 상대적으로 약한 양의 상관관계를 의미한다. -1 혹은 1 에 가까울때 상관관계가 크다고 말 할 수 있다.



지금까지 통계에 관한 기본적인 내용에 대해 다루어봤습니다. 이러한 통계에는 구멍도 있다는 것을 관련 서적들을 참고 해서 찾아보세요. 다음에 여유가 되면 기초 확률, 통계적 검증(p-value) , 회귀분석, 베이즈 모형 등에 대해서 알아 보겠습니다. 


참고:

그림으로 설명하는 개념쑥쑥 통계학

밑바닥부터 시작하는 데이터 과학

패턴인식

+ Recent posts