※ 이 포스팅은 아래 도서를 공부하며 정리한 글입니다.

<An Introduction to Statistical Learning (ISL)>, <Data Mining for Business Analytics (DMBA)>, <Applied Predictive Modeling>



Decision Trees, Bagging, Random Forests, Boosting
( 의사결정나무, 배깅, 랜덤 포레스트, 부스팅 알고리즘 )


이번 포스팅은 의사결정나무와 그에 기반한 모형인 배깅, 랜덤포레스트, 부스팅 알고리즘을 공부하며 정리한 글입니다.


Decision Trees, 의사결정나무

의사결정나무 기반 모형의 기초는 출력변수의 값에 따라 입력변수의 공간을 다수의 계층/부분으로 나누고,

각 공간에 대한 출력변수의 최빈값이나 평균 등을 취함으로써 예측을 수행한다.

즉, 의사결정나무는 범주형(분류)과 수치형(예측) 출력변수에 모두 사용 가능하다.


(뒤에서 살펴보겠지만, 결과의 형태가 '나무'와 같아서 '의사결정나무'라고 부른다. / 그림 출처 : 2020mag.com)


의사결정나무의 이러한 알고리즘적인 특성에 의해, 일반적으로 간단하고 해석이 쉬운 모형으로 많이 알려져 있다.

그러나, 그 반대급부로 다른 더 복잡한 모형들보다 예측력이 상당히 떨어질 수 있다.


이러한 문제를 해결하기 위해 사용할 수 있는 모형이 배깅, 랜덤 포레스트, 부스팅 알고리즘이다.

이들은 상대적으로 많은 나무들을 생성하여 집단지성의 형태로 예측을 수행, 예측력을 향상시킨다.

하지만, 반대로 여러 나무를 활용하기 때문에 해석이 어려워지는 단점이 있다.



#. Idea

의사결정나무의 가장 큰 아이디어는, 위에서 언급한 내용과 같이 입력변수의 공간을 서로 겹치지 않는 다차원의 직사각형들로 반복 분할하는 것이다.

결과적으로 위의 나무 그림처럼 기둥으로 시작해서 가지가 점점 얇아지고, 잎사귀까지 가서 예측 값이 산출되는 형태가 된다.


범주형은 뒤에서 다루고, 우선 수치형 출력변수에 대한 모형을 적합하는 문제를 생각해보자.

쉬운 예시를 위해, 입력변수가 2개(X1, X2)만 있는 상황을 가정하면 의사결정나무는 아래 그림과 같이 모형을 형성한다.


( 출처 : ISL )


위 그림에서 R은 입력변수의 분할된 공간으로, 아래 수식과 같다.



수식에서 j와 s는 출력변수의 Residual Sum of Squares(RSS, 아래 수식) 를 최소화하는 특정 값으로 결정한다.

출력변수의 예측값(y_hat)은, 해당 분할된 입력변수 공간(R)에 대응되는 출력변수 훈련데이터의 평균을 이용한다.



여기에 입력변수를 3개로 확장하면, 다음 그림과 같이 분할되는 공간을 3차원으로 표현할 수 있다.


( 출처 : ISL )


입력변수가 범주형인 경우에는, 하나의 값과 나머지 값의 경우로 나누되 가장 낮은 RSS를 산출하는 것을 선택하면 된다.

예를 들어, 4개의 범주 {a, b, c, d}를 갖는 입력변수가 있다고 하자. 이를 두 하위집합으로 나눌 수 있는 7가지 방법이 존재한다.

{a} 와 {b,c,d} / {b} 와 {a,c,d} / {c} 와 {a,b,d} / {d} 와 {a,b,c} / {a,b} 와 {c,d} / {a,c} 와 {b,d} / {a,d} 와 {b,c}


( 참고로, 의사결정나무의 알고리즘 중 C4.5 는 범주의 종류만큼 분할하여 덤불나무와 유사한(bushlike) 구조를 만든다. )


출력변수가 범주형인 경우, 평균 대신 최빈값을 예측값(y_hat)으로 산출한다.

문제는 RSS인데, 분류 오차(classification error) 대신 아래 2가지 '순도(purity)'에 관한 지표를 사용한다.


(1) Gini Index


(2) Cross-entropy


위 수식에서 는 m번째 입력변수 공간 내에서 k번째 분류에 대한 훈련 데이터의 비율(proportion)이다.

두 수식을 잘 살펴보면, 가 0이나 1에 가까울수록 두 지표는 줄어들도록 정의되어 있어 '불순도(비동질성 척도)'라는 의미를 갖게 된다.

두 지표와 분류 오차의 차이점을 다음 그림을 통해 시각적으로 살펴보자.


