부트스트래핑(Bootstrapping)으로 의사결정 분석하기

주어진 상황

  • 한 기업이 미국 주요 주의 도시별로,  약 한달간 서비스를 시범운영
  • 각 주 별로 도시의 수가 다른 상황
  • 각 주 별 수익의 분포가 정규분포와 같은 기존 분포를 따르는지 확신할 수 없는 상황에서 비모수 데이터로 기간도 짧고, 데이터 양도 많지 않은 상황이다.
  • 초기 데이터는 다음과 같다.
state_idcity_idsales
0KK0237549889.39
1KK0249276755.93
2KK02110267332.39
3KK01612201990.58
4KK00616268325.38
............
247NN022382.61
248SS00225041331.94
249SS0017411905.33
250MM00111.21
251TT00112474.74

각 주별 평균 수익률과 95% 신뢰구간 계산하고자 할 때

  • 이 때 Bootstrapping을 사용해서 평균 수익률 외에, 95% 신뢰구간을 빈도주의 컨셉으로 계산해볼 수 있다.
  • Bootstrapping은 모집단에서 추출한 Sample이 있을 때, 해당 Sample에서 복원추출을 여러번 해서 각가가 통계량을 계산한 이후, 해당 통계량의 분포를 이용하는 방식이다. Sample도 어떻게 보면 모집단으로 가정하고, 여기서 재추출함으로써 표본 추출에서 발생했던 불확실성을 감소시킬 수 있기 때문에 Bootstrapping을 사용하는 것은 의미가 있다고 볼 수 있다.
  • 물론 원래의 모집단이 iid가정을 충족해야 한다는 제약이 있고 분석을 할 때마다 수치가 다르게 나올 가능성이 충분히 있으나, Resamplng 횟수를 많이 하면 대략 무시할 정도로 그 차이가 줄일 수있다.
  • 이 때 95%신뢰구간은 표본추출을 100번 해보았을 때, 그 안에 모평균이 95%는 포함되고 있음을 이야기 하는데, 이렇게 이야기 하면 비전공자 분들은 쉽게 이해하기가 어려울 수 있으니 적절히 95% 확신할 수 있다로 이야기 하면 정리가 가능하다. 신뢰구간에 대한 내용은  이 분 블로그를 참조하는게 좋다.
import numpy as np
import pandas as pd
import pymc3 as pm
from scipy.stats import norm

def bootstrap(df, column='sales', n_sample=10000):   
    data = df[column]
    samples = np.random.choice(data, size=(int(n_sample), len(data))).mean(axis=1)
    
    sample_mean = data.mean()
    std_err = samples.std()
    z = norm.ppf( [0.025,1-0.025] ) #95% Confidence Interval
    
    lower_bound, upper_bound = sample_mean + z*std_err
    
    return pd.DataFrame([{
        'sample_mean': sample_mean,
        'std_err': std_err,
        'lower_bound': lower_bound,
        'upper_bound': upper_bound,
    }])
    
    
threshold = 2e+06
raw_df.groupby(['state_id']).apply(bootstrap).assign(significant=lambda d: d.lower_bound >= threshold).sort_values('sample_mean', ascending=False)
(Source: The RED : 우버처럼 하는 머신러닝 의사결정)

그러면 다음과 같이 결과를 얻을 수 있다. 각 주별로 도시의 수익을 95% 상한/하한구간을 구할 수 있다. Significant는 지정한 수익의 Threshold를 넘었는지 여부 정도의 수치이다.  이러한 식으로 Bootstartpping을 이용해서 의사결정을 지원할 수도 있다.

sample_meanstd_errlower_boundupper_boundsignificant
state_id
S01.622662e+076.188215e+064.097940e+062.835530e+07True
K01.574882e+071.229197e+061.333964e+071.815801e+07True
P01.436418e+073.725290e-091.436418e+071.436418e+07True
G01.381692e+073.098904e+067.743177e+061.989066e+07True
L01.376060e+072.381484e+069.092979e+061.842822e+07True
R01.330862e+072.452539e+068.501727e+061.811550e+07True
C01.164475e+071.250479e+069.193860e+061.409565e+07True
J01.163940e+072.620053e+066.504188e+061.677461e+07True
O09.655540e+061.243954e+067.217436e+061.209364e+07True
H09.645671e+061.683808e+066.345468e+061.294587e+07True
N05.716266e+061.447926e+062.878383e+068.554149e+06True
F04.998980e+061.638056e+061.788449e+068.209512e+06False
A04.784722e+061.315375e+062.206635e+067.362808e+06True
B03.702787e+061.152492e+061.443943e+065.961631e+06False
I03.471974e+061.297475e+069.289702e+056.014978e+06False
D03.022050e+061.129446e+068.083771e+055.235724e+06False
E02.171747e+067.592370e+056.836702e+053.659824e+06False
T01.247474e+041.818989e-121.247474e+041.247474e+04False
M01.121000e+013.552714e-151.121000e+011.121000e+01False