1. 전수조사와 표본조사

- 전수조사

  • 모집단 내에 있는 모든 대상을 조사하는 방법
  • 모집단의 특성을 정확히 반영하지만 시간과 비용이 많이 소모됨

- 표본조사

  • 모집단으로부터 추출된 표본을 대상으로 분석 실시
  • 전수조사의 단점을 보완하지만 모집단의 특성을 반영하는 표본이 제대로 추출되지 못하면 수집된 자료가 무용지물

 

2. 용어

- Sample: 큰 데이터 집합으로부터 얻은 부분집합

- Population: 데이터 집합을 구성하는 전체

- N(n): 모집단의 크기

 

- 랜덤 표본추출: 무작위로 표본을 추출하는 것

  • 층화 랜덤 표본추출: 모집단을 여러개의 층으로 나누고 각 층에서 무작위로 표본 추출
  • 단순 랜덤 표본추출: 층화 없이 단순하게 무작위로 표본 추출

- 표본 편항 (Sample Bias): 모집단을 잘못 대표하는 표본 추출

 

- 표준 오차

  • 통계에 대한 표본 분포의 변동성
  • 표본 값들의 표준편차(s)와 표본 크기(n)를 기반으로 추정
  • 표준오차와 표본 크기 사이의 관계는 n제곱근의 법칙이라고 하는데 표준오차를 2배 줄이려면 표본 크기를 4배로 증가시켜야 함

 

3. 표본 추출

- 전체 데이터(모집단) 중 일부를 표본(샘플)로 추출하는 작업이 데이터 분석에서 필수

- 훈련 데이터(80%), 테스트 데이터(20%)로 분리하여 데이터에 대한 모델링은 훈련 데이터로만 수행하고 모델의 성능은 테스트 데이터로 평가하면 모델의 성능을 가장 적절히 평가

- 데이터의 분포가 일정하지 않다면 가중치를 이용해서 데이터 추출하는 부분도 고려

- random.sample

 

3-1) 머신러닝에서 표본 추출

- train data(모델 생성) 와 test data(모델 테스트)로 나누는 방법

- train data 와 test data 와 validation data(모델 검증)로 나누는 방법

 

3-2) 복원추출과 비복원추출

- 복원 추출: 추출된 데이터를 모집단에 포함시켜서 추출

  • python에서는 random.random, randint, randrange 등의 함수가 있음

- 비복원 추출: 한 번 추출된 데이터는 모집단에 포함시키지 않고 추출

  • python에서는 random.sample 함수가 제공됨
import random

li = ["SES", "소녀시대", "f(x)", "레드벨벳", "에스파"]

#복원 추출
for i in range(5):
    print(li[random.randint(0, len(li)-1)], end='\t')

print()
#비복원 추출
print(random.sample(li, k=5))
f(x) f(x) 레드벨벳 SES f(x)
['f(x)', '레드벨벳', '소녀시대', 'SES', '에스파']

 

 

3-3) 가중치를 적용한 표본추출

- 표본의 비율이 달라져야 하는 경우 표본을 추출할 때 비율을 설정해서 추출하는 것

- numpy.random.choice(a, size=None, replace=True, p=None)

  • a: 배열,   size: 개수,   replace: 복원여부,   p: 확률
  • 추출하고자 하는 배열, 개수, 확률을 제시하면 가중치를 적용해서 표본 추출
  • 현실 세계의 문제에서는 간혹 가중치를 적용한 표본 추출이 어려우면 표본 추출을 해서 분석을 한 후 가중치 적용
ar = ["라투", "오미크론", "다크스펙터", "나이즈", "슬리피"]

print(np.random.choice(ar, 10, [0.05, 0.15, 0.2, 0.3, 0.3]))
['다크스펙터' '라투' '나이즈' '슬리피' '다크스펙터' '라투' '라투' '슬리피' '오미크론' '슬리피']

 

 

3-4) pandas의 표본추출

- Series나 DataFrame에서 sample 함수 제공

Series.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)

  • n: 추출한 샘플의 수 (frac과 중복 사용 불가)
  • frac: 전체 개수의 비율만큼 샘플을 반환하려 할 경우 사용
  • replace: 복원추출 여부, 기본값음 False(중복불가)
  • weights: 샘플 추출 시 샘플마다 뽑힐 확률 조정
  • random_state: 랜덤 샘플 추출 시 시드를 입력받음
  • axis: 샘플을 추출할 방향. 기본=0(행)
ex_df = pd.DataFrame(np.arange(0,12).reshape(4, 3))
print(ex_df)
print()

#2개의 행을 추출
print(ex_df.sample(n=2))
print()

#1개의 열을 랜덤하게 추출
print(ex_df.sample(n=1, axis=1))
print()
   0   1   2