( 출처 : http://blog.princehonest.com )


초록색은 Cross-entropy를, 붉은색은 Gini Index를, 파란색은 분류 오차다.

모두 분류 비율(x축)에 따른 변화의 양상에 차이는 없으나, 분류 오차보다 순도의 변화에 감도가 더 높기 때문에 사용한다고 알려져 있다.



#. 나무 가지치기 (Tree Pruning)

문제는 의사결정나무에서 입력변수의 공간을 언제까지 분할할 것인가에 있다.

극단적으로 더 이상 나눌 수 없을 때까지 반복적으로 입력변수의 공간을 분할하면, 주어진 훈련 데이터에만 성능이 좋게 되는 과적합(overfit) 문제가 발생한다.

아래 그림을 통해 자세히 알아보자.


( 출처 : 위키피디아 )


위 그래프에서, 붉은색 곡선은 검증 데이터셋에 대한 모형의 오류이고 파란색은 훈련 데이터셋에 대한 오류이다.


이처럼 검증 데이터셋에 대해서는 실제 입력과 출력변수간의 구조를 학습하는 단계까지만 오류가 감소한다.

그러나, 훈련(모델링) 단계에서는 훈련 데이터의 잡음들까지 학습하게 된다.

따라서, 훈련 데이터셋에 대해서는 지속적으로 오류가 감소하지만 어느정도 모형의 복잡도가 오르면 검증 데이터셋의 오류도 높아질 수 밖에 없다.


그렇기 때문에, 적절한 정도까지 나무를 성장(적합, 또는 입력변수 공간의 분할)시키는 것이 좋다.


적절한 분할의 정도를 결정하는 직관적 방법은, 모든 분할의 경우들을 개별 모형으로 산정하여 평가(validation) 데이터셋을 활용해 가장 성능이 좋은 것을 채택하는 것이다.

그러나 입력변수의 수가 많거나 데이터의 구조가 복잡할수록 그 경우의 수도 매우 다양해진다.

따라서 의사결정나무를 최대로 적합(더 이상 나눌 수 없을 때까지)한 다음, 모형의 복잡도를 비용으로 처리하여 의사결정나무의 결과물을 결정하는데 이를 '가지치기'라고 한다.

(입력변수의 공간을 분할할 때마다 가지가 하나 더 생기기 때문에, 가지치기라는 직관적인 이름으로 명명한 것 같다.)


구체적으로, 모형의 복잡도를 비용삼아 최적의 의사결정나무를 정하는 수식은 아래와 같다.



위 수식에서 |T| 는 최대로 적합한 의사결정나무에 존재하는 잎(끝마디, terminal node)의 수로서, 모형의 복잡도를 의미한다.

Rm은 m번째 잎에 대응하는 분할된 입력변수 공간이다.

알파(alpha)는 가지치기를 조정하는 매개변수(tuning parameter)이다.


의사결정나무 알고리즘에서, 잎의 개수별로 위 수식을 계산하여 가장 낮은 값을 산출하는 잎의 개수를 선택한다.


주목할 것은 알파인데, 알파 값이 클수록 수식의 결과도 커져서 최적의 잎 개수가 줄게 된다.

알파가 작아지는 반대의 경우는 수식의 값이 떨어져 최적 잎의 개수가 많아진다.



#. 최적의 가지치기 조정 매개변수 찾기 (find optimal tuning parameter of tree pruning)

알파 값은 어떻게 정할까? 의사결정나무 알고리즘은 K-fold 교차검증법을 활용한다.

방법은 다음과 같다.


(1) 조정 매개변수의 범위를 산정하고, 그 범위에 따라 특정(constant)한 값을 알파 값의 후보로 둔다.

(2) 훈련 데이터셋을 K개 fold 만큼 무작위로 관측수(observation)를 균등하게 나눈다.

(3) 각 k = 1, ... K 에 대해...

(a). k 번째 데이터셋을 훈련 데이터로 삼고 의사결정나무를 최대한으로 적합한다.

(b). 주어진 알파 값마다 위 수식을 활용하여 최적의 의사결정나무를 산출한다.

(c). 각 의사결정나무에 대해, 나머지 K-1개 데이터셋을 평가 데이터로 삼아 예측 오차를 구한다.

(4) 각 알파 값에 따른 예측 오차의 평균을 구한다.

(5) 가장 낮은 평균 예측 오차를 보이는 알파 값을 최적의 조정 매겨변수로 삼는다.


참고로, K는 훈련 데이터 갯수의 배수로 산정 수 있다.

가령 훈련 데이터의 관측(observation) 수가 132 면 K=6 이다. (ISL 310p.)



#. 분류 나무에서, 하나의 가지(branch)에 있는 두 끝마디(잎)의 값이 같은 경우

범주형 의사결정나무 즉, 분류 나무를 적합하다 보면 하나의 가지에 같은 예측 값으로 산출되는 때가 있다.

아래 예제 그림을 살펴보자.


( 출처 : ISL )


가장 왼쪽의 가지와 가장 오른쪽의 가지로 입력변수의 범위가 분할됐지만, 예측 값은 둘 다 같다.

그렇다면 이 가지는 쓸모 없는 것이 아닌가?


예측의 측면에서는 그렇다. 결국 모형의 예측 오차는 그 가지가 있으나 없으나 같기 때문이다.

하지만 이는 분할에 따라 '순도는 달랐으나 최빈값은 같았기 때문'에 발생한 결과임을 참고할 수 있다.



#. 의사결정나무의 장단점

예측력의 측면에서, 같은 훈련 데이터와 검증 데이터에 대해 모형의 예측 오차가 낮은 모형을 선택할 수 있다.

입력변수와 출력변수의 관계에 대한 해석의 측면에서는 두 모형 모두 적합한 후 더 직관적인 모형을 선택할 수 있다.


아래 그림을 살펴보자.


( 출처 : ISL )


윗줄에 해당하는 데이터셋의 경우, 예측력은 물론 해석의 측면 모두 의사결정나무보다 선형모형이 적합하다.

아랫줄은 의사결정나무가 더 나은 경우라고 볼 수 있다.


사실, 위 그림의 예와 같이 극단적인 경우는 드물다.

일반적으로 선형 모형보다는 의사결정나무가 해석이 용이하고 시각화하기 좋은 반면, 예측력은 떨어진다고 볼 수 있다.


나무 기반의 모형은 시스템적으로 자동화하기 매우 간편하며, 결측치를 하나의 경우(case)로 다루기 때문에 결측치에 대한 큰 부담이 없다는 장점이 있다.

더 나아가, 의사결정나무의 상단 부분에서 나타나는 변수들이 예측에 중요한 입력변수들이기 때문에 차원축소나 변수선택에도 활용할 수 있다.

또한, 나무 모형은 관측값(observation)의 절대적 '크기'가 아닌 '순서'를 활용하여 학습하므로 변수변환이 무의미하며 이상치에 대해 강건(robust)하다.


그러나, 이러한 특성은 반대로 데이터의 작은 변화에 따라 완전히 다른 분할 결과를 초래하기도 한다.

나무 모형은 모든 변수들에 대한 모든 가능한 분할을 순서에 따라 계산하기 때문에, 계산량 측면에서 상대적으로 많은 비용이 든다 할 수 있다.



Bagging (or Bootstrap Aggregation), 배깅

배깅(또는 부트스트랩 집합체) 알고리즘의 중심은 부트스트랩에 있다.

부트스트랩은 표준편차나 관심 값을 구하기 어렵거나 아예 불가능한 경우에 사용된다.

하지만 부트스트랩은 통계적 학습(또는 머신러닝)의 성능 향상에도 쓰이며, 배깅이 그 대표적인 예이다.


배깅은 통계적 학습(머신러닝) 결과의 분산을 줄이는 알고리즘이며, 의사결정나무는 다른 모형에 비해 상대적으로 분산이 높기 때문에 종종 활용된다.

따라서, 배깅은 의사결정나무 뿐 아니라 다양한 학습 알고리즘에 활용할 수 있음을 기억하자.



#. Idea

배깅의 기본 아이디어는 각 관측값 집합(set of obs.)이 가진 분산에 평균을 취하면 최종 분산은 줄어드는 점에서 출발한다.

이를 통해 학습 결과의 분산을 줄일 수 있다면, 자연스럽게 학습 모형의 정확도 또한 올라갈 수 있다.


즉, 배깅은 모집단에서 다수의 학습 데이터셋을 취하고 각 학습 데이터셋마다 예측모형을 수립하여 평균을 취하는 아이디어를 활용한다.

이를 수식으로 표현하면 다음과 같다.



위 수식에서 함수(f)는 예측모형을, B는 학습 데이터셋의 총 개수, b는 각 데이터셋을 의미한다.


그러나, 일반적으로 학습 데이터셋을 여러개 얻는 것은 불가능하므로 여기에 부트스트랩을 활용하여 아이디어를 확장한다.

다시말해, 다수의 학습 데이터셋 대신 주어진 학습 데이터셋을 여러번 복원추출(sampling with replacement)하여 활용한다.

이를 다시 수식으로 표현하면 다음과 같다.



위 수식에서 *b는 부트스트랩한 훈련 데이터셋(bootstrapped training data set)을 의미한다.

추가로, 평균을 취할 수 없는 범주형 출력변수의 경우에는 '투표'의 의미로 평균 대신 최빈값을 취하면 된다.


이제 배깅을 의사결정나무에 적용해보자.

지금까지 설명한 것처럼, B개의 부트스트랩한 훈련 데이터셋을 만들고 각 *b의 데이터에 의사결정나무를 수립한다.

이 때, 각 의사결정나무는 가지치기 하지 않는데, 이는 각 모형의 분산을 의도적으로 높게 함과 동시에 편향성(bias)을 낮게 만들기 위함이다.

왜냐하면 배깅의 마지막 단계에서 각 모형에 평균을 취하기 때문에 분산이 낮아지기 때문이다.


참고로 배깅은 부트스트랩을 사용하기 때문에, 예측력 측정을 위해 따로 검증(validation) 데이터셋을 만들거나 cross-validation을 수행하지 않아도 된다.



#. B의 값은 어떻게 정하는가?

정확도의 측면에서 B는 크면 클 수록 좋다. B는 과적합(overfitting)과 상관 없는 부트르스랩 횟수이기 때문이다.

(그러나 실제 비즈니스 등에 배깅을 활용하는 경우 B가 클수록 모형 학습에 필요한 시간이 증가한다는 사실에 주의해야 한다.)



#. 결과의 해석 - Variable Importance Measures

배깅은 여러 나무를 적합(fitting)함으로써 예측력을 높이지만, 반대로 복잡하기 때문에 해석하기 어렵다.


하지만 RSS(Sum of Squared Residuals)나 분류의 경우 Gini index를 활용하여 예측의 측면에서 입력변수의 중요성을 확인할 수 있다.

구체적으로, 각 변수에 대한 나무의 가지로 인해 줄어든 RSS(또는 Gini index) 총 합을 B(부트스트랩한 훈련 데이터셋의 개수)로 나눈다.

이를 입력변수의 중요도(Variable Importance)로 삼아 값이 클 수록 해당 변수가 예측에 중요하다고 해석할 수 있다.


( 위 예시에서는 입력변수 'Thal' 가 예측에 가장 중요하다. 출처 : ISL )


그러나, 배깅 알고리즘에는 나무 상관성(tree correlation)이라는 한계점이 존재하며 이를 보완한 방법이 랜덤포레스트이다.

배깅의 한계점을 포함하여 랜덤포레스트를 정리해보자.



Random Forests, 랜덤포레스트

랜덤포레스트(Random Forests, 직역하면 무작위 숲들)는 나무들간의 관계성을 줄임(decorrelate)으로써 배깅보다 더 나은 성능을 제공한다.

나무들간의 관계성을 줄이는 방법은, 배깅에서 각 *b의 나무를 적합할 때 총 입력변수의 개수보다 적은 입력변수를 무작위로 추출해 활용하는 것이다.



#. Idea

왜 랜덤포레스트는 모든 입력변수를 사용하지 않는가?

예측에 중요한 소수의 입력변수들에 의해 각 *b 나무들의 결과가 서로 비슷해지면, 예측성능이 크게 향상될 수 없기 때문이다.


몇 개(가령 10개)의 입력변수가 있다고 가정해보자. 이 중, 하나의 입력변수 'A'가 예측에 매우 중요하고 나머지는 미미한 역할을 한다.

이 경우 각 *b 배그된(bagged) 나무들 대다수에서 입력변수 'A'가 최상위 가지 분할에 사용될 것이다.

결국 성능 향상을 위해 여러개의 나무를 적합했음에도 불구하고, 대부분의 나무들이 비슷한(correlated) 형태를 갖게 된다.

여러 나무들의 평균을 취하는 배깅의 특성상, 각 예측모형(나무)의 결과가 비슷하면 평균을 취해도 결과가 비슷할 것이다.

즉, 기본적인 의사결정나무 알고리즘 방법보다 높은 성능 향상을 기대하기 어렵다는 뜻이다.


랜덤포레스트는 배깅의 이러한 한계점을 극복하고자 모든 입력변수를 사용하지 않고 적은 수의 입력변수를 무작위로 선택한다.

(아마 Random Forests(직역하면 무작위 숲들) 라는 이름에는 이러한 속성의 의미가 담겨져 있다고 생각한다.)

이를통해 확률적으로 다른 변수들에게 예측에 활용될 기회를 줌으로써 나무들의 다양성을 확보할 수 있게 된다.


입력변수를 무작위로 선택한다는 점에서, 랜덤포레스트는 다중공선성(입력변수끼리 상관관계)이 존재할때 상대적으로 유리한 알고리즘이다.



#. Parameters

랜덤포레스트에서 분석가가 정하는 매개변수는 나무의 수(B)와 각 나무에 사용할 입력변수의 수(*p)이다.

아래 그림을 통해 살펴보자.

( 출처 : http://blog.princehonest.com )


위 그래프에서 연두색은 모든 입력변수를 사용한 경우(결국 배깅 알고리즘이다),

붉은색은 절반을 채택한 경우, 파랑색은 입력변수의 수에 제곱근을 취한 값으로 설정한 결과이다.


즉, 두 매개변수의 설정값에 따라 개별 모형으로 산정하고 모형의 예측력을 비교하여 최적의 값을 찾아내는 것이 하나의 방법이다.

일반적으로 출력변수가 수치형이면 p/3 을, 범주형이면 sqrt(p) 를 사용한다.



#. 결과의 해석 - Variable Importance Measures

랜덤포레스트 또한 마찬가지로 여러 나무를 적합하기 때문에 해석이 어렵지만, 배깅과 같은 방법으로 각 변수별 예측 중요도를 계산할 수 있다.



Boosting, 부스팅

배깅은 각 *b개마다 독립된 나무를 생성하지만, 부스팅은 이전에 적합된 나무의 정보를 활용하면서 생성해 나아간다. (the trees are grown sequentially)

또한 부스팅은 배깅에서 사용하는 부트스트랩 대신, 본래 데이터셋을 변형하여 모형을 적합한다.



#. Idea

부스팅은 모형을 순차적으로 만들어 나아가며 예측 성능을 향상해보자는 아이디어에서 출발한다.

즉, 모형을 적합하고, 여기서 실제 값과 예측 값의 차이인 잔차(residual)를 가지고 다시 모형을 재적합하는 방식을 반복하는 방법을 말한다.

이를 알고리즘적으로 정리하면 다음과 같다.


1. 으로, 그리고 훈련 데이터셋의 모든 개별 관측치에 대해 로 둔다. (r은 residual을, y는 출력변수를 의미한다.)

2. 총 횟수 B에 대해 b=1,2,...B 를 반복 수행한다.

(1) 출력변수를 y가 아닌 r로 삼고, d+1 개의 잎(끝마디, terminal node)를 가진 의사결정나무 를 적합한다.

(2)

(3) 

3. 결과적으로 부스팅 모형의 결과(output)는,  이 된다.


아이디어를 확장하면 부스팅 또한 배깅과 마찬가지로 다양한 학습 알고리즘에 활용할 수 있다.

참고로, 나무 기반의 부스팅을 부스트 나무(Boosted Tree)라고 부르기도 한다.



#. Parameters

부스팅은 B가 클수록 과적합의 문제가 야기될 수 있다. 따라서, 일반적으로 cross-validation을 통해 최적의 B를 추정한다.

부스팅은 랜덤포레스트보다 더 작은 B(smaller trees)로도 예측 성능상 충분한 경우가 많다.

이는 이전에 생성된 나무로부터 결과를 향상시켜 나아가는 부스팅의 알고리즘 때문이다.


람다는 수축 매개변수(shrinkage parameter)로서, 일반적으로 0.01이나 0.001로 설정한다.

아래 그림을 참고해보자.

( 출처 : http://blog.princehonest.com )


좌측은 람다값이 커질수록 훈련 데이터에 대한 오차가 줄지만, 우측의 그림처럼 검증 데이터에 대한 오차는 되려 오차가 커진다.
또한, 람다가 작을수록 많은 학습량(더 큰 B 값)을 요구하게 되는 성질을 염두하며 최적의 값을 선정하는 것이 좋다.

d는 알고리즘의 복잡도를 조정하는 매개변수이다.

개괄적으로 d는 상호작용의 정도(interaction depth and order)라고 생각할 수 있는데, 대부분 d만큼 각 나무에 d개의 변수가 포함되기 때문이다.

가령 각 나무가 그루터기(stump)의 형태(최상위 노드만이 예측에 큰 역할을 하는 경우)일 때 보통 d=1로 두면 좋다.

이런 경우 부스팅은 각 나무에 하나의 변수만 포함되므로 가법(additive) 모형이라고 볼 수 있다.



#. 결과의 해석 - Partial Dependence Plots

부스팅 역시 변수별 예측 중요도를 산출할 수 있다.


여기에 더하여, 부스팅 알고리즘은 '부분 의존 그래프(partial dependence plots)'를 통해 어느정도 출력변수와 입력변수간의 관계를 추론해볼 수 있다.

이는 분석가가 선택한 입력변수의 출력변수에 대한 주변 효과를 산출한 것이다.

(These plots illustrate the marginal effect of the selected variables on the response after integrating out the other variables. : ISL 331p.)


아래 예를 살펴보자.


위의 부분 의존 그래프에 따르면, 좌측의 입력변수 'rm'은 출력변수와 양의 상관관계가 있다고 볼 수 있다.

반대로 우측의 입력변수 'lstat'은 출력변수와 음의 상관관계가 있다고 해석할 여지가 있다.



※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 08. Advanced Visualization (고급 시각화)


다양한 데이터 구조와 알고리즘을 이해하기 쉽도록 시각화 가능하다. 다음 사이트들에 좋은 예시들이 풍부하게 수록되어 있다.
https://visualgo.net/en
https://www.cs.usfca.edu/~galles/visualization

Computer Simulation (컴퓨터 시뮬레이션)

컴퓨터 시뮬레이션의 모델은 알려지지 않거나 분석이 어려운 시나리오를 이해하고 평가하기 위한 방법으로서, 지난 수십 년 이상 인기를 얻은 학문이다.

또한, 컴퓨터 시뮬레이션은 물리학, 화학, 생물학, 경제학, 공학, 심리학, 사회 과학 등 다양한 분야에서 시스템의 수학적 모델링에 매우 생산적인 방법이다.


컴퓨터 시뮬레이션 모델의 장점

- 연구되고 있는 알고리즘 또는 프로세스에 대한 이해의 향상

- 프로세스 및 알고리즘의 문제 영역을 식별

- 알고리즘 / 모델에 관한 특정 변화에 대한 영향력 평가


컴퓨터 시뮬레이션 모델의 유형

- 이산 모델 : 특정 시간에 발생한 시스템의 변화들

- 연속 모델 : 일정 기간에 걸쳐 연속적으로 변화하는 시스템의 상태

- 혼합 모델 : 이산과 연속 요소를 모두 포함


시뮬레이션을 수행하기 위해, 일반적으로 임의의 확률적 입력 값을 사용한다. 이는 시뮬레이션 모델에 대응하는 실제 데이터를 갖기 어려워서이다.



#. Python's Random Package (파이썬의 랜덤 패키지)

파이썬은 여러 편리한 함수들을 가진 random 이라는 패키지를 제공한다.


- 0.0 ~ 1.0 사이 또는 특정 시작과 끝 값 사이의 임의 랜덤 실수(real numbers) 생성

- 숫자의 특정 범위 사이의 임의 랜덤 정수(integers) 생성

- 숫자 또는 문자 목록에서 임의 랜덤값 생성


import random


print random.random() # between 0.0 and 1.0

print random.uniform(2.54, 12.2) # between 2.54 and 12.2

print random.randint(5,10)  # random integer between 5 and 10


print random.randrange(25)  # random number between 0 and 25


#  random numbers from the range of 5 to 500 with step 5

print random.randrange(5,500,5) 


# three random number from the list 

print random.sample([13,15,29,31,43,46,66,89,90,94], 3) 


# Random choice from a list

random.choice([1, 2, 3, 5, 9])



#. SciPy's Random Functions (SciPy의 랜덤 함수)

알고리즘과 수학적 기술을 가진 NumPy 의 확장 버전이 SciPy 패키지다.


NumPy는 Built-in Pseudo Random 생성기를 제공한다. 숫자들은 Pseudo 랜덤이다.

즉, 하나의 Seed 숫자로부터 결정성을 갖고 생성된다. 따라서 같은 Seed 번호를 사용하면 같은 랜덤 숫자들을 생성할 수 있음을 참고하자.


import numpy as np

np.random.seed(10)


각기 다른 랜덤 시퀀스는 Seed 값을 설정하지 않음으로써 생성할 수 있다.

NumPy는 자동으로 랜덤 Seed를 시간 기준으로 선택한다.


np.random.seed()


구간 [0.0, 1.0] 에서 5개의 랜덤값 배열을 다음과 같이 생성할 수 있다.


np.random.rand(5)


Out[]: array([ 0.54340494,  0.27836939,  0.42451759,  0.84477613,  0.00471886])


rand 함수를 이용해 다음 코드처럼 임의의 2차원 배열을 생성할 수 있다.


np.random.rand(2, 4)


Out[]: 

array([[ 0.12156912,  0.67074908,  0.82585276,  0.13670659],

       [ 0.57509333,  0.89132195,  0.20920212,  0.18532822]])


임의의 랜덤 정수를 생성하기 위해 randint(min, max)를 사용할 수 있다.

min과 max는 임의 랜덤 정수를 선택하기 위한 범위다.


np.random.randint(4, 18)


Out[]: 13


Lambda = 8.0 을 가진 이산 포아송 분포(Discrete Poisson distribution)의 임의 랜덤 값은 다음과 같이 생성할 수 있다.


np.random.poisson(8.0)


Out[]: 3


표준편차(sigma) = 3.0, 평균(mu) = 1.25 를 가진 연속 정규분포(Gaussian distribution)의 임의 랜덤 값은 다음과 같이 생성할 수 있다.


np.random.normal(1.25, 3.0)


Out[]: 0.5128392013916669



#. Simulation Examples (시뮬레이션 예제)

첫 번째 예제는 확률 미분 방정식(SDE)로 주가 변동을 모델링하기 위한 기하학적 브라운 운동(Geometric Brownian Motion)이다.

기하학적 브라운 운동은 지수 브라운 운동(Exponential Brownian Motion)으로도 알려져 있다.


위 수식에서 W_t 는 브라운 운동을, mu는 percent drift, sigma는 percent volatility(변동성) 이다.


다음 코드는 브라운 운동을 시각화한 것이다.


from numpy.random import standard_normal

from numpy import zeros, sqrt

import matplotlib.pyplot as plt


S_init = 20.222

T = 1

tstep = 0.0002

sigma = 0.4

mu = 1

NumSimulation = 6


colors = [ (214,27,31), (148,103,189), (229,109,0), (41,127,214), 

(227,119,194),(44,160,44),(227,119,194), (72,17,121), (196,156,148) ]  


# Scale the RGB values to the [0, 1] range.  

for i in range(len(colors)):  

    r, g, b = colors[i]  

    colors[i] = (r / 255., g / 255., b / 255.)  

    

plt.figure(figsize=(12,12))


Steps = round(T/tstep); #Steps in years

S = zeros([NumSimulation, int(Steps)], dtype=float)

x = range(0, int(Steps), 1)


for j in range(0, NumSimulation, 1):

    S[j,0]= S_init

    for i in x[:-1]:

       S[j,i+1]=S[j,i]+S[j,i]*(mu-0.5*pow(sigma,2))*tstep+ \

          sigma*S[j,i]*sqrt(tstep)*standard_normal()

    plt.plot(x, S[j], linewidth=2., color=colors[j])


plt.title('%d Simulation using %d Steps, \n$\sigma$=%.6f $\mu$=%.6f $S_0$=%.6f '

          % (int(NumSimulation), int(Steps), sigma, mu, S_init), fontsize=18)

plt.xlabel('steps', fontsize=16)

plt.grid(True)

plt.ylabel('stock price', fontsize=16)

plt.ylim(0,90)


plt.show()



추가로, 아래 사이트에서 신경망 시뮬레이션 모델링을 수행할 수 있는 파이썬 패키지를 구할 수 있다.

http://briansimulator.org



Animation (애니메이션)

파이썬에서 matplotlib 을 이용해 애니메이션을 만들 수 있다. 

우선, 애니메이션을 생성하기 위한 기본 코드는 다음과 같다.


import numpy as np 

import matplotlib.pyplot as plt 

from matplotlib import animation  


# Set up the figure, axis, and the plot element to be animated 

fig = plt.figure() 

ax = plt.axes(xlim=(0, 3.2), ylim=(-2.14, 2.14)) 

line, = ax.plot([], [], lw=2)


matplotlib 에서 애니메이션 패키지를 정확히 불러오기 위해 다음과 같이 축을 설정하고, 필요한 도식화 변수(비어있는 선)를 준비한다.


# initialization function: plot the background of each frame

def init():

    line.set_data([], [])

    return line,


다음과 같이 모든 애니메이션을 시작하기 전 기본 프레임을 만든다.


# animation function.  This is called sequentially

def animate(i):

    x = np.linspace(0, 2, 1000)

    xval = 2 * np.pi * (x - 0.01 * i)

    y = np.cos(xval) # Here we are trying to animate cos function

    line.set_data(x, y)

    return line,


여기에 입력인 x 와 y 값이 변하는 것으로 정의된 프레임 번호, 도식화되는 변수들의 집합들을 포함하는 에니메이션 함수를 사용한다.


anim = animation.FuncAnimation(fig, animate, init_func=init,\

            frames=200, interval=20, blit=True)

anim.save('basic_animation.html', fps=30)


실제 애니메이션 개체는 FuncAnimation 을 통해서 만들어지며,

init()와 animate() 함수에게 프레임의 개수인 fps(frames per second)와 시간 간격 파라미터를 통해 전달된다.

blit=True 파라미터는 바뀌는 부분만 다시 그려지면서 표시하는 것을 의미한다.


더 많은 예제들은 다음 블로그에서 확인할 수 있다.

http://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/

http://matplotlib.org/examples/animation/


※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 07. Bioinformatics, Genetics and Network Models (생물정보학, 유전학, 네트워크 모델)


이 장에서는 예제들을 다루기 위해 metaseq, NetworkX, matplotlib, Biopython, ETE toolkit 과 같은 라이브러리들을 사용한다.

Directed Graphs and Multigraphs (방향성 그래프와 멀티 그래프)

그래프와 방향성 그래프의 활용성을 알아보기 위해, 간단한 예를 생각해보자.


대학 캠퍼스 지역 내 서로 연결된 컴퓨터는 연결 그래프로 생각할 수 있고, 이 연결 안에서 각 컴퓨터는 노드(Node)나 정점(Vertex)이라 볼 수 있다.

연결된 길은 모서리이고, 어떤 경우 오직 한 방향 연결만 있다면 그것이 방향성 그래프이다.

또 다른 예로, 매우 엄격한 연방 네트워크는 밖에서부터 안으로 들어오는 어떤 연결도 허락하지 않을 것이나, 아마 반대로는 제약이 없을 것이다.


다음 간단한 그래프는 각 장소들 간의 거리를 보여준다.


앞의 예시에서 A부터 F까지 이름 붙여진 도시의 그래프는 방향성 그래프이고, 오른쪽 다른 그래프는 방향성이 없는 그래프이다.

방향성 그래프에서 화살표 방향이 양쪽으로 되어 있다면, 두 방향으로 갈 수 있는 길이 존재하는 것이다.

한편, 비방향성 그래프에서는 양쪽 방향 모두 가정한다. (오른쪽 그래프)


이러한 그래프들을 어떤 데이터 구조를 사용해 표현해야 할까?



#. Storing Graph Data (그래프 데이터 저장)

그래프 데이터는 보통 희소 행렬이나 인접 행렬로 나타내진다.

인접 행렬은 그래프가 V개의 정점(Vertex) 또는 노드(Node)를 갖는다고 가정했을 때, V2개 행을 갖는 행렬이다.


예를 들어, 앞에서 본 두 그래프들은 다음 표와 같은 인접 행렬이다.


 

25 

26 

 

 

 

 

85 

10 

 

26 

85 

 

 

10 

 

 

 

 

11 

 

 

 

88 

 

 

 

11 

88 


 

Chicago 

Boston 

New York 

Wash DC 

Miami 

Dallas 

Chicago 

1613 

 

1145 

 

 

Boston 

1613 

338 

725 

 

 

New York 

 

338 

383 

2145 

 

Wash DC 

1145 

725 

383 

1709 

2113 

Miami 

 

 

2145 

1709 

2161 

Dallas 

 

 

 

2113 

2161 


비방향성 그래프는 대칭적으로 저장공간의 반 정도를 사용하는 것으로 충분하다.

행렬의 빈 성분들은 거리에 관한 충분한 데이터가 있지 않음을 보여준다.


scipy에는 희소 행렬을 편리하게 다룰 수 있는 함수들이 존재한다.

다음 코드는 앞 그림의 첫 그래프에 해당한다.


import scipy.sparse as sparse


matrixA = sparse.lil_matrix((6,6))


matrixA = sparse.lil_matrix([

        [0,25,26,0,0,0],

        [0,0,85,5,10,0],

        [26,85,0,0,0,10],

        [0,0,0,0,0,11],

        [0,0,0,9,0,88],

        [0,0,0,11,88,0]

        ])


print matrixA


  (0, 1)        25

  (0, 2)        26

  (1, 2)        85

  (1, 3)        5

  (1, 4)        10

  (2, 0)        26

  (2, 1)        85

  (2, 5)        10

  (3, 5)        11

  (4, 3)        9

  (4, 5)        88

  (5, 3)        11

  (5, 4)        88


그래프를 보여주기 위해 사용할 수 있는 방대한 파이썬 패키지가 있다. 그 중 많이 사용되고 있는 TOP 3는 NetworkX, igraph, graph-tool 이다.



#. igraph

원래 igraph는 R 언어를 위해 만들어졌는데, 후에 파이썬 버전이 추가됐다.

igraph는 dimacs, dl, edgelist, graml, graphdb, gml, lgl, ncol, pajek 등 몇 가지 포맷들을 제공한다.


GraphML은 XML 기반 파일 포맷이며 큰 그래프에 사용될 수 있다.

NCOL 와 LGL 그래프 포멧은 Weighted Edge List를 가진 큰 그래프에 적합하다.

다른 대부분의 포맷들은 간단한 원문 포맷을 사용한다.

DL 파일 포맷만 igraph에 의해 모두 지원되며, 다른 포맷들은 부분적으로 지원한다.


igraph의 장점은, 편리하며 SVG 포맷으로 결과를 저장해 HTML 파일에 내장할 수 있다는 것이다.

(Anaconda Prompt에서 win-64(Microsoft Windows 64bit OS) 환경에서 다음 명령어를 통해 igraph를 설치할 수 있습니다.)


conda install -c vtraag python-igraph


이제, pajek 포맷을 포함하는 예제를 살펴보자. (http://vlado.fmf.uni-lj.si/pub/networks/pajek/ 참고)


import igraph


vertices = ["A", "B", "C", "D", "E"]


edges = [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,1),

         (1,8),  (8,2),(2,4),(4,9),(9,5),(5,7),(7,0)]


graphStyle = { 'vertex_size': 20}

g = igraph.Graph(vertex_attrs={"label": vertices}, edges=edges, directed=True)

g.write_svg("simple_star.svg", width=500, height=300, **graphStyle)



다음은 파일로부터 그래프 데이터를 읽는 예제 코드이다.

(http://vlado.fmf.uni-lj.si/pub/networks/pajek/data/gphs.htm 에서 데이터 다운로드 가능)


from igraph import read  


g=read("Ragusa18.net",format="pajek")  

  

g.vs["color"]="#3d679d" 

g.es["color"]="red" 


graphStyle={ 'vertex_size': 12, 'margin': 6} 

#graphStyle["layout"]=g.layout("fr")  # optional


g.write_svg("ragusa_graph.svg", width=600, height=600,**graphStyle)



같은 데이터셋에서 다음 코드처럼 원형 모양 등의 레이아웃 옵션을 선택적으로 할당할 수 있다.


graphStyle["layout"]=g.layout("circle")




#. NetworkX

다음은 matplotlib을 통해 방향성 그래프를 만든 예제 코드이다


import matplotlib.pyplot as plt

import pylab

from pylab import rcParams


import networkx as nx


# set the graph display size as 10 by 10 inches

rcParams['figure.figsize'] = 10, 10


G = nx.DiGraph()


# Add the edges and weights

G.add_edges_from([('K', 'I'),('R','T'),('V','T')], weight=3)

G.add_edges_from([('T','K'),('T','H'),('T','H')], weight=4)


# these values to determine node colors

val_map = {'K': 1.5, 'I': 0.9, 'R': 0.6, 'T': 0.2}

values = [val_map.get(node, 1.0) for node in G.nodes()]


edge_labels=dict([((u,v,),d['weight'])

                 for u,v,d in G.edges(data=True)])


#set edge colors

red_edges = [('R','T'),('T','K')]

edge_colors = ['green' if not edge in red_edges else 'red' for edge in G.edges()]


pos=nx.spring_layout(G)


nx.draw_networkx_edges(G,pos,width=2.0,alpha=0.65)

nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels)


nx.draw(G,pos, node_color = values, node_size=1500,

 edge_color=edge_colors, edge_cmap=plt.cm.Reds)


pylab.show()

nx.is_directed_acyclic_graph(G) 

.


이 그래프는 NetworkX를 사용해 그래프의 시각미와 모서리 굵기를 조절한 것이다.

방향성 그래프를 보여주는 여러 방법 중 NetworkX는 방향을 화살표 대신 끝을 두꺼운 막대로 표시한다.


추가로 최단 경로부터 연결수 분포, 클러스터링 계수까지 NetworkX는 다양한 그래프 분석 방법을 제공한다.
다음 코드는 최단 경로를 보여주는 간단한 예제 코드이다.

import networkx as nx


g = nx.Graph()

g.add_edge('m','i',weight=0.1)

g.add_edge('i','a',weight=1.5)

g.add_edge('m','a',weight=1.0)

g.add_edge('a','e',weight=0.75)

g.add_edge('e','h',weight=1.5) 

g.add_edge('a','h',weight=2.2)


nx.draw(g)


print nx.shortest_path(g,'i','h')


['i', 'a', 'h']


다음 예제는 레미제러블 소설에 등장하는 인물들의 관계를 나태낸다.

(https://gephi.org/datasets/lesmiserables.gml.zip 으로부터 데이터셋을 받을 수 있으며, GML 포맷 데이터로 되어있다.)


import networkx as nx

from pylab import rcParams

rcParams['figure.figsize'] = 12, 12


G = nx.read_gml('./lesmiserables.gml')

G8 = G.copy()

dn = nx.degree(G8)


#for n in G8.nodes():

#  if dn[n] <= 8:

#    G8.remove_node(n)


pos= nx.spring_layout(G8)

nx.draw(G8, node_size=10, edge_color='b', alpha=0.45, font_size=9, pos=pos)

labels = nx.draw_networkx_labels(G8, pos=pos)




#. The Planar Graph Test (평면 그래프 테스트)

평면 그래프는 교차하는 가장자리(Intersecting Edge) 없이 평면에 그려질 수 있는 그래프이다.

평면 그래프를 그리기 위해 정점으로부터 시작해 가장자리(edge)와 엣지(edge)를 그리고, 그려지는 면(face)들을 계속해서 추적해야 한다.


다음은 평면 그래프의 간단한 예이다.



오일러의 공식은 정점, 가장자리(edge), 면(face)들을 연결한다.

오일러에 따르면, 유한 연결 평면 그래프(Finite and Connected Planar Graph)가 교차하는 가장자리(edge) 없이 평면에 그려졌을때 다음 식이 성립한다.

v - e + f = 2 (v : 정점의 수, e = 가장자리 수, f = 면의 수)



#. The Directed Acyclic Graph Test (방향성 비순환 그래프 테스트)

방향성 비순환 그래프(DAG, Directed Acyclic Graph)란, 예로 정점 A로부터 B로의 엣지들은 특정 방향(A -> B or B -> A)을 지시하고 비순환성을 지닌다.

비순환 그래프는 순환이 없다. 다시말해, 사이클이 없는 것을 의미한다.


NetworkX 패키지의 is_directed_acyclic_graph(Graph)는 시각화의 맥락에서 그래프가 비순환인지 아닌지를 체크하는 방법을 제공한다.


import matplotlib.pyplot as plt

import pylab

from pylab import rcParams


import networkx as nx


# set the graph display size as 10 by 10 inches

rcParams['figure.figsize'] = 10, 10


G = nx.DiGraph()


# Add the edges and weights

G.add_edges_from([('K', 'I'),('R','T'),('V','T')], weight=3)

G.add_edges_from([('T','K'),('T','H'),('T','H')], weight=4)

# these values to determine node colors

val_map = {'K': 1.5, 'I': 0.9, 'R': 0.6, 'T': 0.2}

values = [val_map.get(node, 1.0) for node in G.nodes()]


edge_labels=dict([((u,v,),d['weight'])

                 for u,v,d in G.edges(data=True)])


#set edge colors

red_edges = [('R','T'),('T','K')]

edge_colors = ['green' if not edge in red_edges else 'red' for edge in G.edges()]


pos=nx.spring_layout(G)


nx.draw_networkx_edges(G,pos,width=2.0,alpha=0.65)

nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels)


nx.draw(G,pos, node_color = values, node_size=1500,

 edge_color=edge_colors, edge_cmap=plt.cm.Reds)


pylab.show()


nx.is_directed_acyclic_graph(G) 


True




#. Maximum Flow and Minimum Cut (최대 플로우와 최소 컷)

'플로우 네트워크'는 소스로부터 목적지까지 방향성을 가진 그래프이며 각 엣지를 따라 할당된 용량(Capacity)이 주어진다.

최단 경로를 찾기 위해 방향성 그래프로 거리 지도를 모델링하는 것과 비슷하게, 플로우 네트워크로 방향성 그래프를 해석할 수 있다.

예시로는 파이프를 통해 흐르는 액체, 전기 네트워크를 통과하는 전류, 데이터 통신 네트워크를 통해 전송되는 데이터 등이다.



그래프 G의 엣지들은 엣지가 얼만큼의 플로우를 지원할 수 있는지에 대한 용량을 가지고 있다. 용량이 없다면 무한으로 가정한다.

위 그래프에서 플로우 네트워크 G의 최대 플로우는 4이다.


NetworkX 패키지의 maximum_flow_value(Graph, from, to)는 그래프의 최대 플로우를 계산한다.


import networkx as nx

G = nx.DiGraph()

G.add_edge('p','y', capacity=5.0)

G.add_edge('p','s', capacity=4.0)

G.add_edge('y','t', capacity=3.0)

G.add_edge('s','h', capacity=5.0)

G.add_edge('s','o', capacity=4.0)


flow_value = nx.maximum_flow_value(G, 'p', 'o')


print "Flow value", flow_value


Flow value 4.0


...

※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 06. Statistical and Machine Learning (통계 및 머신러닝)


최근 머신러닝은 인공지능에서 가장 중요한 부분이 됐다.
연산 속도 등 컴퓨팅 성능의 발전으로 인해, 머신러닝을 통해 인공지능 시스템을 구축할 수 있게 될 가능성이 매우 높아졌다.

Linear Regression (선형 회귀)

선형 회귀분석은 주어진 입력변수(X)와 반응변수(Y)간의 관계를 선형 관계식으로 모형화하는 것이다.

다음 주어진 데이터에 대해 선형 회귀분석을 실시. 목표 응답변수(target response variable, Y)는 acceptance 이다.



import pandas as pd

import statsmodels.formula.api as smf

from matplotlib import pyplot as plt


df = pd.read_csv('./sports.csv', index_col=0)

fig, axs = plt.subplots(1, 3, sharey=True)

df.plot(kind='scatter', x='sports', y='acceptance', ax=axs[0], figsize=(16, 8))

df.plot(kind='scatter', x='music', y='acceptance', ax=axs[1])

df.plot(kind='scatter', x='academic', y='acceptance', ax=axs[2])


# 적합된 모형을 생성

lm = smf.ols(formula='acceptance ~ music', data=df).fit()


X_new = pd.DataFrame({'music': [df.music.min(), df.music.max()]})

preds = lm.predict(X_new)


df.plot(kind='scatter', x='music', y='acceptance', figsize=(12,12), s=50)


plt.title("Linear Regression - Fitting Music vs Acceptance Rate", fontsize=20)

plt.xlabel("Music", fontsize=16)

plt.ylabel("Acceptance", fontsize=16)


# 최소제곱선 도식화

plt.plot(X_new, preds, c='red', linewidth=2)




선형 회귀분석을 사용하기 위한 많은 수학적 파이썬 라이브러리들이 있다.

주로 scikit-learn, seaborn, statsmodels, mlpy 들이 사용되고 기억할 만한 것들이다.

참고 : http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html




Decision Tree (의사결정나무)

날씨에 따라 야외활동을 하는 것이 좋은가 나쁜가의 예제를 살펴본다.


from sklearn.externals.six import StringIO

from sklearn import tree

import pydot 


X=[[1,1,1,0],[1,1,1,1],[2,1,1,0],[2,3,2,1],[1,2,1,0],[1,3,2,0],\

[3,2,1,0],[3,3,2,0],[3,3,2,1],[3,2,2,0],[1,2,2,1],[2,2,1,1],\

[2,1,2,0],[3,2,1,0]]  


Y=[0,0,1,1,0,1,1,1,0,1,1,1,1,0] 


clf = tree.DecisionTreeClassifier()

clf = clf.fit(X, Y)


dot_data = StringIO() 

tree.export_graphviz(clf, out_file=dot_data) 


graph = pydot.graph_from_dot_data(dot_data.getvalue()) 

graph.write_pdf("game.pdf")




Naive Bayes Classifier (나이브 베이즈 분류기)

나이브 베이즈 분류법의 선행으로 베이즈 정리를 이해할 필요가 있다.


#. 베이즈 정리 (The Bayes Theorem)

다음 그림처럼 모든 사람이 U라는 전체 공간에 속하고,

유방암에 걸린 사람들의 집합을 A, 표본 검사에서 유방암으로 진단 받은 사람들의 집합을 B라고 가정하면 A∩B의 교집합이 존재한다.



2개의 다른 공간에 주목할 필요가 있다.

(1) B - A∩B : 유방암 진단에서 양성 판정을 받았으나, 실제 유방암에 걸리지 않은 집단

(2) A - A∩B : 유방암 진단에서 음성 판정을 받았으나, 실제 유방암에 걸린 집단


여기서 우리가 알고 싶은 정보가, 양성 판정을 받은 사람이 실제로 유방암에 걸렸을 확률이라고 하자.

수학적으로 표현한다면, B(유방암 진단을 받음)가 주어졌을 때 A(실제 유방암에 걸림)인 조건부 확률이라고 할 수 있다.



(즉, B에 대한 정보가 있기 때문에 확률 계산의 전체 공간이 U에서 B로 좁혀지게 되는 것입니다.)


비슷하게, 암을 가지고 있는 사람에 대해 알고 있을 때 암진단 테스트가 양성 반응으로 나올 확률은 다음과 같다.



따라서, 베이즈 정리를 다음과 같이 유도할 수 있다.



이 베이즈 정리는 A와 B 사건에 대해 B가 일어날 확률이 0이 아닐 경우에 유용하다.



#. 나이브 베이즈 분류기 (Naive Bayes Classifier)

나이브 베이즈 분류 기법은 입력 자유도(변수의 개수)가 높을 때 사용하기 적절하다.

상대적으로 간단하지만, 좋은 성능을 나타낸다.

(참고 : http://scikit-learn.org/stable/modules/naive_bayes.html 및 http://sebastianraschka.com/Articles/2014_naive_bayes_1.html )


다음 예제를 살펴보자.


빨간색 : 유방암에 걸린 집단

파란색 : 양성 진단을 받은 집단

흰색    : 새로운 사람



이 데이터를 통해 우리가 알 수 있는 정보는 다음과 같다.




#. TextBlob 라이브러리를 활용한 나이브 베이즈 분류기

TextBlob은 문자처리를 위한 도구들의 모음이다.

또한, NLP(자연어 처리, Natural Language Processing)를 위한 API를 제공해 분류, 명사구 추출, 대화 일부분 태깅, 감정 분석 등을 할 수 있다.


아나콘다 프롬포트(Anaconda Prompt)에서 다음 명령어를 통해 TextBlob를 설치할 수 있습니다.

conda install -c conda-forge textblob


그 다음, 예제 분석을 위한 말뭉치(Corpus)를 다음과 같이 다운받을 수 있다. (이 또한 하나콘다 프롬포트에서 진행합니다.)


python -m textblob.download_corpora


TextBlob을 이용해 고유의 문자 분류기를 쉽게 만들 수 있다.

TextBlob 0.6.0 버전 기준으로 다음 분류기들을 사용할 수 있다.


- BaseClassifier

- DecisionTreeClassifier

- MaxEntClassifier

- NLTKClassifier

- NaiveBayesClassifier

- PositiveNaiveBayesClassifier


다음 예제는 NaiveBayesClassifier를 활용한 감성 분석이다.


from textblob.classifiers import NaiveBayesClassifier

from textblob.blob import TextBlob


train = [

    ('I like this new tv show.', 'pos'),

    ('This is a very exciting event!', 'pos'),

    ('I feel very good about after I workout.', 'pos'),

    ('This is my most accomplished work.', 'pos'),

    ("What an awesome country", 'pos'),

    ("They have horrible service", 'neg'),

    ('I do not like this new restaurant', 'neg'),

    ('I am tired of waiting for my new book.', 'neg'),

    ("I can't deal with my toothache", 'neg'),

    ("The fun events in costa rica were amazing",'pos'), 

    ('He is my worst boss!', 'neg'),

    ('People do have bad writing skills on facebook', 'neg')

]

test = [

    ('The beer was good.', 'pos'),

    ('I do not enjoy my job', 'neg'),

    ("I feel amazing!", 'pos'),

    ('Mark is a friend of mine.', 'pos'),

    ("I can't believe I was asked to do this.", 'neg')

]


cl = NaiveBayesClassifier(train)

print(cl.classify("The new movie was amazing."))  # "pos"

print(cl.classify("I don't like ther noodles."))   # "neg"


print "Test Results"

cl.update(test)


# Classify a TextBlob

blob = TextBlob("The food was good. But the service was horrible. "

                "My father was not pleased.", classifier=cl)

print(blob)

print(blob.classify())


for sentence in blob.sentences:

    print(sentence)

    print(sentence.classify())


# Compute accuracy

print("Accuracy: {0}".format(cl.accuracy(test)))


# Show 5 most informative features

cl.show_informative_features(10)




Viewing Positive Sentiments using Word Clouds (워드클라우드를 이용해 긍정적인 감정 분석)

워드클라우드는 문서에 대한 각 단어들의 빈도를 잘 표현하는 것으로 매우 유명하다.

단어의 등장 횟수에 따라 글자의 크기를 바꿔 시각화한다. 가장 자주 나타난 단어의 크기가 가장 크게 표현된다.


아나콘다 프롬포트(Anaconda Prompt)에서 다음 명령어를 통해 wordcloud 패키지를 설치할 수 있습니다.

conda install -c conda-forge wordcloud


다음 예제 코드에서 STOPWORDS 는 a, an, the, is, was, at, in 과 같은 분석적으로 의미 없는 단어들을 제외하기 위한 단어 모음이다.


from wordcloud import WordCloud, STOPWORDS

import matplotlib.pyplot as plt

from os import path


d = path.dirname("__file__")

text = open(path.join(d, './results.txt')).read()


wordcloud = WordCloud(

    #font_path='/Users/MacBook/kirthi/RemachineScript.ttf',

    stopwords=STOPWORDS,

    background_color='#222222',

    width=1000,

    height=800).generate(text)


# Open a plot of the generated image.

plt.figure(figsize=(13,13))


plt.imshow(wordcloud)

plt.axis("off")


plt.show()




#. K-근접 이웃 (K-nearest Neighbors, KNN)

KNN 분류법은 가장 이해하기 쉬운 분류 방법 중에 하나이며, 부분적으로 데이터 분포의 사전 정보가 적거나 없을 때 사용된다.


사과, 배, 그리고 바나나 분류에 대한 다음 데이터와 예제를 살펴보자.



import csv

import matplotlib.pyplot as plt


count=0

x=[]

y=[]

z=[]


with open('./fruits_data.csv', 'r') as csvf:

  reader = csv.reader(csvf, delimiter=',')

  for row in reader:

    if count > 0:

      x.append(row[0])

      y.append(row[1])

      if ( row[2] == 'Apple' ): z.append('r')

      elif ( row[2] == 'Pear' ): z.append('g')

      else: z.append('y')

    count += 1


plt.figure(figsize=(11,11))


recs=[]

classes=['Apples', 'Pear', 'Bananas']

class_colours = ['r','g','y']

plt.title("Apples, Bananas and Pear by Weight and Shape", fontsize=18)


plt.xlabel("Shape category number", fontsize=14)

plt.ylabel("Weight in ounces", fontsize=14)


plt.scatter(x,y,s=600,c=z)



위 사진은 주어진 데이터를 시각화 한 것이다.

( 빨강색 : 사과 / 초록색 : 배 / 노랑색 : 바나나 )


만약 종류를 알 수 없는 과일에 대한 정보가 있을 때, 그것을 예측하는 예제 코드는 다음과 같다.


from math import pow, sqrt


dist=[]


def determineFruit(xv, yv, threshold_radius):

  for i in range(1,len(x)):

    xdif=pow(float(x[i])-xv, 2)

    ydif=pow(float(y[i])-yv, 2)

    sqrtdist = sqrt(xdif+ydif)

    if ( xdif < threshold_radius and 

         ydif < threshold_radius and sqrtdist < threshold_radius):

      dist.append(sqrtdist)

    else:

      dist.append(99)


  pear_count=0

  apple_count=0

  banana_count=0


  for i in range(1,len(dist)):

      if dist[i] < threshold_radius:

        if z[i] == 'g': pear_count += 1

        if z[i] == 'r': apple_count += 1

        if z[i] == 'y': banana_count += 1


  if ( apple_count >= pear_count and apple_count >= banana_count ):

    return "apple"

  elif ( pear_count >= apple_count and pear_count >= banana_count):

    return "pear"

  elif ( banana_count >= apple_count and banana_count >= pear_count):

    return "banana"


dist=[]

determine = determineFruit(3.5,6.2, 1)

print determine


banana




Logistic Regression (로지스틱 회귀분석)

범주형 반응변수(Y=1 or 0)의 경우, 선형 회귀분석보다 시그모이드 함수를 활용하는 로지스틱 회귀분석이 더 적합하다.

왜냐하면, 입력변수와 계수의 곱의 합으로 구성된 선형모형은 그 범위가 ±∞ 이지만, 시그모이드 함수는 (0, 1) 이기 때문이다.


아래 수식은 p개의 입력변수에 대한 Y의 확률을 시그모이드 함수로 표현한 것이다.



파이썬에서 표준 시그모이드 함수를 그려보면, 왜 이 함수를 사용하는지 조금 더 쉽게 이해할 수 있다.


import matplotlib.pyplot as plt

import numpy as np


plt.title("Sigmoid Functions vs LineSpace")


x = np.linspace(-10,10,100)


y1 = 1.0 / (1.0+np.exp(-x))

plt.plot(x,y1,'r-',lw=2)


y2 = 1.0 / (1.0+np.exp(-x/2))

plt.plot(x,y2,'g-',lw=2)


y3 = 1.0 / (1.0+np.exp(-x/10))

plt.plot(x,y3,'b-',lw=2)


plt.xlabel("x")

plt.ylabel("y")



붉은색(1) --> 초록색(1/2) --> 파랑색(1/10) 순으로 x의 계수가 낮다는 점에 주목하자.

붉은색의 경우에 값이 커짐에 따라 y가 1일 확률이 급격하게 높아지지만, 파랑색의 경우에는 경사가 완만함을 알 수 있다.

또한 값이 항상 (0, 1) 범위에 있어 해당 범주에 속할 확률로 사용이 가능함을 알 수 있다.


이제, Kaggle에서 제공한 '타이타닉 생존자 예측' 위한 데이터셋을 예제로 로지스틱 회귀분석을 적용해보자.


import numpy as np

import pandas as pd

import sklearn.linear_model as lm

import sklearn.cross_validation as cv

import matplotlib.pyplot as plt


train = pd.read_csv('./titanic_train.csv')

test = pd.read_csv('./titanic_test.csv')

train[train.columns[[2,4,5,1]]].head()


   Pclass     Sex   Age  Survived

0       3    male  22.0         0

1       1  female  38.0         1

2       3  female  26.0         1

3       1  female  35.0         1

4       3    male  35.0         0


data = train[['Sex', 'Age', 'Pclass', 'Survived']].copy()

data['Sex'] = data['Sex'] == 'female'

data = data.dropna()


data_np = data.astype(np.int32).values

X = data_np[:,:-1]

y = data_np[:,-1]


female = X[:,0] == 1

survived = y == 1


# This vector contains the age of the passengers.

age = X[:,1]


# We compute a few histograms.

bins_ = np.arange(0, 121, 5)

S = {'male': np.histogram(age[survived & ~female], 

                          bins=bins_)[0],

     'female': np.histogram(age[survived & female], 

                            bins=bins_)[0]}

D = {'male': np.histogram(age[~survived & ~female], 

                          bins=bins_)[0],

     'female': np.histogram(age[~survived & female], 

                            bins=bins_)[0]}

bins = bins_[:-1]

plt.figure(figsize=(15,8))

for i, sex, color in zip((0, 1),('male', 'female'), ('#3345d0', '#cc3dc0')):

    plt.subplot(121 + i)

    plt.bar(bins, S[sex], bottom=D[sex], color=color,

            width=5, label='Survived')

    plt.bar(bins, D[sex], color='#aaaaff', width=5, label='Died', alpha=0.4)

    plt.xlim(0, 80)

    plt.grid(None)

    

    plt.title(sex + " Survived")

    plt.xlabel("Age (years)")

    plt.legend()

    

(X_train, X_test, y_train, y_test) = cv.train_test_split(X, y, test_size=.05)

print X_train, y_train


# Logistic Regression from linear_model

logreg = lm.LogisticRegression();

logreg.fit(X_train, y_train)

y_predicted = logreg.predict(X_test)


plt.figure(figsize=(15,8));

plt.imshow(np.vstack((y_test, y_predicted)),

           interpolation='none', cmap='bone');

plt.xticks([]); plt.yticks([]);

plt.title(("Actual and predicted survival outcomes on the test set"))


logreg.coef_

array([[ 2.40922883, -0.03177726, -1.13610297]])






Support Vector Machine (서포트 벡터 머신, SVM)

SVM은 수치형 또는 범주형 반응변수(Y) 모두 적용 가능한 지도학습 알고리즘으로서, 경험적으로 좋은 성능을 제공한다고 알려져있다.

생물정보학(Bioinformatics), 문자, ㅇ므성 인식 등의 여러 범위에서 성공적인 비선형(Non-linear) 모형의 확장 알고리즘으로 자리잡고 있다.

SVM은 계산이 복잡하지 않고 구현이 쉽다는 장점이 있으나, Underfitting의 경향이 있어 정확도가 낮을 수 있다는 단점이 있다.


다음은 scikit-learn(skilearn) 라이브러리를 활용한 파이썬 예제이다.


import numpy as np

from sklearn.svm import SVR

import matplotlib.pyplot as plt


X = np.sort(5 * np.random.rand(40, 1), axis=0)

y = (np.cos(X)+np.sin(X)).ravel()

y[::5] += 3 * (0.5 - np.random.rand(8))


svr_rbfmodel = SVR(kernel='rbf', C=1e3, gamma=0.1)

svr_linear = SVR(kernel='linear', C=1e3)

svr_polynom = SVR(kernel='poly', C=1e3, degree=2)

y_rbfmodel = svr_rbfmodel.fit(X, y).predict(X)

y_linear = svr_linear.fit(X, y).predict(X)

y_polynom = svr_polynom.fit(X, y).predict(X)


plt.figure(figsize=(11,11))

plt.scatter(X, y, c='k', label='data')

plt.hold('on')

plt.plot(X, y_rbfmodel, c='g', label='RBF model')

plt.plot(X, y_linear, c='r', label='Linear model')

plt.plot(X, y_polynom, c='b', label='Polynomial model')

plt.xlabel('data')

plt.ylabel('target')

plt.title('Support Vector Regression')

plt.legend()

plt.show()





k-Means Clustering (k-평균 군집분석)

k-평균 군집분석은 데이터셋의 어떤 k 개의 영역들의 중심을 최선으로 표현할 수 있는 데이터셋의 k 개의 지점들을 찾아낸다.

이 알고리즘은 수행 전 군집의 숫자(k)를 분석가가 미리 정해주어야 한다는 단점이 있다.

그러나 k-평균 군집분석은 군집분석에 매우 많이 사용되는 방법이고, 어떤 가정도 요구하지 않는 강점을 가지고 있다.


k-평균 군집분석의 알고리즘을 간단하게 정리하면 다음과 같다.

- n개의 점 (x, y) 집합과 k개의 센트로이드 집합이 주어진다. (초기에는 랜덤하게 배정됨)

- 각 점 (x, y)에 대해, 그 지점에서 가장 가까운 센트로이드를 찾아 해당 점을 소속시킨다.

- 각 군집에서 중앙값을 찾고 이 값을 다음 k개의 센트로이드 집합으로 설정한다.

- 센트로이드 집합의 값이 변하지 않을 때까지 계속 반복한다.


다음 예제는 scikit-learn 라이브러리를 사용해 k-평균 군집분석을 구현한 파이썬 코드이다.


import matplotlib.pyplot as plt


from sklearn.cluster import KMeans


import csv


x=[]

y=[]


with open('./cluster_input.csv', 'r') as csvf:

  reader = csv.reader(csvf, delimiter=',')

  for row in reader:

      x.append(float(row[0]))

      y.append(float(row[1]))


data=[]

for i in range(0,120):

  data.append([x[i],y[i]])


plt.figure(figsize=(10,10))


plt.xlim(0,12)

plt.ylim(0,12)


plt.xlabel("X values",fontsize=14)

plt.ylabel("Y values", fontsize=14)


plt.title("Before Clustering ", fontsize=20)


plt.plot(x, y, 'k.', color='#0080ff', markersize=35, alpha=0.6)


kmeans = KMeans(init='k-means++', n_clusters=3, n_init=10)

kmeans.fit(data)


plt.figure(figsize=(10,10))


plt.xlim(0,12)

plt.ylim(0,12)


plt.xlabel("X values",fontsize=14)

plt.ylabel("Y values", fontsize=14)


plt.title("After K-Means Clustering (from scikit-learn)", fontsize=20)


plt.plot(x, y, 'k.', color='#ffaaaa', markersize=45, alpha=0.6)


# Plot the centroids as a blue X

centroids = kmeans.cluster_centers_


plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', s=200,

  linewidths=3, color='b', zorder=10)


plt.show()






※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 05. Financial and Statistical Models (금융과 통계 모형)


( 이전 포스팅에 이어서 .. )

An Overview of Statistical and Machine Learning (통계 및 머신러닝 개요)

머신러닝은 인간이 수행하기에 충분히 간단한 분류식들을 생성하는 것을 목표로 한다.

머신러닝은 결정 프로세스에 대한 통찰력을 제공하기 위해 인간의 추론을 충분히 모방해야만 한다.

통계와 유사하게 배경 지식은 개발 단계에서 활용될 수 있다.


통계 학습과 머신 러닝의 차이는 추론 중심이냐 예측 중심이냐의 차이라고 볼 수 있다.



#. k-최근접 이웃 (k-Nearest Neighbor)

k-NN은 훈련 데이터로부터 모델을 구축하지 않는, 알고리즘이다. 

k-NN의 접근 방법은 다음과 같다.

- 어떤 방법이든 사용해 데이터를 수집

- 거리 계산에 필요한 수치 값을 준비

- 계산된 k-NN 이웃 검색에 대한 몇 가지 작업을 수행하고, 상위 k의 가까운 이웃을 식별


from numpy import random, argsort, sqrt

from pylab import plot, show

import matplotlib.pyplot as plt


def knn_search(x, data, K) :

    ndata = data.shape[1]

    K = K if K < ndata else ndata

    # 다른 지점으로부터의 유클리드 거리

    sqd = sqrt(((data - x[:,:ndata])**2).sum(axis=0))

    idx = argsort(sqd) # 정렬

    # k-NN 인덱스 반환

    return idx[:K]


data = random.rand(2,200)   # 랜덤 데이터 셋

x = random.rand(2,1)        # 쿼리 포인트


neig_idx = knn_search(x, data, 10)


plt.figure(figsize=(12, 12))


# 데이터와 입력 포인트를 플로팅한다.

plot(data[0,:], data[1,:], 'o', x[0, 0], x[1, 0], 'o', color='#9a88a1', markersize=20)

     

# 이웃들을 하이라이팅

plot(data[0, neig_idx], data[1, neig_idx], 'o', markerfacecolor='#bbe4b4', markersize=22, markeredgewidth=1)


show()




#. 일반화 선형 모델 (Generalized Linear Models)

회귀분석은 변수들 사이의 관계를 추정하는데 쓰이는 통계 프로세스다. 구체적으로, 회귀는 독립변수 중 하나가 변화할 때 종속 변수의 전형적인 값이 어떻게 변화하는지 이해하는 데 도움이 된다.


그 중 선형 회귀분석은 보간법(interpolation)을 적용할 수 있는 고전적인 방법이나 예측력은 부족하다. 또한, 이상치와 교차 상관관계에 민감하다.

선형 회귀모형은 목표 값(y)을 입력변수(x1, x2, ... xn)의 선형결합으로 다음과 같이 추정한다.

베이즈 회귀는 분리한 추정의 일종이며, 전통적인 선형 회귀보다 더 유연하고 안정적이다.

왜 베이즈를 사용하는가? 베이즈 모델은 더 유연하고, 작은 샘플에서 더 정확하며, 이전의 정보를 포함할 수 있기 때문이다.



#. 베이즈 선형 회귀 (Bayesian Linear Regression)

먼저, 선형 회귀를 위한 그래픽 모델을 살펴보자.

주어진 값을 다음과 같이 설정한다.



목표는 이 데이터를 모델링하여 다음과 같은 함수 식을 마련하는 것이다.

여기서 w는 가중치 벡터이고, 위의 식에 나타낸 것처럼 각 Yi는 정규분포를 따르게 된다.

Yi는 랜덤변수고, 데이터로부터 랜덤변수 Yi=yi의 각 조건의 변수 x값을 바탕으로 새로운 변수 x값을 대응하는 y를 예측할 수 있다.


import numpy as np

import matplotlib.pyplot as plt

from scipy import stats


from sklearn.linear_model import BayesianRidge


np.random.seed(0)

n_samples, n_features = 200, 200


X = np.random.randn(n_samples, n_features) # 가우시안 데이터

# 정밀도 4로 가중치를 만듦

theta = 4.

w = np.zeros(n_features)


# 관심 있는 8개의 가중치만 유지

relevant_features = np.random.randint(0, n_features, 8)

for i in relevant_features :

    w[i] = stats.norm.rvs(loc=0, scale=1. / np.sqrt(theta))

    

alpha_ = 50.

noise = stats.norm.rvs(loc=0, scale=1. / np.sqrt(alpha_), size=n_samples)

y = np.dot(X, w) + noise


# 베이즈 능형 회귀 (Bayesian Ridge Regression) 에 맞춤

clf = BayesianRidge(compute_score=True)

clf.fit(X, y)


# 가중치, 예측 및 히스토그램 가중치를 시각화

plt.figure(figsize=(11, 10))

plt.title("Weights of the model", fontsize=18)

plt.plot(clf.coef_, 'b-', label="Bayesian Ridge estimate")

plt.plot(w, 'g-', label="Training Set Accuracy")

plt.xlabel("Features", fontsize=16)

plt.ylabel("Values of the weights", fontsize=16)

plt.legend(loc="best", prop=dict(size=12))


plt.figure(figsize=(11, 10))

plt.title("Histogram of the weights", fontsize=18)

plt.hist(clf.coef_, bins=n_features, log=True)

plt.plot(clf.coef_[relevant_features], 5 * np.ones(len(relevant_features)),

         'ro', label="Relevant features")

plt.ylabel("features", fontsize=16)

plt.xlabel("Values of the weights", fontsize=16)

plt.legend(loc="lower left")

plt.show()





#. 애니메이션 및 인터랙티브 플롯 작성

Bokeh, Plotly, VisPy 등의 인터랙티브한 그래프 작성 도구들이 있다.


Bokeh는 인터랙션하는 부분을 쉽게 하기 위해 자바스크립트와 D3.js로 matplotlib 객체를 시각화할 수 있으며, 큰 데이터셋에서도 좋은 성능을 제공한다.


#import bokeh

#bokeh.sampledata.download() # 이 2개 라인은 최초만 실행하면 됨


from bokeh.sampledata import us_counties, unemployment

from bokeh.plotting import figure, show, output_file

from bokeh.models import HoverTool


import collections

from collections import OrderedDict


county_coordinate_xs=[

        us_counties.data[code]['lons'] for code in us_counties.data

        if us_counties.data[code]['state'] == 'ca'

        ]

county_coordinate_ys=[

        us_counties.data[code]['lats'] for code in us_counties.data

        if us_counties.data[code]['state'] == 'ca'

        ]


colors = ["#e6f2ff", "#cce5ff", "#99cbff", "#b2d8ff", "#73abe5", "#5985b2"]

county_colors = []

for county_id in us_counties.data :

    if us_counties.data[county_id]['state'] != 'ca' :

        continue

    try :

        rate = unemployment.data[county_id]

        idx = min(int(rate/2), 5)

        county_colors.append(colors[idx])

    except KeyError :

        county_colors.append("black")


output_file("california.html", title="california.py example")


TOOLS="pan, wheel_zoom, box_zoom, reset, hover, save"

p = figure(title="California Unemployment 2009",

           width=1000, height=1000, tools=TOOLS)


p.patches(county_coordinate_xs, county_coordinate_ys,

          fill_color=county_colors, fill_alpha=0.7,

          line_color="white", line_width=0.5)


mouse_hover = p.select(dict(type=HoverTool))

mouse_hover.point_policy = "follow_mouse"

mouse_hover.tooltips = collections.OrderedDict([

        ("index", "$index"), ("(x, y)", "($x, $y)"),

        ("fill color", "$color[hex, swatch]:fill_color"),])


show(p)



Plotly는 인터랙티브한 그래프 시각화를 제공하는 또 다른 옵션이다.

그러나 참고로, 온라인상에 있어야 하고 Plotly 계정이 필요하다.


VisPy는 파이썬과 OpenGL을 사용해 만들어진 고성능 인터랙티브 도구로, 최신 GPU 성능을 제공하는 라이브러리이다.




(빅데이터의 다음 단계는) 예측 분석이다 - 에릭 시겔 저 (옮긴이 고한석, 이지스퍼블리싱)





Introduction


#. 테스코(Tesco, 영국 식료품 체인)

13개국에 진출해 있는 수많은 점포의 계산대에서 연간 1억 장의 맞춤식 할인쿠폰을 발행

소비자들이 어떤 할인쿠폰들을 사용할지 예측, 할인쿠폰 사용률 이전 방식 대비 3.6배 증가


#. FedEx

고객들이 경쟁업체로 넘어갈 위험성 예측의 정확성을 65%에서 90%로 높여 업계 선두 유지


#. Allstate

보험에 가입한 차량의 특성에 근거, 자동차 충돌 시 신체적 피해 경향을 예측, 개선하여 연간 4천만$ 보험지급금 절약


#. Citizens Bank

예측 기술을 통해 수표 사기에 의한 손실을 20% 줄임


#. HP

가짜 제품보증서에 의한 수리 및 교환 요청을 탐지하여 6,600만 $ 절감


#. 예측 효과(The Prediction Effect) : 예측은 굳이 매우 정확할 필요가 없다

기업들은 이미 Mass Marketing(불특정 다수를 대상으로 하는 마케팅) 이라는 일종의 숫자 게임을 하고 있다.

그렇기에 예측을 통해서 기업은 게임의 균형을 미묘하게, 그러나 의미 있는 수준 안에서 자기에게 유리한 쪽으로 기울어지게 할 수 있다.

어렴풋하게라도 볼 수 있는 것이 완전히 깜깜한 암흑 상태보다는 훨씬 우수한 결과를 낳는다.




Chapter 02 - 힘이 커질수록 책임도 커진다


#. Target 쇼핑몰 (대형 유통업체) - 임신 예측

쇼핑몰의 신생아 등록 할인판매 이벤트 : 자신의 임신 여부와 출산 예정일 기재 유도, 데이터 확보

cf) 내가 기차에서 노인 또는 임산부에게 자리를 양보한 경우, 임신이나 나이와 같은 민감한 개인 데이터를 유추하려고 노력한 것인가? 아니면 그저 그 사람이 필요로 하는 바를 제공하려고 노력한 것인가?

귀납(Induction, 구체적 사실들로부터 일반적 원칙을 논리적으로 이끌어내는 것)적 추론에 죄를 뒤집어 씌우는 것은 마치 사고를 불법화하는 것과 같다. 귀납적 추론이란 무언가를 파악해 내는 행위와 전혀 다를 바 없다.

** 연역(Deduction,일반적인 것으로부터 구체적인 것, 또는 원인으로부터 결과를 논리적으로 이끌어내는 것)


#. 그것이 위엄하다고 여겨지는 사실 그 자체가 바로 그것이 가진 힘

페이스북 사용자 약 1/4는 프라이버시에 대한 우려로 가짜 데이터를 입력함


#. 윤리적 딜레마

법 집행에 예측 분석을 활용하는 경우


어떤 한 사람과 일정한 특징을 공유하는 다른 사람들이 취한 행위들에 근거, 그 사람이 나쁜 행위를 예측하는 것은 부당한가?
vs
인간도 마찬가지로 경험으로부터 배우기 때문에 한 개인의 미래 범죄 가능성에 대해 인간이 예측하는 것 또한 본질적으로는 그 이전에 다른 사람들을 대상으로 관찰한 것에 근거한 것이 아닌가?


범죄 리스크 모델은 작은 수의 특징들만을 가지고 과거의 범죄자를 지극히 제한된 관점으로 축소시킴으로써 비인간화한다.
vs
하지만 애당초 교도소에 수감되는 것 자체가 인권의 제한이므로 예측 분석을 통해 전반적인 범죄율을 낮추며 교도소 관련 예산을 절약할 수 있다면?


의사결정을 형식화하고 계량화하게 되면, 의도와는 달리 소수에 대한 편견을 심어줄 수 있다.




Chapter 03 - 데이터 효과


#. 상관관계 != 인과관계

예측 분석을 적용함에 있어 일반적으로 인과관계에 대해 알 수 없지만, 굳이 신경쓸 필요도 없다.

많은 예측 분석 프로젝트는 미래를 예측하는 데 목적이 있는 것이지, 세상을 이해하고 무엇이 그렇게 작동하게 만드는지를 알아내는 것이 아니다.

그러니 그저 제대로 작동하기만 하면 된다. 예측은 해석보다 우위를 차지한다. 예측 분석은 해결책을 찾아 제시하는 것에 목표를 두고 수행한다.

예측 분석이 종종 다양한 사회과학적 통찰력을 제공해 주기도 하지만, 그러한 것들은 예측 분석의 부산물이지 주요한 목적은 아니다.




Chapter 04 - 학습하는 기계


#. 향상도 (Lift)

예측 모델의 성능을 비교하는 하나의 측정치(metric). 예측 승수(Predictive Multiplier)라고 할 수 있음. 모델을 적용하지 않았을 때와 비교해 어떤 특정한 모델을 적용했을 때 얼마나 더 많은 목표(target) 고객을 파악해 낼 수 있는지 알려줌.


#. 체이스 은행의 부동산 담보대출 조기상환 예측 시스템

CART 의사결정나무 방법을 활용, 각기 다른 특성을 가지는 부동산 담보대출 건들을 위해 특화된 모델을 수립.

건들을 각각의 조건에 따라 그룹별로 나눈 후 각 그룹마다 각기 다른 의사결정 나무를 생성함.

나무마다 서로 다른 상황을 대상으로 한 것이었기 때문에 나무들은 상당히 다양한 모습(고유한 변수들을 서로 다른 방식으로 포함)을 띠게 됨.

도입 첫해에 6억 $ 이익 추가 창출, 조기상환 대출 건 중 74% 정확히 인식

이후 JP모건 인수, 자산 기준 미국 최대의 은행으로 성장




Chapter 05 - 앙상블 효과


#. 넷플릭스 영화 추천 시스템

고객이 선택하는 영화의 70%는 온라인 추천 시스템에 의한 것으로 매우 중요한 역할을 하고 있음


#. Meta-learning (메타 학습)

학습한 것을 토대로 학습하는, 앙상블 모형의 근간.


#. 집단 지성 효과

여러 모델들은 각각 장점과 단점이 있다. 마치 사람들의 추측처럼 예측 모델들이 만들어낸 각각의 예측 점수들은 불완전하다. 어떤 것은 너무 높고 어떤 것은 너무 낮다.

그러나 모델들을 혼합, 각 점수의 평균을 내면 오류의 상당 부분을 제거할 수 있다.


#. Bagging (Bootstrap Aggregating, 자루 담기)

앙상블 모델에서 중요한 방법론 중 하나. 서로 다른 데이터 하위집합을 토대로 모델을 구축, 한 자루 가득 다양성을 지닌 모델들을 만들어 냄.


#. 앙상블 효과 (The Ensemble Effect)

예측 모델들이 하나의 앙상블로 합쳐지면서 각자의 한계를 보완하게 되어, 전체로서의 앙상블은 자신의 구성요소 모델들보다 더 정확하게 예측할 가능성이 높아짐.




Chapter 06 - 인간을 이긴 기계학습


#. IBM Watson

질문/답을 하나의 예측 분석 응용의 틀로 바라봄 --> 대답의 정확성을 예측.

방식 : '문서 및 DB' 등으로부터 잠재적 답안(데이터) 수집 --> '수천 개의 후보 답안' 추출 --> '예측 모델'을 적용해 가장 우수한 답을 골라냄.

질문 유형(퍼즐, 사지선다, 날짜, 숫자, 번역, 어원 등)에 따라 상이한 모델을 활용

왓슨에게 가장 우수한 결과는 '로지스틱 회귀분석(Logistic Regression)'에서 나왔음




Chapter 07 - 숫자를 통한 설득


#. Churn Modeling (이탈 모델링)

경험이 있는 경영자라면 누구나 현재의 고객을 유지하는 것이 비즈니스의 기본이자 가장 중요한 일이라고 말할 것임.

왜냐하면 새로운 고객을 확보하는 것보다 기존 고객을 그대로 머물도록 설득하는 것이 비용 측면에서 더 싸게 먹히기 때문.

고객 손실률을 조금이라도 줄이면 고객 기반이라는 풍선은 더 부풀어 팽창률이 늘어남.

즉 회사의 고객기반 성장률이 늘어나는 것으로, 고객기반 성장은 비즈니스의 기본임.


#. 예측의 역설

예상 이탈고객에게 계약을 갱신하고 혜택을 받으라는 접촉 = 약정기간이 끝나가니 이제 고객님은 자유롭게 갈아탈 수 있다는 사실을 상기시켜 준 것

결국, 고객의 이탈 가능성을 오히려 더 높게 만드는 행휘. 잠자는 사자의 코털을 건드리지 말자.

--> 따라서, 무엇을 예측해야 하는가라는 근원적인 질문에, 단순히 이탈 예측을 넘어 고객이 어떻게 반응할 것인지 2차적 예측이 필요함.

그러므로 우리는 무엇이 효과적일 것인가 뿐 아니라, 무엇이 역효과를 가져올 것인가를 고려해야 함.


* 물건을 구입한 사람들 중 우리가 접촉하지 않았더라도 우리 물건을 구입했을 사람은 누구인가? 원래부터 구입 의향이 있었던 것은 아닌가?


#. Uplift Model (향상 모델 = 설득 모델) ★

대상을 접촉하는 것이 좋은지 여부를 결정하는 것보다, 그를 접촉하는 것이 접촉하지 않는 것보다 과연 더 좋은지 여부를 결정하는 방법

경쟁하는 2개의 조치들로부터 발생한 결과들을 기록한 서로 다른 훈련용 데이터 집합들을 예측을 목적으로 모델링하는 것


무작위 A/B 통제 실험(테스트)의 개념과 예측 모델링 방법을 영리하게 결합한 것.

* 설득 가능성은 하나의(오직) 집단에 걸쳐 집합적으로 관찰될 수 있으나, 한 사람에 대해서는 절대로 성립할 수 없다.


'향상 점수'는 이러한 조치가 또 다른 조치와 비교할 때 우리가 원하는 결과를 발생시킬 가능성이 얼마나 더 많은가란 질문의 대답이다.

* Lift는 무작위 선별 대비 타게팅 선별 방식의 효과성 vs Uplift는 조치 대상 집단이 그렇지 않은 집단에 비해 얼마나 효과적인가


이를 통해 접촉에 의해 일어난 반응을 예측하는 것이 아닌, 고객을 접촉한 후의 구매를 예측한다.

ex) Uplift Trees(향상 나무)


확실한 고객과 접촉 불가 고객을 제외함으로써, 향상 모델링은 전체 타기팅 대상의 100%보다 적게 접촉하면서도 100%보다 많은 반응을 거둘 수 있는 힘을 갖게 해준다 - Kathleen Kane (Principal Decision Scientist of Fidelity Investments)

ex) 383p


