관리 메뉴

HAMA 블로그

파이썬 코딩으로 말하는 데이터 분석 - 1. 통계 본문

통계 & 머신러닝 & 딥러닝

파이썬 코딩으로 말하는 데이터 분석 - 1. 통계

[하마] 이승현 (wowlsh93@gmail.com) 2017. 1. 7. 15:03




요즘 데이터분석과 관련하여 신경망(특히 딥러닝) 과 스파크(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) , 회귀분석, 베이즈 모형 등에 대해서 알아 보겠습니다. 


참고:

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

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

패턴인식

Comments