0  0   1   2
1  3   4   5
2  6   7   8
3  9  10  11

   0  1  2
2  6  7  8
0  0  1  2

   0
0  0
1  3
2  6
3  9

 

 

3-5) sklearn을 이용한 샘플 추출

- scikit-learn.model_selection 의 train_test_split()

  • 훈련 데이터 와 테스트 데이터를 분할하기 위한 API
  • shuffle 옵션: 기본값은 True 로 되어 있는데 시계열 데이터와 같은 데이터를 추출할 때처럼 랜덤한 추출이 아닌 순차적 분할을 해야 하는 경우, False 를 설정해서 추출
  • test_size: 테스트 데이터의 비율을 설정
    • 일반적으로 8:2 또는 7:3을 선호하지만 데이터가 아주 많은 경우에는 5:5 설정 가능
    • 이렇게 데이터가 많으면 테스트 데이터를 다시 테스트 데이터 와 검증을 위한 데이터로 분할하기도 함
  • 리턴되는 데이터는 4개의 데이터 그룹에 대한 tuple
    • 피처의 훈련데이터, 피처의 테스트 데이터, 타겟의 훈련 데이터, 타겟의 테스트 데이터
#데이터 생성
#특별한 경우가 아니면 피처는 대문자 X로 나타냅니다.
#타겟은 소문자 y로 나타냅니다.
#numpy 의 1차원 ndarray는 출력을 하면 옆으로 펼쳐지지만 하나의 열로 간주합니다.
X = np.arange(20).reshape(10, 2)
print(X)  
y = np.arange(10)
print(y)
[[ 0  1]
 [ 2  3]
 [ 4  5]
 [ 6  7]
 [ 8  9]
 [10 11]
 [12 13]
 [14 15]
 [16 17]
 [18 19]]
[0 1 2 3 4 5 6 7 8 9]

 

#순차적 분할 7:3

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=False)
print('X_train shape:', X_train.shape)
print('X_test shape:', X_test.shape)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)
print()
print(X_train)
X_train shape: (7, 2)
X_test shape: (3, 2)
y_train shape: (7,)
y_test shape: (3,)

[[ 0  1]
 [ 2  3]
 [ 4  5]
 [ 6  7]
 [ 8  9]
 [10 11]
 [12 13]]

 

- 무작위 추출

  • shuffle 옵션 제거하고 동일한 데이터 추출을 위해 random_state에 seed값 고정
#순차적 분할 - 7:3
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(X_train)
print()
print(y_train)
[[ 0  1]
 [14 15]
 [ 4  5]
 [18 19]
 [ 8  9]
 [ 6  7]
 [12 13]]

[0 7 2 9 4 3 6]
l1 = np.array(range(0, 10))
l2 = np.array(range(10, 20))

print(np.vstack((l1, l2)).T)
[[ 0 10]
 [ 1 11]
 [ 2 12]
 [ 3 13]
 [ 4 14]
 [ 5 15]
 [ 6 16]
 [ 7 17]
 [ 8 18]
 [ 9 19]]

 

- 층화 추출

  • 데이터를 일정한 비율로 무작위 추출
  • 머신러닝에서 타겟의 비율의 일정하지 않은 경우 사용
    • 타켓의 비율이 편차가 심한 경우 랜덤하게 추출했을 때 비율이 낮은 데이터가 추출이 안될 수 도 있고 너무 많이 추출될 수 도 있다.
    • 이렇게 머신러닝의 결과가 엉뚱한 결과가 추출될 수 있습니다.
  • 층화 추출을 할 때는 stratify 옵션에 리스트를 대입하면 비율 계산을 해서 추출
X = np.arange(30).reshape(15, 2)
y = np.arange(15)
grep = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

#단순한 방식으로 추출을 하게 되면 한쪽으로 샘플이 쏠리는 현상이 발생하고
#이렇게 되면 샘플 데이터에서는 잘 맞지만 실제 서비스 환경에서는 제대로 맞추지 못하는
#현상이 발생할 수 있습니다.
'''
X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                   test_size=0.2,
                                                   shuffle=True,
                                                   random_state = 1004)
print(y_train)
print(y_test)
'''

#stratify 에 대입한 리스트의 비율을 확인해서 샘플링
X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                   test_size=0.2,
                                                   shuffle=True,
                                                   stratify=grep,
                                                   random_state = 32)
print(y_train)
print(y_test)
[14 12  7  9 10  4  6  2 11  1  0  5]
[ 8  3 13]

 

 

3-6) 층화 추출 API

- scikit-learn.model_selection 의 StratifiedShuffleSplit()

- n_splits: 데이터를 몇 개의 그룹으로 분할할지를 설정

- 데이터를 리턴하지 않고 데이터의 인덱스를 리턴