세계 7위 규모 이통사 텔레노르 : 전통 이탈 분석 모델링 대비 마케팅 캠페인 ROI 11배 증가 + 고객 이탈률 36% 감소 + 비용 40% 감소


오바마 재선 : 일정한 수의 유권자들에 대해 선거운동 접촉을 미리 실험하고 반대로 통제집단을 선별하여 아무런 조치를 하지 않았다.
이후 두 집단을 대상으로 설문조사를 진행하여 이들이 결국 투표소에 갔을 때 오바마를 지지할 것인지 여부를 알아보았다.
이를 바탕으로 각 경합지역 주별로 설득 모델을 구축하였다. 변수는 인구사회학적 특징, 과거 투표 참가 이력, 잡지 구독 등 80가지 이상.




그 외


#. 분석 사례들 : 164 ~ 171p, 273 ~ 291p


#. 대응변수(Proxy Variable)
    수집 대상의 값을 직접 얻을 수 없지만, 간접적으로 알아볼 수 있도록 하는 변수.
    어떤 특정한 변수에 대해 직접적으로 획득이 곤란하거나 사용이 어려운경우, 혹은 반영이 제대로 이루어지지 않는 경우에 원래 변수 대신하여 사용되는 변수를 의미한다. 이렇게 대리 변수를 사용하는 경우 선택된 대리변수와 원래의 변수 사이에는 밀접한 상관관계가 있어야 한다.
    삶의 질이나 생활수준과 같은 수치화 하기 어려운 변수를 사용하고자 할때 이를 직접적으로 계산 하기에는 어려움이 따르므로, 1인당 GDP를 해당 변수 대신하여 사용하는 경우 등이 있다. (출처 : 위키피디아)


