[Python] 층화 무작위 추출을 통한 train set, test set 분할 (Train, Test set Split by Stratified Random Sampling in Python)
Python 분석과 프로그래밍/Python 데이터 전처리 2020. 2. 15. 23:29지난번 포스팅에서는 무작위로 데이터셋을 추출하여 train set, test set을 분할(Train set, Test set split by Random Sampling)하는 방법을 소개하였습니다.
이번 포스팅에서는 데이터셋 내 층(stratum) 의 비율을 고려하여 층별로 구분하여 무작위로 train set, test set을 분할하는 방법(Train, Test set Split by Stratified Random Sampling)을 소개하겠습니다.
(1) sklearn.model_selection.train_test_split 함수를 이용한 Train, Test set 분할
(층을 고려한 X_train, X_test, y_train, y_test 반환)
(2)sklearn.model_selection.StratifiedShuffleSplit 함수를 이용한 Train, Test set 분할
(층을 고려한 train/test indices 반환 --> Train, Test set indexing)
참고로 단순 임의 추출(Simple Random Sampling), 체계적 추출(Systematic Sampling), 층화 임의 추출(Stratified Random Sampling), 군집 추출(Cluster Sampling), 다단계 추출(Multi-stage Sampling) 방법에 대한 소개는 https://rfriend.tistory.com/58 를 참고하세요.
(1) sklearn.model_selection.train_test_split 함수를 이용한 Train, Test set 분할 (층을 고려한 X_train, X_test, y_train, y_test 반환) |
먼저 간단한 예제로 사용하기 위해 15행 2열의 X 배열, 15개 원소를 가진 y 배열 데이터셋을 numpy array 를 이용해서 만들어보겠습니다. 그리고 앞에서 부터 5개의 관측치는 '0' 그룹(층), 6번째부터 15번째 관측치는 '1' 그룹(층)에 속한다고 보고, 이 정보를 가지고 있는 'grp' 리스트도 만들겠습니다.
import numpy as np X = np.arange(30).reshape(15, 2) X
y = np.arange(15) y
# stratum (group) grp = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] grp [Out]: [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] |
이제 scikit-learn model_selection 클래스에서 train_test_split 함수를 가져와서 X_train, X_test, y_train_y_test 데이터셋을 분할해 보겠습니다.
- X와 y 데이터셋이 따로 분리되어 있는 상태에서 처음과 두번째 위치에 X, y를 각각 입력해줍니다.
- test_size에는 test set의 비율을 입력하고 stratify에는 층 구분 변수이름을 입력해주는데요, 이때 각 층(stratum, group) 별로 나누어서 test_size 비율을 적용해서 추출을 해줍니다.
- shuffle=True 를 지정해주면 무작위 추출(random sampling)을 해줍니다. 만약 체계적 추출(systematic sampling)을 하고 싶다면 shuffle=False를 지정해주면 됩니다.
- random_state 는 재현가능성을 위해서 난수 초기값으로 아무 숫자나 지정해주면 됩니다.
# returns X_train, X_test, y_train, y_test dataset from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=True, stratify=grp, random_state=1004) 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) [Out]: X_train shape: (12, 2)
X_test shape: (3, 2)
y_train shape: (12,)
y_test shape: (3,) |
아래는 X_train, y_train, X_test, y_test 로 각각 분할된 결과입니다.
X_train [Out]: array([[12, 13],
[ 8, 9],
[28, 29],
[ 0, 1],
[10, 11],
[ 6, 7],
[ 2, 3],
[18, 19],
[20, 21],
[22, 23],
[26, 27],
[14, 15]]) y_train [Out]: array([ 6, 4, 14, 0, 5, 3, 1, 9, 10, 11, 13, 7]) X_test [Out]: array([[16, 17],
[ 4, 5],
[24, 25]]) y_test [Out]: array([ 8, 2, 12]) |
(2) sklearn.model_selection.StratifiedShuffleSplit 함수를 이용한 Train, Test set 분할 (층을 고려한 train/test indices 반환 --> Train, Test set indexing) |
(2-1) numpy array 예제
위의 train_test_split() 함수가 X, y를 input으로 받아서 각 층의 비율을 고려해 무작위로 X_train, X_test, y_train, y_test 로 분할된 데이터셋을 반환했다고 하며, 이번에 소개할 StratfiedShuffleSplit() 함수는 각 층의 비율을 고려해 무작위로 train/test set을 분할할 수 있는 indices 를 반환하며, 이 indices를 이용해서 train set, test set을 indexing 하는 작업을 추가로 해줘야 합니다. 위의 (1)번 대비 좀 불편하지요? (대신 이게 k-folds cross-validation 할 때n_splits 를 가지고 층화 무작위 추출할 때는 위의 (1)번 보다 편리합니다)
1개의 train/ test set 만을 분할하므로 n_splits=1 로 지정해주며, test_size에 test set의 비율을 지정해주고, random_state에는 재현가능성을 위해 난수 초기값으로 아무값이 지정해줍니다.
train_idx, test_idx 를 반환하므로 for loop문을 사용해서 X_train, X_test, y_train, y_test를 X와 y로 부터 indexing해서 만들었습니다.
i# Stratified ShuffleSplit cross-validator # provides train/test indices to split data in train/test sets. from sklearn.model_selection import StratifiedShuffleSplit split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=1004) for train_idx, test_idx in split.split(X, grp): X_train = X[train_idx] X_test = X[test_idx] y_train = y[train_idx] y_test = y[test_idx]
|
X_train, y_train, X_test, y_test 값을 확인해보면 아래와 같은데요, 이는 random_state=1004로 (1)번과 같게 설정해주었기때문에 (1)번의 train_test_split() 함수를 사용한 결과와 동일한 train, test set 데이터셋이 층화 무작위 추출법으로 추출되었습니다.
X_train
y_train
X_test [Out]: array([[16, 17],
[ 4, 5],
[24, 25]]) y_test [Out]: array([ 8, 2, 12]) |
(2-2) pandas DataFrame 예제
위의 (2-1)에서는 numpy array를 사용해서 해보았는데요, 이번에는 pandas DataFrame에 대해서 StratifiedShuffleSplit() 함수를 사용해서 층화 무작위 추출법을 이용한 Train, Test set 분할을 해보겠습니다.
먼저, 위에서 사용한 데이터셋과 똑같이 값으로 구성된, x1, x2, y, grp 칼럼을 가진 DataFrame을 만들어보겠습니다.
import pandas as pd import numpy as np X = np.arange(30).reshape(15, 2) y = np.arange(15) df = pd.DataFrame(np.column_stack((X, y)), columns=['X1','X2', 'y']) df
df['grp'] = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] df
|
이제 StratifiedShuffleSplit() 함수를 사용해서 층의 비율을 고려해서(유지한채) 무작위로 train set, test set DataFrame을 만들어보겠습니다.
from sklearn.model_selection import StratifiedShuffleSplit split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=1004) for train_idx, test_idx in split.split(df, df["grp"]): df_strat_train = df.loc[train_idx] df_strat_test = df.loc[test_idx]
|
층 내 class의 비율을 고려해서 층화 무작위 추출된 DataFrame 결과는 아래와 같습니다.
df_strat_train
df_strat_test
|
정말로 각 층 내 계급의 비율(percentage of samples for each class)이 train set, test set에서도 유지가 되고 있는지 확인을 해보겠습니다.
df["grp"].value_counts() / len(df)
df_strat_train["grp"].value_counts() / len(df_strat_train) [Out]: 1 0.666667
0 0.333333
Name: grp, dtype: float64 df_strat_test["grp"].value_counts() / len(df_strat_test) [Out]:
|
pandas DataFrame에 대한 무작위 표본 추출 방법은 https://rfriend.tistory.com/602 를 참고하세요.
많은 도움이 되었기를 바랍니다.
이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)