- k-folds cross-validation을 수행하기 위해서 만든 API

  • 모델을 만들고 이를 검증을 할 때 전체 데이터를 N 등분해서 N-1 개의 그룹으로 모델을 만들고 1개의 그룹으로 테스트를 수행하는데 N 번 수행
#교차 검증을 위해서 데이터를 n 등분 층화 추출을 해주는 API
#직접 사용하지 않고 머신러닝 모델이 내부적으로 사용
from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=1002)

#인덱스를 리턴하므로 인덱스를 이용해서 데이터를 추출해서 확인
for train_idx, test_idx in split.split(X, grep):
    X_train = X[train_idx]
    X_test = X[test_idx]
    y_train = y[train_idx]
    y_test = y[test_idx]    

print(y_train)
print(y_test)
[ 8  4  7  0 10 11 12  2 14  3  5  6]
[13  9  1]

 

 

3-7) 재표본 추출

- 랜덤한 변동성을 알아보자는 일반적인 목표를 가지고 관찰된 데이터의 값에서 표본을 반복적으로 추출하는 것

- 종류

순열 검정

- 두 개 이상의 표본을 함께 결합해서 관측값을 무작위로 재표본으로 추출하는 과정

- 두 개 이상의 표본을 사용하여 A/B 검정(하나의 UI 와 새로운 UI를 도입했을 때 결과 비교 등에 사용하는 방식)  등에 이용

  1. 여러 그룹의 결과를 하나의 데이터 집합으로 합침
  2. 결합된 데이터를 잘 섞은 다음 A 그룹과 동일한 크기의 표본을 비복원 무작위로 추출
  3. 나머지 데이터에서 B 그룹과 동일한 크기의 샘플을 비복원 무작위로 추출
  4. 그룹이 더 존재하면 동일한 방식으로 추출
  5. 원래의 표본에 대해 구한 통계량 또는 추정값이 무엇이었든지 간에 추출한 재표본에 대해서 다시 계산하고 기록을 한 다음 원래의 표본에 대해서 구한 값과 비교
  6. 이 작업을 여러 번 반복

부트스트래핑

- 모수의 분포를 추정하는 방법으로 현재있는 표본에서 추가적으로 표본을 복원 추출하고 각 표본에 대한 통계량을 다시 계산하는 것

- 개념적으로만 보면 원래 표본을 수천 수백만 번 복제하는 것

  1. 1억개의 모집단에서 200개의 표본을 추출
  2. 200개의 표본 중에서 하나를 추출해서 기록하고 복원
  3. 이 작업을 n번 반복
  4. n번 반복한 데이터를 가지고 통계량을 계산
  5. 이전 과정을 여러 번 반복해서 통계량을 구하고 이 통계량을 이용해서 신뢰구간을 구하는 방식

- 시간이 오래 걸린다.

  • coffee_dataset.csv 파일의 데이터
    21살 미만 여부
    커피를 마시는지 여부
    키에 대한 데이터
diffHeightListOver21 = []
for _ in range(iterationNum):
    bootSample = df_sample.sample(200, replace=True) # 복원 추출
    nonCoffeeHeightMeanOver21 = bootSample.query("age != '<21' and drinks_coffee == False").height.mean() # 21살 이상이며 커피를 마시지 않는 사람 평균 키
    coffeeHeightMeanOver21 = bootSample.query("age != '<21' and drinks_coffee == True").height.mean() # 21살 이상이며 커피를 마시는 사람 평균 키

    diff = nonCoffeeHeightMeanOver21 - coffeeHeightMeanOver21
    diffHeightListOver21.append(diff)

np.percentile(diffHeightListOver21, 0.5), np.percentile(diffHeightListOver21, 99.5)
0.37151963778610253 3.266041922112605
# 1. 커피를 마시지 않는 사람과 커피를 마시는 사람의 평균 키 차이
print(df[df['drinks_coffee'] == False].height.mean() - df[df['drinks_coffee'] == True].height.mean())
# 2. 21살 이상과 21살 미만인 사람들의 평균 키 차이
print(df[df['age'] == '>=21'].height.mean() - df[df['age'] == '<21'].height.mean())
# 3. 21살 미만인 사람들 중 커피를 마시지 않는 사람과 커피를 마시는 사람의 평균 키 차이
print(df.query("age == '<21' and drinks_coffee == False").height.mean() - df.query("age == '<21' and drinks_coffee == True").height.mean())
# 4. 21살 이상인 사람들 중 커피를 마시지 않는 사람과 커피를 마시는 사람의 평균 키 차이
print(df.query("age != '<21' and drinks_coffee == False").height.mean() - df.query("age != '<21' and drinks_coffee == True").height.mean())
-1.9568024933369799
3.88229124992111
1.6993900935511732
1.9509354889786579