#. 예측이란, 미지의 사물에 대해서 불완전하게 추론하는 것


#. 많은 산업 분야에서 기업들이 비슷한 제품들을 제공하고 비슷한 테크놀로지를 사용하는 시대에 있어서 차별화의 마지막 남은 지점들 중 하나는 고효율의 비즈니스 프로세스이다 - 『분석으로 경쟁하라 (Competing on Analytics) - Thomas Davenport, Jeanne Harris』


#. 지식의 유일한 원천은 경험이다. - 알버트 아인슈타인



※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 05. Financial and Statistical Models (금융과 통계 모형)


( 이전 포스팅에 이어서 .. )

확률론적 모형 (Stochastic Model)

지금까지는 임의성(Randomness)을 고려하지 않은 결정론적 모형을 살펴보았다. 지금부터는 임의성을 고려한 모형을 생각해보자.


#. 몬테카를로 시뮬레이션 (Monte Carlo Simulation)

확률 시뮬레이션으로도 불리며, 어느 예측 모형에든 위험성이나 불확실성의 영향을 활용하는 방법(기술)이다.

예측 모형에는 임의성에 대한 가정들이 포함되어 있다. 이는 포트폴리오상의 투자 수익률, 또는 작업 수행 시간에 대한 가정일 수 있다.

이렇게 임의성에 대해 모형을 세우고 미래에 투영함으로써 기대값(Expected Value)을 예측하는 것이다.


몬테카를로 시뮬레이션은 난수(Random Value)들을 입력하여 결정론적(Deterministic) 모형을 반복적(보통 100,000 ~ 수백 만 회 이상)으로 평가하는 방법이다.

모형이 복잡하고, 비선형적(Non-linear)이며 불확실한 모수(Parameter)가 하나 이상 포함하는 경우에 자주 사용된다.


결정론적 모형과의 차이점을 살펴보자. 결정론적 모형은 일관된 결과를 도출하지만, 

확률론적 모형은 확률밀도함수에서 비롯되며 도출되는 결과 또한 확률적이다.

위의 그림을 설명하면 이렇다. 우선 랜덤 입력 x1~x3에 대한 모형의 함수 f(x1,x2,x3)을 설정한 후, 여러개의 무작위 집합 {x1, x2, x3}을 생성한다.

각 입력값을 모형에 적용한 후, 평가하는 과정을 모든 값(가령 1만 개면 1만 개 까지)에 적용한다.

결과값들을 분석하고, 마지막으로 가장 가능성이 높은 것을 고른다. 몬테카를로 시뮬레이션은 난수 생성에 매우 크게 의존한다.


예를 들어, A 라는 농구 팀이 이길 확률을 알고 싶다면, 농구라는 상황에 대한 확률론적 가정 아래 수 많은 무작위 입력값들을 생성하는 몬테카를로 시뮬레이션을 통해 추정해볼 수 있겠다.

이와 같이 몬테카를로 시뮬레이션은 대다수의 현실 상황에 적용하여 문제를 해결할 수 있습니다.



#. 몬테카를로 시뮤레이션 예시 1 - 재고 관리 모형 (An Inventory Model for Monte Carlo Simulation)

한 과일 소매 판매상은 과일을 팔고 매일 주문한다. 

매 단위의 매출 당 $0.6의 이익이 발생하고, 그 날 안에 팔리지 않으면 단위당 $0.4의 손실이 발생한다.

수요 D는 [80, 140] 범위에서 균등하게 분포한다고 가정하자.

이 때, 소매상의 수익을 최대로 하기 위해서는 얼마나 주문해야 좋을까?


수익을 P, 판매량을 s, 수요를 d라고 한다면 다음과 같이 수식을 세울 수 있다.

* 만약 수요가 재고보다 많으면 수요량 만큼만 판매되며, 수요량이 재고에 못미치면 폐기된다는 전제를 하고 있는 것 같습니다.

* 즉, 의사결정의 주체인 소매상은 주문량을 결정할 수 있지만 수요량은 임의성(Randomness)이 존재함을 적용한 것입니다.


이를 바탕으로 파이썬을 통해 다음과 같이 시뮬레이션하면, 최대 기대 수익과 최적의 주문량을 구할 수 있다.

(제가 임의로 수정한 것으로, 책의 예제 코드와 다소 상이할 수 있습니다.)


import numpy as np

import matplotlib.pyplot as plt


# 수익식

def generateProfit(d):

    global s

    if d >= s:

        return 0.6*s

    else: 

        return 0.6*d - 0.4*(s-d)

    

# 시뮬레이션 시작

x = []

y = []


# 주문량 10 ~ 200 까지 시행

for s in range(10, 200):

    profit = []

    

    # n = 1,000 회 시뮬레이션 시행 // 횟수가 많을 수록 정밀해지나 거의 비슷하다.

    for i in range(1, 1000):

        

        # 균등분포(Uniform distribution)으로 정수의 수요량 난수를 생성한다.

        d = np.random.randint(low = 80, high = 140)

        # 이 수요량 난수 d에 대한 수익 계산

        profit.append(generateProfit(d))

        

    # x 축을 따라 그려질 s(주문량) 저장

    x.append(s)

    

    # y 축을 따라 그려질 평균 수익 저장

    y.append(np.mean(profit))

    

plt.grid(True)

plt.xlabel('Volume of Orders')

plt.ylabel('Average Profits')

plt.plot(x,y)


print 'Max Average Profit:', max(y)

print 'Optimal Volume of Orders:', x[y.index(max(y))]


Max Average Profit: 58.8266266266

Optimal Volume of Orders: 112



실제 계산을 통해서 시뮬레이션이 얼마나 합리적인지 확인해보자.


시뮬레이션 결과는 112로, 실제 계산값인 116과 매우 흡사하다. 난수 개수를 늘리면 더더욱 실제 계산값과 가까워진다.

* 더 복잡한 상황들과 가정을 적용했을 때, 계산보다 시뮬레이션이 용이할 수 있습니다.



#. 몬테카를로 시뮬레이션 예시 3 - 농구 게임 (A Basketball Game Model for Monte Carlo Simulation)

Khan Academy에서 Lebron James의 질문을 해결해보자.

" 3점을 뒤지고 있는 상태에서 단 30초가 남았을 때, 어려운 3점 슛을 시도하는 것이 나은가 아니면 쉬운 2점 슛과 추가 점유를 시도하는 것이 나은가? "


여기에 필요한 몇 가지 파라미터들을 다음과 같이 (쉽게 풀이하기 위해 간단히) 정의한다.

우리팀 : threePtPercent , twoPtPercent

상대팀 : oppTwoPtPercent , oppFtPercent (프리드로우 전력 퍼센티지)


상대 팀의 프리드로우 퍼센티지(oppFtPercent)가 높을 수록, 3포인트 슛이 유리하다.

반대로 oppFtPercent 값이 낮아지면? 이를 시뮬레이션해보자.


import numpy as np

import matplotlib.pyplot as plt


colors = [(31, 119, 180), (174, 199, 232), (255, 127,  14), 

          (255, 187, 120), (44, 160, 44), (214,  39,  40), (148,103,189),

          (152, 223, 138), (255,152,150), (197, 176, 213), (140, 86, 75),

          (196, 156, 148), (227,119,194), (247, 182, 210), (127,127,127),

          (199, 199, 199),(188,189, 34),(219, 219, 141), (23, 190,207),

          (158, 218, 229),(217,217,217)]


# RGB 값들을 matplotlib이 받을 수 있는 [0, 1] 범위로 스케일

for i in range(len(colors)) :

    r, g, b = colors[i]

    colors[i] = (r / 255., g / 255., b / 255.)


# 3점 시도

def attemptThree() :

    if np.random.randint(0, high=100) < threePtPercent :

        if np.random.randint(0, high=100) < overtimePercent :

            return True # 게임 승리

        return False    # 3점에 실패하거나 시간 초과로 게임 패배


# 2점 시도

def attemptTwo() :

    havePossession = True

    pointsDown = 3

    timeLeft = 30

    while (timeLeft > 0) :

            

        # 점유권을 가지고 있다면?

        if (havePossession) :

            # 우리가 3점 이상 뒤지고 있다면, 2점을 빠르게

            # 우리가 2점 이하로 뒤지고 있다면, 우리는 먼저 시간을 끌어야

            if (pointsDown >= 3) :

                timeLeft -= timeToShoot2

            else :

                timeLeft = 0

                

        # 슈팅을 해야 할까?

        if (np.random.randint(0, high=100) < twoPtPercent) :

            pointsDown -= 2

            havePossession = False

        else :

            # 상대팀이 리바운드를 한다면? 우리는 점유권을 잃는다

            # 그러나 우리가 시간을 끌게 되면, 이것은 문제가 되지 않는다.

            if (np.random.randint(0, high=100) >= offenseReboundPercent) :

                havePossession = False

            else : # 우리가 점유권을 갖지 않는 경우

                if (pointsDown > 0) : # 점유권을 되찾기 위해 파울

                    # 파울을 위한 시간

                    timeLeft -= timeToFoul

                    # 상대 팀이 두 개의 프리드로우 획득

                    if (np.random.randint(0, high=100) < oppFtPercent) :

                        pointsDown += 1

                    if (np.random.randint(0, high=100) < oppFtPercent) :

                        pointsDown += 1

                        havePossession = True

                else : 

                    if (np.random.randint(0, high=100) >= ftReboundPercent) :

                        havePossession = True

                    else :

                        # 동점 또는 이기고 있어 파울을 원하지 않음.

                        # 상대팀이 시간 끄는 것을 가정

                        if (np.random.randint(0, high=100) < oppTwoPtPercent) : 

                            pointsDown += 2 # 상대팀이 2점 기록

                        timeLeft = 0

        if (pointsDown > 0) :

            return False

        else :

            if (pointsDown < 0) :

                return True

            else :

                if (np.random.randint(0, high=100) < overtimePercent) :

                    return True

                else : 

                    return False

    

# 시각화

plt.figure(figsize=(14,14))

names=['Lebron James', 'Kyrie Irving', 'Steph Curry', 'Kyle Krover', 'Dirk Nowitzki']

threePercents = [35.4, 46.8, 44.3, 49.2, 38.0]

twoPercents =   [53.6, 49.1, 52.8, 47.0, 48.6]

colind=0


for i in range(5) : # 각각 수행할 수 있음

    x=[]

    y1=[]

    y2=[]

    trials = 400 # 시뮬레이션 실행 시도 수

    threePtPercent = threePercents[i]   # 3점 슛 만들 기회 %

    twoPtPercent = twoPercents[i]       # 2점 슛 만들 기회 %

    oppTwoPtPercent = 40                # 상대팀이 2점 슛 만들 기회 %

    oppFtPercent = 70                      # 상대팀의 프리드로우 %

    timeToShoot2 = 5                       # 2점 슛을 위해 경과된 초

    timeToFoul = 5                          # 상대팀을 파울하는 데 경과된 초

    offenseReboundPercent = 25        # 공격 리바운드 %

    ftReboundPercent = 15                # FT를 놓친 후 공격 리바운드 %

    overtimePercent = 50                  # 연장선에서 우승 확률 %

    

    winsTakingThree = 0

    lossTakingThree = 0

    winsTakingTwo = 0

    lossTakingTwo = 0

    curTrial = 1

    

    while curTrial < trials :

        # 3점 획득 시도 실행

        if (attemptThree()) :

            winsTakingThree += 1

        else :

            lossTakingThree += 1

        

        # 2점 획득 시도 실행

        if attemptTwo() == True :

            winsTakingTwo += 1

        else :

            lossTakingTwo += 1

        

        x.append(curTrial)

        y1.append(winsTakingThree)

        y2.append(winsTakingTwo)

        curTrial += 1

    

    plt.plot(x, y1, color=colors[colind], label=names[i]+" Wins Taking Three Point", linewidth=2)

    plt.plot(x, y2, color=colors[20], label=names[i]+" Wins Taking Two Point", linewidth=1.2)

    colind += 2

    

legend = plt.legend(loc='upper left', shadow=True,)

for legobj in legend.legendHandles :

    legobj.set_linewidth(2.6)

    

plt.show()



모든 경우에서 상대 팀의 프리드로우 비율이 높기 때문에(70%), 모든 선수들에 대해 몬테카를로 시뮬레이션은 3점 득점을 제안하고 있다.



#. 변동성 그래프 (The Volatility Plot)

표준편차는 평균 주위에서의 변화량 또는 흩어짐 정도를 측정하는 통계 용어로 변동성의 척도이다.

이처럼 정의에 의해, 흩어짐의 정도란 실제 값과 평균 값 사이의 차이이다.

가까운 값들을 바탕으로 변동성을 그래프로 살펴보기 위해 주어진 시작 일로부터 어떻게 볼 수 있는지,

어떻게 특정 주식에 대해 수행하는지 예제(IBM, 2005.10.01. ~)로 살펴보자.


import pandas_datareader as stockdata

import numpy as np


ibmquotes = stockdata.DataReader(name='IBM', data_source='yahoo', start='2005-10-01')


ibmquotes['Volatility'] = np.log(ibmquotes['Close'] / ibmquotes['Close'].shift(1))


ibmquotes[['Close', 'Volatility']].plot(figsize=(12,10),subplots=True)



주가 변동성은 다양한 특징적 피크점을 가질 수 있는 가격의 변동이다.

지수(exponential) 함수는 시간에 따른 성장을 보여주고,

로그(log, 지수의 역) 함수는 성장에 이르기 위한 시간을 측정할 수 있게 한다.





※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 05. Financial and Statistical Models (금융과 통계 모형)


금융과 경제 모형들은 확률과 통계를 사용하며 데이터의 단순화와 추상화를 돕는다.

모형에 대해서 논하기 전에, 데이터를 살피는 것은 항상 중요하다는 사실을 잊지 말자.
즉, 데이터 분석의 첫 단계에서는 반드시 데이터를 시각화 해보는 일이 필요하다.
불량 데이터, 아웃라이어, 결측치 등과 같은 문제들은 데이터 시각화를 통해 쉽게 검출할 수 있기 때문이다.
이들은 가능하면 모두 정정하거나 삭제해야 마땅하나, 주식 시장과 같이 특별한 경우에는 아웃라이어가 큰 의미를 지닌 경우도 있다는 점에 유의하자.

이 장에서는 몇 가지 모형의 예제들을 살펴볼 것이다. 이 외에도 유용한 애플리케이션이 많이 존재한다.

그 전에, 기본적인 금융 문제를 파이썬으로 시각화하여 해결하는 방법을 간단한 예시를 통해 알아보자.

결정론적 모형 (Deterministic Model)

투자의 궁극적 목적은 수익을 내는 것이며, 투자의 손익은 가격의 변동과 보유한 자산 수에 달려 있다.


#. 총 수익률

- 시간(현재 시점) t에 투자 양을 Pt라고 가정하면, 총 수익률 Rt를 다음과 같이 간단하게 표현할 수 있다.

   즉, Pt = 10, P_t+1 = 10 이라면, R_t+1 = 0.06 = 6% 이다.


- 더 나아가, 최근 k년 동안의 수익률은 다음 식과 같이 1년 수익률(t-k부터 t까지)의 곱이다.


- Rt에 해당하는 인플레이션을 Ft라 정의하면, 위 식을 다음과 같이 정리할 수 있다.

   즉, 인플레이션을 고려하지 않으면(Ft = 0) 직전 식과 같아진다.


- 또한, 배당금과 같은 추가적인 수익이 있다면 다음과 같이 수익률을 정리할 수 있다.


#. 파이썬으로 위 수식을 적용해 문제를 풀어보자.

- 2010년 초기에 $10,000를 투자했고 수익률이 6%라면, 몇 년이 지나야 투자금액의 2배($20,000)가 될까?

(단, 인플레이션과 다른 수익은 고려하지 않는다. --> Ft = Dt = 0)

- 저는 2배가 되는 시점을 시각적으로 쉽게 찾기 위해, 'y = 20,000' 직선을 추가하여 교차하는 지점을 확인해봤습니다.

(참고로, 책에서는 y = 2x 그래프? 를 그리라고 했습니다.)

import matplotlib.pyplot as plt


principle_value = 10000 # 투자 금액

grossReturn = 1.06       # Rt


return_amt = []

x = []

year = 2010

return_amt.append(principle_value)

x.append(year)


for i in range(1,15):

    return_amt.append(return_amt[i-1] * grossReturn)

    print 'Year-', i, ' Returned:', return_amt[i]

    

    year += 1

    x.append(year)

    

# 눈금이 보이도록

plt.grid()


# return값들의 커브 그리기

plt.plot(x, return_amt, color='r')

plt.plot(x, [20000]*len(x), color='b')


   약 2022년 즈음에 목표 수익($20,000)을 달성할 수 있을 것으로 기대할 수 있다.


#. 모기지론의 적용

- 금융기관에서 $350,000를 연 5% 이자율로 30년 만기 원리금균등상환으로 대출받았을 때, 월 상환금은 다음과 같이 계산될 수 있습니다.

(책의 내용과 조금 다를 수 있습니다. 읽으면서 제가 다시 정리한 것으로, 오류 발견 시 댓글로 알려주시면 감사하겠습니다!)


   P : 원금, r : 이자율(%), 기간 : t(월)


- 1,878$를 360개월동안 납입하면 총 납입원금은 $676,080가 됩니다. 즉, $350,000를 빌렸지만 실질적으로 빌린 돈이 2배가 되는 셈이지요.

- 이는 원금에 이자가 복리로 붙기 때문인데, 월 상환금을 높이면 총 납입원금도 줄고 조기 상환도 가능합니다.

- 문제를 파이썬으로 시각화 해보면, 상환금을 100$ 올릴 때마다 얼마가 절약되며 얼만큼 조기에 상환이 가능한지 쉽게 확인할 수 있습니다.

(예제 코드는 책의 출판사 사이트에서 받으실 수 있습니다.)



- 산점도(Scatter Plot)와 버블차트를 조합, 대출금과 추가상환액에 따라 절약되는 금액을 다음과 같이 파이썬으로 시각화 할 수 있다.




( .. 다음 포스팅에 계속 .. )



※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 04-2. Interactive Plotting (반응형 시각화)


( 이전 포스팅에 이어서 .. )

이 책에서는 주로 matplotlib 이라는 파이썬 패키지로 시각화를 진행하고 있습니다.

워드 클라우드(Word Cloud) 예제

주어진 문장에서 자주 나타나는 단어에 대한 중요성을 보여주는 시각화이다.
다른 말로, 태그 클라우드(Tag Cloud)나 단어 비중의 시각화라 불린다.

워드 클라우드는 소셜 미디어와 마케팅 분야에 유용하게 사용되고 있다.
- 소비자들의 브랜드에 대한 인식 분석
- 어떤 단어들과 테마가 Target 시장에서 경쟁력을 가지는지 확인


#. wordcloud 라이브러리 설치


Mac 기준으로 작성된 책이여서, Windows OS 환경에서의 설치는 다음과 같이 제가 했던 방식으로 진행하시면 될 것 같습니다.


(1). 아나콘다 프롬포트 실행



(2). conda install -c conda-forge wordcloud 입력

중간에 Proceed ([y]/n)? 나오면 y 입력




#. 워드 클라우드 생성


원본 데이터(wordcloudInput_fromFeeds.txt)와 폰트 파일(RemachineScript.ttf) 파일은 책의 예제 다운로드를 통해서 얻으실 수 있습니다.


from wordcloud import WordCloud, STOPWORDS

import matplotlib.pyplot as plt

from os import path


d = path.dirname("__file__")

text = open(path.join(d, 'wordcloudInput_fromFeeds.txt')).read()


wordcloud = WordCloud(

        font_path='remachine_script/RemachineScript.ttf',

        stopwords=STOPWORDS,

        background_color='#222222',

        width=1000,

        height=800).generate(text)


plt.figure(figsize=(13,13))

plt.imshow(wordcloud)

plt.axis('off')

plt.show()



#. 텍스트 감성 분석

고전적인 방법은 아래의 수식을 활용하는 것이다. 또는 TextBlob 패키지를 이용하는 방법이 있다. 추후에 더 자세하게 다루도록 한다.




주식 차트 시각화 예제

blockspring 라이브러리를 통해 반응형 시각화가 가능하다.

자동으로 생성된 결과물은 HTML 코드와 자바스크립트(D3.js) 포맷으로 되어 있다.


* 위에서 실행한 아나콘다 프롬포트에서 " pip install blockspring " 명령을 입력하시면 블락스프링 패키지를 이용할 수 있습니다.


import blockspring 

import json  


print blockspring.runParsed("stock-price-comparison", 

   { "tickers": "FB, LNKD, TWTR", 

   "start_date": "2014-01-01", "end_date": "2015-01-01" }).params




스포츠 데이터 시각화 예제

이 외에도 다음과 같은 결과물을 파이썬을 통해 얻을 수 있다.





이번 장에서는 간단한 동작 예제만 살폈으며, 추후 좀 더 많은 예제들을 살펴보게 될 것이다.



※ 이 포스팅은 저자 'Kirthi Raman'의 도서 <Mastering Python Data Visualization> 을 공부하며 정리한 글입니다. 



Chapter 04-1. Numerical Computing (수치 연산)


NumPy, SciPy 라이브러리

이들은 파이썬 계산 모듈로서, 함수들이 미리 컴파일 되어있어 빠르게 수행된다.

NumPy는 많은 양의 수치 데이터를 다루기 위한 것이며, SciPy는 응용 수학 부문의 유용한 알고리즘을 제공하는 NumPy의 확장판이다.


#. Interpolation (보간)

- NumPy의 interp() 함수


#. Vectorization 기능

- NumPy와 SciPy에서 vectorize()는 함수를 벡터화하여 효율적인 작업을 가능하게 한다.

- 즉, vectorize()는 스칼라 값을 인수로 받는 함수를 배열도 인수로 사용되도록 하는 기능이다.


#. NumPy 선형 대수

- dot(a,b) : 두 배열의 내적

- linspace(a,b,n) : a부터 b의 범위에서 n개의 수를 생성

- diff(x) : 미분


- linalg.norm(x) : 행렬이나 벡터의 노말

- linalg.cond(x) : 조건 수

- linalg.solve(A, b) : Ax=b의 솔루션

- linalg.inv(A) : 역행렬

- linalg.pinv(A) : 역행렬

- linalg.eig(A) : 제곱의 고유/벡터값

- linalg.eigval(A) : 고유값

- linalg.svd(A) : 단수 값 분해


#. SciPy의 함수

- spio.loadmat : 행렬 로드

- spio.savemat : 행렬 저장

- scio.imread : 이미지 로드

- scipy.polyid : 다항식을 정의하여 여러 편리한 계산이 가능하도록 도움.

* 등 많은 것이 이미 SciPy에 구현되어 있으므로, 파이썬으로 계산 프로그램을 개발할 때 의도한 기능 관련하여 먼저 개발 문서를 확인해보자.


- scipy.cluster : 벡터 양자화, K-means 등 클러스터링

- scipy.fftpack : 고속 푸리에 변환

- scipy.integrate : trapezoidal(사다리꼴), Simpson's, Romberg 등의 방법으로 적분 수행.

quad, dblquad, tplquad 함수로 객체에 싱글, 더블, 트리플 통합 수행 가능.

- scipy.interpolate : 분리된 수치 데이터와 선형 또는 spline interpolation(보간) 개체 함수와 클래스

- scipy.linalg : NumPy의 linalg 패키지를 위한 wrapper. 모든 NumPy 함수들은 scipy.linalg의 일부임.

- scipy.optimize : Neider-Mead Simplex, Powell, 결합 그라데이션 BFGS, 최소 제곱, 제약 최적화, 시뮬레이션 annealing, Newton's Method, 이분법, Broyden Anderson, Linear Search 등을 통한 최대값과 최소값 등을 찾는 기능.

- scipy.sparse : Sparse matrices 관련

- scipy.special : 타원, 베셀, 감마, 베타 초기화, 포물선, 실린더, Mathieu 및 회전 타원체 파도와 같은 계산 물리 위한 특수 함수



MKL 함수

인텔이 개발한 벡터와 행렬에 대한 고성능 처리를 지원하는 함수이다.

FFT 함수와 벡터 통계 함수도 포함되어있다.

인텔 프로세서(CPU)에서 효율적으로 동작하도록 최적화 되어있다.

아나콘다 사용자들은 아나콘다 가속 패키지 일부의 애드온으로 이용 가능하다.




파이썬의 성능 이슈

종종 파이썬의 성능 이슈로 인해 C 언어로 프로그램을 다시 작성하거나, 컴파일된 C 함수들을 호출하는 일이 있다.

Cython처럼 더 쉽게 최적화를 하기 위한 목적의 많은 프로젝트들이 있으나, 기존 파이썬 코드를 효율적으로(빠르게) 만드는 것이 좋다.


#. Numbapro

- 파이썬 코드를 CUDA 가능 CPUs나 멀티코어 CPU, 또는 GPU에서 수행하도록 만드는 Continuum Analytics의 파이썬 컴파일러이다.

- 런타임에 just-in-time(JIT) 컴파일 된다.

- 상업 라이센스임에 유의


#. Scipy.weave

- C 코드를 중간에 삽입, NumPy 배열을 C 층으로 자연스럽게 이동시켜주는 모듈. 효율적인 메크로를 가지고 있다.


#. 멀티코어 활용


#. 프로세스 Pool 활용

- Pool은 멀티프로세싱의 패키지에 또 다른 클래스이다.

- Pool 안에 생성도니 프로세스 수를 정의할 수 있으며 각 프로세스에게 전달될 파라미터를 가지고 있는 반복 객체를 보낼 수 있다.


#. Disco 등 분산 계산 패키지 활용

- MapReduce 패러다임 기반 가벼운 오픈소스 프레임워크

- 이 외 Hadoop Streaming, mrjob, dumbo, hadoopy, pydoop 등이 있다.



분할(Slicing)

배열은 리스트나 튜플로 분할될 수 있다.

[ : , : , ... : ] 와 같은 문법을 사용한다.


#. :and:    ==    0:n:0

#. m: , m:n:    ==    m:n:1

#. :n:    ==    0:n:1

#. ::d:    ==    0:n:d


#. 예시


>>> x = numpy.array([5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])


>>> x[:2] # 0 ~ 2 까지

array([5, 6])


>>> x[::2] # 0 ~ n 까지 2 스탭

array([5, 7, 9, 11, 13, 15, 17, 19])



#. 행렬의 분할


>>> a = numpy.array([ [4,5,6], [7,8,9], [1,2,3] ])

>>> a

array([[4, 5, 6],

       [7, 8, 9],

       [1, 2, 3]])


>>> a.flat[:]

array([4, 5, 6, 7, 8, 9, 1, 2, 3])



색인(Indexing)

#. 수치 색인
- 정수 데이터를 가진 리스트로 Indexing 할 수 있다.

>>> a = 10 * numpy.arange(5.0)

>>> a

array([0., 10., 20., 30., 40.])


>>> a[1] # 값을 반환

10.0

>>> a[[1]] # 배열을 반환

array([10.])


>>> sel = numpy.array([3,1,4,2,3,3])

>>> a[sel] # 위치를 반복하여 배열로 반환할 수 있다

array([ 30.,  10.,  40.,  20.,  30.,  30.])


>>> sel = numpy.array([[4,1], [3,2]])

>>> sel # 2차원에서도 가능

array([[4, 1],

       [3, 2]])

>>> a[sel]

array([[ 40.,  10.],

       [ 30.,  20.]])



#. 논리 색인

- 전등 스위치와 같이, 참이거나 거짓인 조건을 활용할 수 있다.


>>> x = numpy.arange(-4, 5)

>>> x

array([-4, -3, -2, -1,  0,  1,  2,  3,  4])


>>> x < 0

array([ True,  True,  True,  True, False, False, False, False, False], dtype=bool)


>>> x[x>0]

array([1, 2, 3, 4])


>>> x[abs(x) >= 2]

array([-4, -3, -2,  2,  3,  4])


>>> x = numpy.reshape(numpy.arange(-8, 8), (4, 4)) # 2차원 예시

>>> x

array([[-8, -7, -6, -5],

       [-4, -3, -2, -1],

       [ 0,  1,  2,  3],

       [ 4,  5,  6,  7]])


>>> x[x<0]

array([-8, -7, -6, -5, -4, -3, -2, -1])


>>> from math import isnan # 여러 방식의 논리 색인


>>> list1 = [[3, 4, float('NaN')],

                 [5, 9, 8],

           [3, 3, 2],

                 [9, -1, float('NaN')]]


>>> [elem for elem in list1

if not any([isnan(element) for element in elem])]

[[5, 9, 8], [3, 3, 2]]


>>> list2 = [3,4,5,6]


>>> [list2[index] for index, elem in enumerate(list1)

     if not any([isnan(element) for element in elem])]

[4, 5]



파이썬의 다양한 데이터 구조

파이썬은 스택, 리스트, 집합(Set), 시퀀스, 튜플, 리스트, 힙, 배열, 사전(Dictionary), 디큐(Dequeue) 등의 데이터 구조를 가진다.

참고로, 튜플은 불변성으로 리스트보다 더 효율적인 메모리 효율을 갖는다.



#. 스택(Stack)

- list() 함수는 스택으로서 사용되기 유용하다.

- 새로 들오온 요소는 뒤로 추가되고, 가장 먼저 들어온 것이 먼저 나가는(선입선출) 실행 원칙을 가진 추상 데이터 유형이다.


- append()로 아이템을 추가한다.

- pop()으로 아이템을 추출한다.

- remove(item-value)로 아이템을 제거한다.



#. 튜플(Tuple)

- 불변하는 오브젝트의 시퀀스이다. 따라서 오브젝트를 바꿀 수도, 제거할 수도 없다. 하지만 튜플 자체(오브젝트 전체)를 지우는 것은 가능하다.

- 다원화된 데이터 구조를 갖는다.

- 리스트가 같은 요소들의 시퀀스인 반면, 튜플은 요소들이 서로 다른 의미를 가진다.

- 리스트는 시퀀스를 갖고, 튜플은 구조를 갖는다.

- 튜플의 예는 한 주의 요일, 강의의 과목 이름, 등급 척도 등이다.


- del tuble-name 으로 튜플을 지운다.

- cmp(tup1, tup2) : 튜플의 요소를 비교

- len(tuple) : 전체 길이

- max(tuple) : 요소들의 최댓값

- min(tuple) : 요소들의 최솟값

- tuple(lista) : 리스트를 튜플로 변환


- 참고로, 파이썬은 max() 함수에 문자열의 리스트를 입력하면 가장 긴 문자열을 return한다. (min() 함수도 마찬가지)


#. 셋(Set)

- 리스트와 유사하나, 순서가 없는 집합이다. 리스트는 위치나 색인에 의해 순서화된다.

- 중복을 허용하지 않는다. (중복이 없다)


#. 사전(Dictionary)

- 키와 값의 쌍으로 구성된, 순서가 없는 데이터 값들의 집합

- 색인으로서 키를 기반으로 값에 접근할 수 있는 장점이 있다.

- 디폴트 사전(Default Dictionary)은 키와 값의 쌍이 삽입된 순서를 유지하지 않는다. OrderedDict() 함수로 생성된 빈 사전은 순서를 유지한다.


>>> # 예제 1 : 아프리카의 10대 GDP

>>> gdp_dict = {

        'South Africa': 285.4,

        'Egypt': 188.4,

        'Nigeria': 173,

        'Algeria': 140.6,

        'Morocco': 91.4,

        'Angola': 75.5,

        'Libya': 62.3,

        'Tunisia': 39.6,

        'Kenya': 29.4,

        'Ethiopia': 28.5,

        'Ghana': 26.2,

        'cameron': 22.2

        }


>>> gdp_dict['Angola']

75.5



( .. 다음 포스팅에 계속 .. )



+ Recent posts