PyTorch 는 이미지, 텍스트, 오디오에 대한 데이터셋과 DataLoader 를 제공합니다. 

 

(1) torch.utils.data.Dataset: train, test 샘플 데이터와 label 을 저장해 놓은 데이터셋

(2) torch.utils.data.DataLoader: 샘플 데이터에 쉽게 접근 가능하도록 해주는 iterable 을 제공. 

 

 

PyTorch에서 Dataset을 로딩할 때 매개변수는 다음과 같습니다. 

 

- root : train/test 데이터가 저장될 경로
- train : train/test 데이터셋 여부 설정. True 이면 training set, False 이면 test set
- download=True : root 에서 데이터가 사용가능하지 않으면 인터넷에서 데이터를 다운로드함
- transform : Feature 를 변환하는 함수 지정
- target_transform : Label 을 변환하는 함수 지정

 

 

이번 포스팅에서는 torchvision.datasets 에서 FashionMNIST 이미지 데이터셋을 로딩하고 변환 (transformation)하는 예를 들어보겠습니다. 

 

 

1. FashionMNIST 데이터셋 가져와서 변환하기

 

torchvision.datasets.FashionMNIST() 메소드를 사용해서 train, test 데이터셋을 다운로드하여 가져옵니다.

 

이때, 모델 훈련에 적합한 형태가 되도록 Feature와 Label 을 변환할 필요가 생길 수 있는데요, PyTorch에서는 Transforms 를 사용하여 변환을 합니다. Dataset 을 가져올 때 사용자 정의 함수를 정의해서 transfrom, target_transform 매개변수에 넣어주여 Feature와 Lable 을 모델 훈련에 맞게 변환해줍니다. 

 

- Feature 변환: 이미지를 (224, 224) 크기로 조정하고, PyTorch tensor로 변환

- Label 변환: integer list 로 되어있는 것을 one-hot encoding 변환

 

 

transform, target_transform 매개변수 자리에는 Lambda 를 사용해서 사용자 정의 함수를 바로 넣어줘도 됩니다. 

 

## Loading and Transforming Image Dataset
import torch
from torchvision import datasets
import torchvision.transforms as transforms

# Define transformations for the image
image_transforms = transforms.Compose([
    transforms.Resize((224, 224)),  # Resize the image to 224x224 pixels
    transforms.ToTensor(),          # Convert the image to a PyTorch tensor
])

# Define transformations for the target (e.g., converting to a one-hot encoded tensor)
def target_transform(target):
    return torch.eye(10)[target]  # there are 10 classes
    
    
# Load the dataset with transformations
training_data = datasets.FashionMNIST(
    root='data',
    train=True, # training set
    download=True, 
    transform=image_transforms,         # (1) specify how the input data should be preprocessed
    target_transform=target_transform # (2) specifies how the labels should be converted
)

trest_data = datasets.FashionMNIST(
    root='data',
    train=False, # test set
    download=True, 
    transform=image_transforms,         # (1) specify how the input data should be preprocessed
    target_transform=target_transform # (2) specifies how the labels should be converted
)


print(f"Image tensor:\n {training_data[0][0]}")
print("-----------" * 5)
print(f"Label: \n {training_data[0][1]}")

# Image tensor:
#  tensor([[[0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          ...,
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.]]])
# -------------------------------------------------------
# Label: 
#  tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.])

 

 

 

FashionMNIST 데이터셋에서 무작위로 이미지를 9개 가져와서 시각화해보면 아래와 같습니다. 

 

## Iterating and Visualizing the Dataset
import matplotlib.pyplot as plt

labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}

figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3

for i in range(1, cols * rows + 1):
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[torch.argmax(label).item()])
    plt.axis("off")
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()

 

FashionMNIST

 

 

 

 

2. DataLoader 를 사용해서 Iterate 하기

 

모델 학습을 할 때 보통 mini-batch 만큼의 데이터셋을 가져와서 iteration을 돌면서 학습을 진행합니다. 이때 batch_size 만큼 mini-batch 만큼 데이터셋 가져오기, shuffle=True 를 설정해서 무작위로 데이터 가져오기 설정을 할 수 있습니다.  

 

## Preparing your data for training with DataLoaders
from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)


# Display image and label.
train_features, train_labels = next(iter(train_dataloader))
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

# Feature batch shape: torch.Size([64, 1, 28, 28])
# Labels batch shape: torch.Size([64])
# Label: 9

 

 

[Reference]

(1) PyTorch Datasets & DataLoaders
: https://pytorch.org/tutorials/beginner/basics/data_tutorial.html

 

Datasets & DataLoaders — PyTorch Tutorials 2.2.0+cu121 documentation

Note Click here to download the full example code Learn the Basics || Quickstart || Tensors || Datasets & DataLoaders || Transforms || Build Model || Autograd || Optimization || Save & Load Model Datasets & DataLoaders Code for processing data samples can

pytorch.org

 

(2) PyTorch Transforms
: https://pytorch.org/tutorials/beginner/basics/transforms_tutorial.html

 

Transforms — PyTorch Tutorials 2.2.0+cu121 documentation

Note Click here to download the full example code Learn the Basics || Quickstart || Tensors || Datasets & DataLoaders || Transforms || Build Model || Autograd || Optimization || Save & Load Model Transforms Data does not always come in its final processed

pytorch.org

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

 

728x90
반응형
Posted by Rfriend
,

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

   - 이산형화

   - 이항변수화

(4) 개수 축소 (Sampling)

(5) 차원 축소

   - 주성분분석

   - 요인분석

(6) 시그널 데이터 압축

의 6개 구분 중에서

 

(4) 개수 축소 (Sampling)에 대해서 알아보겠습니다.

 

통계를 크게 두 축으로 나누자면 모집단으로 부터 표본을 추출해서 표본의 분포와 데이터 특성을 파악하는 기술통계(descriptive statistics)와 표본집단의 통계량을 통해 모집단의 모수(parameter)를 추정하고 가설을 검정(test)하는 추론통계(inferential statistics)로 구분합니다.  기술통계의 시작은 표본 추출 (Sampling)이라고 할 수 있겠습니다.  즉, 표본 추출이 모집단을 대표하지 못하게 되는 경우, 샘플링 이후의 모든 기술통계와 추론통계가 말짱 꽝이 되는 것입니다.  반면, 샘플링이 모집단을 잘 대표할 경우 엄청난 시간과 돈을 절약할 수 있는 통계의 무기를 획득할 수 있게 됩니다.

 

 

 R 데이터 변환 : (4) 개수 축소 (Sampling)

 

 

 

[ 데이터 변환 구조 ]

 

미국 대공황 시절에 루즈벨트 대통령이 뉴딜정책을 펼 때 경제정책의 기조를 의사결정하기 위해 실업율을 조사해야 했다고 합니다.  통계학자들은 잘 설계된 표본 추출기법을 통해 일부만 조사하고도 실업율을 파악할 수 있다고 한 반면에, 경제학자들은 전수조사를 해야한다고 우겼다고 하는군요.  그래서 샘플 조사도 하고 전수조사도 했다고 하는데요, 샘플 조사야 며칠 이면 끝나지만 전수조사는 몇 달이 걸렸다고 합니다.  전수 조사결과와 샘플 조사 결과를 비교해보니 오차가 무시할 수 있을 정도로 작았다고 했답니다. 이처럼 잘 설계된(!) 샘플링은 시간과 돈을 많이 절약해줄 수 있습니다. 

 

제품을 생산하는 제조공장에서도 품질검사를 위해 샘플링을 많이 사용합니다.  품질검사한다고 전수조사 했다가는 시장에 내다 팔 제품이 남아나지 않아서 공장 망하겠지요?  이럴 때는 어쩔 수 없이 샘플링을 해야만 하는데요, 샘플링을 너무 적게 하면 품질검사 결과를 신뢰할 수 없고, 그렇다고 너무 많이 하게 되면 품질검사 한다고 많은 멀쩡한 제품이 손상되어 손실을 보게 되겠지요. 

 

따라서 샘플링 기법의 종류와 개념에 대해서 명확히 이해하고, Biz. 상황과 분석의 목적에 맞는 샘플링 기법을 적용해야 하겠습니다. 확률표본 추출 기법에는 (a) 단순 임의 추출, (b) 체계적 추출, (c) 층화 임의 추출, (d) 군집 추출, (e) 다단계 추출의 5가지 나눌 수 있으며, 아래에 개념 설명과 도식을 참고하시기 바랍니다. (비 확률표본 추출 기법은 생략)

 

 

[ 확률표본 추출 기법 ]

 

 

 

 

저는 실무에서는 단순 임의 추출과 층화 임의 추출을 가장 많이 사용하기에 이번 포스팅에서는 이 두개에 대해서 R 사용법을 소개해드리도록 하겠습니다. 

 

 

(1) 단순 임의 추출 (simple random sampling) : sample()

 

먼저 1~10까지 정수 벡터에 대해서 5개 표본을 비복원, 복원추출로 단순 임의 추출해보겠습니다.

 

> sample(1:10, 5, replace = FALSE) # 비복원추출
[1] 9 6 5 7 3
> 
> sample(1:10, 5, replace = TRUE) # 복원추출
[1] 9 3 3 2 3

 

단순 임의 추출은 sample(x, size, replace = FALSE/TRUE) 함수를 사용해서 쉽게 실행할 수 있습니다.  위 예시의 첫번째는 비복원 추출 (한번 뽑으면 다시는 안뽑힘) 옵션을 부여한 것이고, 두번째 예시는 복원추출(한번 뽑혔더라도 다시 뽑힐 수도 있음) 예시가 되겠습니다.  복원추출의 경우 1~10중에서 3이 3번 중복해서 추출되었습니다.

 

> sample(1:10, 5, replace = TRUE)
[1] 10  6  9  7 10
> sample(1:10, 5, replace = TRUE)
[1] 1 4 1 4 3

 

똑같은 sample() 명령어인데도 매번 실행할 때마다 표본 추출되는 결과가 다름을 알 수 있습니다.  R 내부적으로 난수표를 생성하면서 무작위로 샘플링을 하기 때문에 그렇습니다.

 

 

다음으로 MASS 패키지에 있는 Cars93 데이터 프레임의 93개 모집단에서 5개 표본을 단순 임의 추출을 해보도록 하겠습니다.

 

> library(MASS)
> dim(Cars93)
[1] 93 27

>

> sim_ran_sam <- sample(1:nrow(Cars93), 5)

> Cars93_srs <- Cars93[sim_ran_sam, ]
> dim(Cars93_srs)
[1]  5 27
 

 

 

 

 

(2) 층화 임의 추출 (stratified random sampling) : strata()

 

다음으로 성별, 연령대별로 고객을 계층(stratum)을 나누어서 임의 추출을 해보도록 하겠습니다.  이를 위해 데이터 핸들링하는 data.table 패키지와 샘플링 하는 sampling 패키지 설치 및 호출이 필요합니다. 

 

> ## data.table 패키지, sampling 패키지 설치 및 호출

> install.packages("data.table")
> install.packages("sampling")
> require(data.table)
> require(sampling)

 

 

 

다음으로 1000명으로 구성된 모집단을 성별(1, 0), 연령대별(1, 2, 3, 4, 5), 재구매여부별(1, 0)로 3개의 변수에 대해서 각각 확률을 설정해주고 d.t 라는 이름의 data.table 을 생성해보겠습니다. 

 

> set.seed(1) > n <- 1000 > d.t <- data.table(gender = rbinom(n, 1 , .5), + age = sample(1:5, n, replace=TRUE), + rebuy_yn = rbinom(n, 1, .2))

 

 

 

 

 

data.table 에서는 data.frame과는 달리 특정 칼럼을 key값으로 색인을 지정(setkey)해주게 되고, 이 key값으로 정렬을 해주게 됩니다. (참고로, 속도가 data.frame보다 빠름)  data.table 의 group 별 집계하는 방식이 data.frame하고는 좀 달라서 낯설것 같은데요, 아래의 두번째 예시 참고하시기 바랍니다.

 

> ## Key 색인 지정, 정렬 > setkey(d.t, gender, age) >

 

 

 

> ## 성, 연령대 계층(stratum) 별로 모집단 원소 수 (총 1,000명)

> d.t[ , .N, keyby = list(gender, age)] gender age N 1: 0 1 113 2: 0 2 108 3: 0 3 93 4: 0 4 106 5: 0 5 100 6: 1 1 115 7: 1 2 86 8: 1 3 96 9: 1 4 73 10: 1 5 110

 

 

다음으로 strata() 함수를 사용해서 층화 임의 추출을 하면 됩니다.

 

> ## 성별, 연령대 계층별 각 20명씩 층화 임화 추출 > set.seed(2) > samp <- data.table(strata(d.t, c("gender", "age"), rep(20, 10), "srswor")) >

 

 

> ## 성별, 연령대 계층별 각 표본 개수 (각 20명 씩 표본 추출) > samp[ , .N, keyby = list(gender, age)] gender age N 1: 0 1 20 2: 0 2 20 3: 0 3 20 4: 0 4 20 5: 0 5 20 6: 1 1 20 7: 1 2 20 8: 1 3 20 9: 1 4 20 10: 1 5 20

 

 

strata() 함수의 사용법은 아래와 같으며, 위에서 method로 "srswor"을 사용했는데요, 이는 simple random sampling without replacement (디폴트) 가 되겠습니다.

 

> help(strata)

 

strata(data, stratanames=NULL, size, method=c("srswor","srswr","poisson", "systematic"), pik,description=FALSE)

-- 중략 --

method

method to select units; the following methods are implemented: simple random sampling without replacement (srswor), simple random sampling with replacement (srswr), Poisson sampling (poisson), systematic sampling (systematic); if "method" is missing, the default method is "srswor".

-- 중략 --

 


여러개의 변수를 가진 DataFrame에 대해서 층화 무작위 추출을 사용해서 Train, Test set 분할을 하는 방법은 아래의 포스팅을 참고하세요. 

==> https://rfriend.tistory.com/515



이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

728x90
반응형
Posted by Rfriend
,

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

  - (3-1) 이산형화(discretization)    

  - (3-2) 이항변수화(binarization)

(4) 개수 축소

(5) 차원 축소

  - 주성분분석

  - 요인분석 

(6) 시그널 데이터 압축

 

중에서 이전 포스팅 (3-1) 이산형화(discretization)에 이어서 (3-2) 이항변수화(binarization)에 대해서 알아보겠습니다.  

 

이산형화가 다수의 구간으로 연속형 변수를 범주화 하는 것이라면, 이항변수화는 '1'과 '0'의 2개의 값으로 가변환(dummy variable)을 만드는 것을 말합니다.  연관분석이나 회귀분석 등에 이항변수화가 필요한 경우가 있습니다.  가령, 연관분석에서는 상품별 구매 여부(1, 0) 거래 데이터 (transaction data)를 가지고 패턴을 찾게 되는데요, 거주 지역이라든지 소득수준을 이항변수화 해서 행렬(matrix)로 만들어서 연관 규칙을 찾는데 활용할 수 있습니다 (더 정확히 말하자면, 메모리를 줄이기 위해서 sparse matrix 형식으로 저장).  또는 만약 시계열 자료가 고정계절변동(계절요인의 변동을 시간의 변화에도 일정하게 변동량을 유지하는 경우)을 가지는 경우에 가변수를 이용한 시계열 회귀모형을 시도해봄직 합니다. 

 

 

 R 데이터 변환 : (3-2) 이항변수화 (binarization)

 

[ 데이터 변환의 구조 ] 

 

 

(1) ifelse() 함수

 

ifelse() 함수를 활용해서 고객 프로파일 내 연령을 10대 간격으로 해서 20대 여부(1, 0), 30대 여부(1, 0), 40대 여부(1, 0), 50대 여부(1, 0) 의 가변수를 만들어보도록 하겠습니다.

 

> ## 고객 프로파일 데이터 프레임 생성

> cust_id <- c("c01", "c02", "c03", "c04", "c05", "c06", "c07")
> age <- c(25, 45, 31, 30, 49, 53, 27)
> 
> cust_profile <- data.frame(cust_id, age, stringsAsFactors = F)
> 
> cust_profile
  cust_id age
1     c01  25
2     c02  45
3     c03  31
4     c04  30
5     c05  49
6     c06  53
7     c07  27
> 
> sapply(cust_profile, class) 
    cust_id         age 
"character"   "numeric" 
> 
> ## 연령대 이항변수화
> cust_profile <- transform(cust_profile, 
+                           age_20 = ifelse(age >= 20 & age < 30, 1, 0), 
+                           age_30 = ifelse(age >= 30 & age < 40, 1, 0), 
+                           age_40 = ifelse(age >= 40 & age < 50, 1, 0), 
+                           age_50 = ifelse(age > 50 & age < 60, 1, 0))

 

> cust_profile
  cust_id age age_20 age_30 age_40 age_50
1     c01  25      1      0      0      0
2     c02  45      0      0      1      0
3     c03  31      0      1      0      0
4     c04  30      0      1      0      0
5     c05  49      0      0      1      0
6     c06  53      0      0      0      1
7     c07  27      1      0      0      0

 

 

 

위 화살표에서 보는 것처럼 해당 연령대에 '1'이, 아닌 곳에는 '0'이 들어가 있는 가변수들을 볼 수 있습니다.   이런 가변수를 활용해 연관분석을 하게 되면 if {연령대 = 20대, 케익구매} -> {반지 구매} 와 같이 구매 상품 정보뿐만 아니라 고객의 프로파일 정보도 같이 반영된, 그래서 타케팅 적중률을 더 높일 수 있는 연관규칙을 도출해낼 수 있습니다.

 

 

가변수를 이용한 시계열회귀모형을 예로 들자면, 아래의 식처럼 시점 t에서의 시계열 추세, 시점 t에서의 계절효과, 시점 t에서의 오차로 분해할 수 있습니다.  시계열회귀분석식을 자세히 보시면 [ D1,t = 1 시점 t에서 계절이 1인 경우, 0 그 이외의 경우 ] 라고 해서 가변수 처리 되어 있음을 알 수 있습니다.

 

 

 

> ## 시계열 데이터 생성 > Season <- c("S1", "S2", "S3", "S4", "S1", "S2", "S3", "S4") > SalesAmt <- c(300, 800, 400, 100, 280, 750, 390, 60) > TS <- data.frame(Season, SalesAmt, stringsAsFactors = F) > > TS Season SalesAmt 1 S1 300 2 S2 800 3 S3 400 4 S4 100 5 S1 280 6 S2 750 7 S3 390 8 S4 60 >

> ## 시계열회귀분석용 가변수 생성 > TS <- transform(TS, + Season1 = ifelse(Season=="S1", 1, 0), + Season2 = ifelse(Season=="S2", 1, 0), + Season3 = ifelse(Season=="S3", 1, 0)) > > TS Season SalesAmt Season1 Season2 Season3 1 S1 300 1 0 0 2 S2 800 0 1 0 3 S3 400 0 0 1 4 S4 100 0 0 0 5 S1 280 1 0 0 6 S2 750 0 1 0 7 S3 390 0 0 1 8 S4 60 0 0 0

 

 

 

 

예측모형적합을 위한 이항변수화를 할 때 한가지 주의해야 할 점이 있습니다.  바로 위의 캡쳐해놓은 표에 보면 S1(봄), S2(여름), S3(가을)은 가변수를 별도로 만들었지만, 노란색으로 동그라미 쳐놓은 S4 (겨울)은 별도의 가변수가 없고 그냥 S1 = 0, S2 = 0, S3 = 0 으로만 처리되어 있습니다.  이는 실수로 S4(겨울)을 빼먹은게 아니구요, 가변수 함정(dummy trap)을 피하기 위해 의도적으로 전체 계절의 갯수(여기서는 봄, 여름, 가을, 겨울의 4개)에서 1개를 빼게 됩니다.  운동회 때 서로의 간격을 맞추어서 줄을 서려면 제일 먼저 하는게 '기준'을 정하고 손을 번쩍 들어 크게 "기준~"하고 외칩니다.  그런 후에야 나머지 학생들이 "기준"이 되는 학생에 맞추어서 대열을 맞추게 되는데요, 계절효과를 반영하는 모형 적합에서도 "기준"이 되는 계절은 가변수를 안만들고, 나머지 계절만 가변수를 만들어서, "기준"이 되는 계절을 기준으로 상대적인 영향도를 계수로 모형에 반영하게 되는 원리입니다.  만약 계절의 전체 개수만큼 가변수를 만들게 되면 최소제곱추정법을 적용할 때 역행렬을 구할 수 없게 되는 가변수 함정에 빠지게 되어 해를 구할 수 없게 됩니다. 

 

시계열분석 통계이론이 잘 이해가 안되시더라도 일단 어떤 상황에서 이항변수화를 하게 되는지, R로는 ifelse() 함수를 써서 어떻게 변환하는지 정도만 이해하셨으면 일단 이번 포스팅에서는 얻어갈 것 다 얻어가신겁니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

 

728x90
반응형
Posted by Rfriend
,

 

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

  - (3-1) 이산형화(discretization)

  - (3-2) 이항변수화(binarization)

(4) 개수 축소

(5) 차원 축소

  - 주성분분석

  - 요인분석 

(6) 시그널 데이터 압축

 

중에서 우선 (3-1) 이산형화(discretization)에 대해서 알아보겠습니다. 

 

분할표(contingency table)를 구하거나 카이제곱 검정을 할 때 범주형 변수를 대상으로 집단 간 독립성 검정을 하게 됩니다.  이처럼 분석기법에 따라서 연속형 변수를 범주형 변수로 변환을 한 이후에야 분석이 가능한 경우가 있습니다. 

 

혹은, 회귀분석을 한다고 했을 때 명목형, 범주형 자료에 대해서 가변수(dummy variable) 화 해서 분석을 진행해야 할 때도 있습니다.  가령 요일효과를 모형에 적합시키고자 한다면 요일 변수를 월요일 여부(mon_yn), 화요일 여부(tue_yn), ... , 토요일 여부(sat_yn) 등과 같이 1, 0 으로 코드화된 가변수로 변환해야 하는 경우도 있습니다.

 

이번 포스팅에서는 첫번째 경우의 (1) 이산형화, 두번째 경우의 (2) 이항변수화에 대해서 R에서는 어떻게 처리하는지 알아보도록 하겠습니다.

 

 

 R 데이터 변환 (3) 범주화 : 이산형화(discretization)

 

 

[ 데이터 변환 구성 ] 

 

 

 

 

(3-1) 이산형화 (Discretization)

 

연속형 변수를 범주형 변수로 변환하는 작업을 이산형화라고 합니다.  이산형화 변화 시에는 '몇 개의 범주로 나눌지?''구분선(cutting line)을 무슨 기준으로, 어디로 할지?'가 중요한 질문이 되겠습니다.

 

두 질문에 대한 학술적인 단 하나의 답안은 없습니다.  두 질문에 대해 공통적으로 분석/활용의 목적이 무엇이냐와 Biz. Domain Konwledge가 충분히 반영이 되어서 의사결정을 해야만 하고, 운영 과정상의 시행착오와 경험을 통한 긍정/부정적 피드백을 반영하여 지속적으로 개선해나가야 할 것입니다. 

 

이번 포스팅에서는 (a) 간격을 동일하게 한 범주화와 (b) quantile을 활용한 범주화 (c) frequency를 동일하게 한 범주화를 R로 어떻게 하는지Cars93 데이터셋의 고속도로 연비(MPG.highway)을 가지고 예를 들어보겠습니다.

 

(참고로, Cars93 은 MASS 패키지에 내장된 데이터 셋으로서, 자동차의 속성에 대해서 27개의 변수, 93개 자동차 관측치를 가진 데이터 프레임)

 

(a) 간격을 동일하게 한 범주화는 R의 hist() 함수로 히스토그램을 그려보는 것이 좋은 출발점이 될 수 있습니다. R의 hist() 함수의 디폴트 구간 개수가 꽤 좋은 결과를 내주거든요.  bin size를 조정해가면서 분포를 탐색해보고서 특정 구간에서 변곡점이 있다면, 혹은 특정 segment 나 factor 별로 분포상의 큰 차이를 보인다면 그 구분선을 가지고 범주를 나눌 수도 있을 것입니다.

 

R의 within() 함수를 활용하여 아래의 Cars93의 고속도로연비는 20~50까지 5단위씩 등간격으로 6개 범주로 나누어 보겠습니다. 

 

> library(MASS)
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ..
 

> ## 고속도로연비(MPG.highway) 히스토그램

> hist(Cars93$MPG.highway)
 

 

 

> ## Model, MPG.highway 두 개 변수만 선택해서 disc_1 데이터 프레임 생성

> disc_1 <- Cars93[,c("Model", "MPG.highway")]

> ## 상위 6개 미리보기

> head(disc_1) Model MPG.highway 1 Integra 31 2 Legend 25

3 90 26 4 100 26

5 535i 30 6 Century 31

>

>

> ## 6개 범주로 등간격 범주화

 

 

> disc_1 <- within( disc_1, { + MPG.highway_cd = character(0) + MPG.highway_cd[ MPG.highway >=20 & MPG.highway <25 ] = "20~25" + MPG.highway_cd[ MPG.highway >=25 & MPG.highway <30 ] = "25~30" + MPG.highway_cd[ MPG.highway >=30 & MPG.highway <35 ] = "30~35" + MPG.highway_cd[ MPG.highway >=35 & MPG.highway <40 ] = "35~40" + MPG.highway_cd[ MPG.highway >=40 & MPG.highway <45 ] = "40~45" + MPG.highway_cd[ MPG.highway >=45 & MPG.highway <=50 ] = "45~50" + MPG.highway_cd = factor(MPG.highway_cd, + level = c("20~25", "25~30", "30~35", + "35~40", "40~45", "45~50")) + })

> 

> ## 상위 6개 보기

> head(disc_1)
    Model MPG.highway MPG.highway_cd
1 Integra          31          30~35
2  Legend          25          25~30
3      90          26          25~30
4     100          26          25~30
5    535i          30          30~35
6 Century          31          30~35
 

 

 

> attributes(disc_1$MPG.highway_cd)
$levels
[1] "20~25" "25~30" "30~35" "35~40" "40~45" "45~50"

$class
[1] "factor"

 

 

> table(disc_1$MPG.highway_cd) # 분할표 생성

 

20~25 25~30 30~35 35~40 40~45 45~50 14 41 27 7 2 2

 

"MPG.highway" 변수 옆에 "MPG.highway_cd" 라는 범주형 변수가 생겼음을 알 수 있습니다.  나중에 통계분석과 연계하기 위해 "MPG.highway_cd" 변수를 within()함수의 제일 마지막 줄에서 요인(factor)으로 지정을 해줬고, level = c("20~25", "25~30", "30~35", "35~40", "40~45", "45~50")) 으로 순서형 요인(ordered factor)의 수준을 지정해주었습니다.

 

 

다음으로 (b) quantile을 활용한 범주화 방법에 대해서 알아보겠습니다.  MPG.highway를 0~25%, 25~50%, 50~75%, 75~100%의 구성비로 해서 4개 범주로 나누어보겠습니다.

 

> summary(disc_1$MPG.highway)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  20.00   26.00   28.00   29.09   31.00   50.00
> 

> # 4분위수의 1Q, 2Q, 3Q, 4Q를 기준으로 4개의 범주 생성

> disc_1 <- within( disc_1, {
+   MPG.highway_cd2 = character(0)
+   MPG.highway_cd2[ MPG.highway <  quantile(MPG.highway, 0.25) ] = "1Q"
+   MPG.highway_cd2[ MPG.highway >= quantile(MPG.highway, 0.25) 
+                    & MPG.highway < quantile(MPG.highway, 0.50) ] = "2Q"
+   MPG.highway_cd2[ MPG.highway >=quantile(MPG.highway, 0.50) 
+                    & MPG.highway < quantile(MPG.highway, 0.75) ] = "3Q"
+   MPG.highway_cd2[ MPG.highway >= quantile(MPG.highway, 0.75) ] = "4Q"
+   MPG.highway_cd2 = factor(MPG.highway_cd2, 
+                           level = c("1Q", "2Q", "3Q", "4Q"))
+ })
> 

> # 상위 6개 보기

> head(disc_1)
    Model MPG.highway MPG.highway_cd MPG.highway_cd2
1 Integra          31          30~35              4Q
2  Legend          25          25~30              1Q
3      90          26          25~30              2Q
4     100          26          25~30              2Q
5    535i          30          30~35              3Q
6 Century          31          30~35              4Q
 

 

 

> table(disc_1$MPG.highway_cd2)  # 분할표 생성

1Q 2Q 3Q 4Q 
22 17 25 29 

 

 

다음으로, (c) frequency를 동일하게 해서 4개 범주를 구성해보도록 하겠습니다.  먼저 고속도로연비(MPG.highway) 기준으로 정렬을 해줘야합니다.

 

> ## 고속도로연비(MPG.highway) 기준으로 오름차순 정렬

> disc_1 <- disc_1[order(disc_1$MPG.highway), ]

 

 

> dim(disc_1) # 93개 관측치, 4개 변수 [1] 93 4

 

## 관측치 개수

> dim(disc_1)[1]
[1] 93
> 

> disc_1$N <- seq(1:length(disc_1$MPG.highway)) # 1~93까지 순서대로 1씩 증가하는 N이라는 변수 생성

>

> # 동일 frequency (23개)로 4개 범주 생성

> disc_1 <- within( disc_1, { + MPG.highway_cd3 = character(0) + MPG.highway_cd3[ N <= 23 ] = "1st_Freq" + MPG.highway_cd3[ N >= 24 & N <= 46 ] = "2nd_Freq" + MPG.highway_cd3[ N >= 47 & N <= 69 ] = "3rd_Freq" + MPG.highway_cd3[ N >= 70 ] = "4th_Freq" + MPG.highway_cd3 = factor(MPG.highway_cd3, + level = c("1st_Freq", "2nd_Freq", "3rd_Freq", "4th_Freq")) + }) > > head(disc_1) Model MPG.highway MPG.highway_cd MPG.highway_cd2 N MPG.highway_cd3 17 Astro 20 20~25 1Q 1 1st_Freq 36 Aerostar 20 20~25 1Q 2 1st_Freq 26 Caravan 21 20~25 1Q 3 1st_Freq 89 Eurovan 21 20~25 1Q 4 1st_Freq 48 Q45 22 20~25 1Q 5 1st_Freq 87 Previa 22 20~25 1Q 6 1st_Freq

 

 

 

 

>

 

 

> table(disc_1$MPG.highway_cd3) 1st_Freq 2nd_Freq 3rd_Freq 4th_Freq 23 23 23 24

 

위의 분할표를 보면 4개 범주별로 23개, 23개, 23개, 24개(총 93개여서 마지막에 1개 더 넣음) 로 동일 frequency로 범주화가 되었음을 알 수 있습니다.  그런데 (c) 같은 frequency 로 범주화 시에 동일한 고속도로연비임에도 범주가 다르게 구분이 되는 수가 생깁니다.  아래 예에서 보면 고속도로연비가 28인 경우 "2nd_Freq"와 "3rd_Freq" 범주에 양다리 걸쳐있는것을 확인할 수 있습니다.  ("1st_Freq"와 "2nd_Freq"에도 고속도로연비 26이 양다리를 걸치고 있습니다.  또한 31이 "3rd_Freq"와 "4th_Freq"에 양다리를 걸치고 있습니다.)  이처럼 동일 frequency로 범주화시에는 중첩됨이 없이 범주화하기가 어려운 문제점이 있습니다.  따라서 해석의 용이성과 중첩 방지를 위해서 (a) 등간격 범주화 또는 (b) quantile 활용 범주화가 (c) 동일 freqency보다는 좀더 유용하다고 볼 수 있겠습니다. 

 

 

이항변수화 (binarization)은 다음번 포스팅에서 소개해드리겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

728x90
반응형
Posted by Rfriend
,
 

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

   - 이산형화

   - 이항변수화

(4) 개수 축소

 

(5) 차원 축소

   - 주성분분석

   - 요인분석 

(6) 시그널 데이터 압축

 

중에서 로그 변환, 제곱급 변환을 활용한 정규분포화에 대해서 알아보겠습니다. 이들 변환들이 정규분포가 아니었던 분포를 정규분포로 변환시키는데 활용되므로 정규분포화 변환이라는 카테고리로 묶어보았습니다. 

 

많은 통계기법이 정규분포를 가정하고 있으므로 정규분포(Gausian distribution)가 아닌 경우 정규분포로 변환시키는 것은 사전작업으로 필수적이라고 하겠습니다. 

 

 

 R 데이터 변환 (2) 정규분포화 log(), sqrt()

 

 

[ 데이터 변환 구성 ]

 

 

 

 

자연현상 중에, 우리 주변의 일상 중에 정규분포가 많은데요(예: 키, 몸무게, 통계성적 등), 그에 못지않게 멱함수 분포(Power-law distribution)도 많이 있답니다.  특히 개체간 상호작용과 (긍정적/부정적) 피드백이 작용하는 관계에서는 멱함수 분포가 존재할 가능성이 높습니다. 

 

아래는 한겨레신문에서 카이스트 정하웅 교수님 인터뷰하면서 정규분포(고소도로 네트워크)와 멱함수분포(항공망 네트워크)의 예로 들은 것인데요(바라바시 링크 책 참조), 멱함수 분포를 띠는 항공망 네트워크의 경우 허브(Hub) 역할을 하는 공항이 있다는 것이지요.  이를 척도 없는 네트워크(scale-free network)라고도 하는데요, 이런 허브(Hub), 매개자(Connector) 가 있음으로 해서 세상이 좁아진다(small world)는 이론이 뒷받침을 받게 됩니다.

 

[ 네트워크의 두가지 유형 ]

 

 

한 국가의 부의 분포를 보면 20%의 국민이 부의80%를 차지하고 있고, 마태복은 13장 12절에 있는 ‘무릇 있는 자는 받아 풍족하게 되고 없는 자는 그 있는 것 까지도 빼앗기리라’ 말씀을 따서 마태효과(Matthew effect)라는 이론이 있기도 한데요, 멱함수 분포의 예라고 할 수 있겠습니다. 

 

그밖에도 멱함수 분포를 따르는 것으로 논문 인용, 인터넷 네트워크, 전기회로도, 전기/하수구 네트워크, 뇌의 뉴런 네트워크, 전염병이나 성병의 전파(아래 그림 예의 왼쪽에서 오른쪽, 상에서 하 순서 참고), 산불이나 지진의 강도별 발생 빈도, 프로야구선수 또는 프로축구선수 연봉 등... 그 예를 들자면 아주 많습니다.  혹시 복잡계과학, 네트워크과학에 대해서 관심이 있으시면 바라바시의 링크(Linked), 버스트(Birst), 던컨와츠의 스몰 월드(Small World), 마큐뷰캐넌 사회적원자(Social Atom), 우발과 패턴(Ubiquity) 등의 책을 추천합니다.  저자들이 물리학자 혹은 사회과학자들인데요, 수학적 공식없이도 일반인들이 쉽고 재미있게 읽을 수 있도록 책을 썼습니다.  지적유희 측면에서 재미있어요.  복잡한 세상 속에 이런 규칙이...하고 놀랄겁니다.

 

 

[ 다양한 멱함수 분포 예 ]

 

 

옆길로 많이 샜는데요 ^^;, 이처럼 많은 멱함수 분포를 정규분포로 변환할 때 로그 변환이나 제곱근 변환을 사용하게 됩니다.

 

UsingR 패키지에 들어있는 cfb 데이터 프레임을 가지고 예를 들어보겠습니다. cfb 데이터셋은 소비자 재정에 관한 설문조사 샘플 데이터로서, 14개의 변수와 1000명의 관측치가 들어있습니다.

 

> install.packages("UsingR")
> library(UsingR)

> > data(cfb) # cfb 데이터 불러오기 > head(cfb) # 상위 6개 미리보기 WGT AGE EDUC INCOME CHECKING SAVING NMMF STOCKS FIN VEHIC X17470 5749.975 54 14 66814.19 6000 2000 0 500 39600 6400 X315 5870.634 40 12 42144.34 400 0 0 0 5400 21000 X8795 8043.695 35 14 25697.77 1000 160 0 0 15460 2000 X10720 6092.872 55 12 35976.87 2600 19100 0 0 54700 18250 X19170 7161.757 40 12 39060.61 1000 8300 0 3500 12800 9100 X22075 11429.633 82 12 13362.84 1000 0 50000 0 70500 7500 HOMEEQ OTHNFIN DEBT NETWORTH X17470 84000 0 40200 170800 X315 8000 0 58640 17760 X8795 12000 0 19610 9850 X10720 90000 0 8000 284950 X19170 47000 0 21000 268900 X22075 175000 0 0 253000 >

> summary(cfb$INCOME) # INCOME 요약통계량 Min. 1st Qu. Median Mean 3rd Qu. Max. 0 20560 38030 63400 69900 1542000 >

> hist(cfb$INCOME, breaks=500, freq=TRUE) # INCOME 히스토그램

 

 

 

 

 

(1) 로그 변환 : log()

 

> ## 로그 변환
> cfb <- transform(cfb, INCOME_log = log(INCOME + 1))
> hist(cfb$INCOME_log, breaks=500, freq=TRUE)

 

 

 

 

위의 로그 변환 시에 INCOME_log = log(INCOME + 1) 처럼 (INCOME +1) 을 했습니다.  INCOME 이 '0'부터 시작하는데 '0'을 로그 취하면 마이너스 무한대가 나오기 때문에 1을 더해서 오른쪽으로 1씩 이동시킨 후에 로그변환을 취했습니다.

 

히스토그램을 보면 이전의 멱함수 분포의 소득이 정규분포로 변환되었음을 알 수 있습니다.

 

 

(2) 제곱근 변환 : sqrt()

 

> ## 제곱근 변환
> cfb <- transform(cfb, INCOME_sqrt = sqrt(INCOME + 1))
> hist(cfb$INCOME_sqrt, breaks=500, freq=TRUE)
 

 

 

 

제곱근 변환은 sqrt() 함수를 사용합니다.  위의 예시를 보면 로그변환 보다는 제곱근 변환이 오른쪽에 수입이 엄청나게 많은 부자들을 덜 정규분포화 시킨다는 것을  알 수 있는데요, 원래 데이터의 분포를 보고 로그변환과 제곱근 변환 중에서 더 적합한 것을 선택해서 사용하면 되겠습니다.

 

정규성 검정을 할 때 정규 분위수-분위수 그림(Q-Q Plot)을 사용하는데요, 아래에 원래 INCOME, 로그 변환 INCOME_log, 제곱근 변환 INCOME_sqrt 의 세개 변수에 대해서 Q-Q plot을 그려보았습니다.   아래 Q-Q plot으로 봐서는 로그 변환이 가장 잘 정규성을 띠고 있네요.

 

> ## Q-Q plot
> par( mfrow = c(1,3))
> qqnorm(cfb$INCOME, main="Q-Q plot of INCOME")
> qqline(cfb$INCOME)
> 
> qqnorm(cfb$INCOME_log, main="Q-Q plot of INCOME_log")
> qqline(cfb$INCOME_log)
> 
> qqnorm(cfb$INCOME_sqrt, main="Q-Q plot of INCOME_sqrt")
> qqline(cfb$INCOME_sqrt)
> par(mfrow = c(1,1))
 

 

 

 

 

모집단의 분포 형태에 따른 대략적인 정규분포 변환 방법은 아래 표와 같습니다

 

 [ 분포 형태별 정규분포 변환 방법 ]

 

 distribution

before transformation

transformation function 

distribution

after transformation 

 left

X^3 

 normal distribution

(bell shape)

 mild left

X^2 

 mild right

sqrt(X) 

 right

ln(X) 

 severe right

1/X 

 

 

단일모집단의 정규성 검정 (shapiro test, Q-Q plot) 방법은 아래의 링크를 참조하세요.

R 단일 모집단 분포의 정규성 검정 : shapiro.test(), qqnorm(), qqline()

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

728x90
반응형
Posted by Rfriend
,

데이터 변환 방법으로서

 

(1) 표준화 (Standardizatio)

(2) 정규분포화

(3) 범주화

   - 이산형화

   - 이항변수화

(4) 개수 축소

(5) 차원 축소

   - 주성분분석

   - 요인분석 

(6) 시그널 데이터 압축

 

중에서 (1) 표준화(Standardization)에 대해서 알아보겠습니다. 

 

 

다양한 소스로 부터 데이터를 R로 불러와서, 결합하고, 결측값과 특이값을 확인 후 처리하고, 필요한 부분의 데이터만 선별적으로 선택 혹은 제거한 후에 분석의 목적과 필요에 따라서, 그리고 데이터의 형태에 따라서 다양한 데이터 변환 (data transformation) 작업을 수행합니다. 

 

고급 분석가와 그렇지 않는 분석가가 나뉘는 부분, 데이터 엔지니어와 데이터 분석가가 나뉘어 지는 부분이 여기서 부터 이지 않을까 싶습니다.  업에 대한 지시과 더불어 분석의 목적과 분석의 기법에 대해서 정확히 알아야 하고, 데이터의 형태가 그에 맞는지, 맞지 않다면 어떻게 변환을 해야 하는지 알아야 하기 때문입니다.  그리고 데이터 변환을 하는데 있어 통계적인 기본 지식이 필요하다보니 여기부터는 프로그래밍을 잘하지만 통계를 잘 모르는 데이터 엔지니어의 경우 어려움을 겪기 시작합니다.

 

이 변환 작업에는 많은 시간과 노력이 필요합니다.  그래서 데이터 분석을 업으로 삼으려고 생각했던 사람이라도 소위 데이터 전처리, 데이터 변환의 지난한 과정에 대해서 재미를 느끼지 못하면 오래 견디지 못하고 다른 커리어로 전향을 하기도 합니다.  그만큼 본격적인 통계/데이터마이닝 과정에 진입하기 위한 전초 단계로 중요하지만 쉽지 많은 않은 과정이라는 얘기입니다. 

 

모델링을 하는데 있어 분석 목적에 유의미하고 적합한 파생변수를 개발하고 input으로 넣는 것이 정말 중요합니다.  개념적 정의, 조작적 정의를 통해 파생변수를 개발하는 과정에 필수로 필요한 이론적 지식이 이번부터 해서 총 6번에 나누어서 진행할 데이터 변환이 되겠습니다. 

 

데이터 변환을 (1) 표준화, (2) 정규분포화, (3) 범주화, (4) 개수 축소(샘플링), (5) 차원 축소, (6) 시그널 데이터 압축 등의 6개 카테고리로 구분하였습니다.  대략적으로 봤을 때 (1) 표준화, (2) 정규분포화, (3) 범주화는 데이터 분포나 속성을 변화시키는 기법이고, (4) 개수 축소(샘플링), (5) 차원 축소, (6) 시그널 데이터 압축은 데이터 크기를 축소하는 기법이 되겠습니다.

 

이번 포스팅에서는 (1) 표준화의 (1-1) z 변환, (1-2) [0-1] 변환에 대해서 알아보겠습니다.  

 

 

데이터 변환 (1) 표준화 

 

 

[ 데이터 변환 구성 ]

 

 

 

 

(1-1) 표준정규분포 z 변환

 

우선 정규분포에 대해서 간략히 짚고 z 변환으로 넘어가겠습니다. 일상 생활 속에서 우리는 다양한 정규분포를 접하고 삽니다.  만약 100명의 수강생을 대상으로 통계와 R 분석 교육을 받고 시험을 치면 아마도 평균을 중심으로 종모양으로 좌우 분포가 비슷한 성적 분포를 띨 것입니다.  수강생 100명의 키와 몸무게를 조사를 해서 히스토그램을 그려보면 이 또한 평균을 중심으로 종모양으로 좌우 대칭인 정규분포를 띨 것입니다.  수강생 얼굴을 아직 본적도 없는데 이렇게 예언을 할 수 있다는거, 이게 참 신기한겁니다. ^^  만약 키의 평균과 표준편차를 저한테 알려주고, 수강생 100명 중에서 한 명의 수강생을 뽑아서 키를 재서 저에게 알려주면 그 수강생이 전체 100명 중에서 상위 몇 % 키에 속할지도 추측할 수 가 있습니다. 놀랍지요?

 

통계학에서는 '중심극한정리(central limit theorem)'이 정말 중요한 역할을 하는데요, 중심극한정리란 분포의 모양을 모르는 모집단으로부터 표본을 추출할 때, 표본평균 의 분포는 표본의 크기 n이 커짐(일반적으로 )에 따라 점점 정규분포로 근사해 간다는 성질을 말합니다.

 

 

참고 ) 중심극한정리 (Central Limit Theorem)

 

을 평균 , 분산 인 모집단으로부터의 크기 n 인 확률표본이라고 했을 때,

표본평균 의 분포는 n이 커짐에 따라 정규분포 으로 근사해 간다.

 

 

중심극한정리에서 표본평균 를 표준화하면

 

통계량 근사적으로 표준정규분포 을 따른다.

 

 

 

 

 이 중심극한정리에 근거해서 보통 샘플이 대략 30개 이상이면 표본평균이 정규분포로 근사한다고 가정하고 정규분포 가정에 근거한 다양한 통계분석 기법(추정과 검정 등...)을 적용할 수 있게 됩니다. 

 

이때 두 개 이상의 모집단으로 부터 표본의 크기가 큰 표본을 추출했을 때, 각 집단의 평균과 표준편차가 다르거나, 혹은 측정 scale 이 다른 경우에는 다수의 집단 간, 변수 간 직접적인 비교가 불가능하게 됩니다.   미국 달러, 유럽의 유로화, 중국의 위안화, 일본의 엔화, 그리고 한국의 원화를 각 각 1000 단위를 가지고 있다고 했을 때, 이게 서로간에 대비해서 얼마나 많은 건지, 값어치가 있는건지 직접 비교하는게 불가능한 것과 같은 이치입니다.  이때 특정 나라의 통화를 기준으로 삼고 다른 나라의 통화를 기준으로 변환을 하면 각 나라별 통화간의 돈의 가치를 비교할 수 있게 됩니다.  이게 표준화의 원리입니다.

 

위에서 정규분포의 중요성에 대해서 설명했는데요, 정규분포 중에서도 평균이 0, 표준편차가 1인 정규분포를 표준정규분포(standadized normal distribution) 이라고 합니다.  평균이 표준편차가 서로 다른 다수의 집합을 표준정규분포로 표준화를 하면 서로 비교를 할 수 있게 됩니다. 

 

그러면, 이제 R로 표준정규화 하는 방법에 대해서 알아보겠습니다.

 

  • 한국 성인 남성 1,000 명의 키가 평균 170cm, 표준편차 10cm의 정규분포
  • 남아프리카 부시맨 성인 남성 1,000명의 키가 평균 150cm, 표준편차 8cm의 정규분포

를 따른 다고 했을 때 두 집단의 키를 평균이 0, 표준편차가 1인 표준정규분포로 표준화를 해보도록 하겠습니다.

 

 

 먼저, 데이터 생성은 아래와 같이 랜덤하게 생성하였습니다.

 

> ## 한국인, 부시맨 각 성인 1000명 키 데이터 생성 > height_korean <- rnorm(n=1000, mean = 170, sd = 10) > height_bushman <- rnorm(n=1000, mean = 150, sd = 8) > > height <- data.frame(height_korean, height_bushman) # 데이터 프레임 생성

> rm(height_korean, height_bushman) # 벡터 삭제

> > head(height) # 상위 6개 데이터 확인 height_korean height_bushman 1 162.7654 132.5271 2 180.5701 135.5497 3 172.6752 142.5168 4 171.8035 156.7872 5 186.5258 154.3027 6 171.4634 156.1118 

 

> ## 한국인, 부시맨 키 히스토그램

> attach(height)
> par( mfrow = c(1,2))
> hist(height_korean, freq = TRUE, main = "한국인 키 빈도 히스토그램")
> hist(height_korean, freq = FALSE, main = "한국인 키 확률밀도함수 그래프")
> 

 

 

> hist(height_bushman, freq = TRUE, main = "부시맨 키 빈도 히스토그램")
> hist(height_bushman, freq = FALSE, main = "부시맨 키 확률밀도함수 그래프")

 

 

> detach(height)

 

 

그리고 표준정규화를 해보겠는데요, (a) scale()함수를 쓰는 방법과 (b) (x-mean(x))/sd(x) 처럼 공식을 직접 입력하는 방법이 있습니다.  결과는 동일합니다.

 

> ## a. scale() 함수
> 
> height <- transform(height, 
+                     z.height_korean = scale(height_korean), 
+                     z.height_bushman = scale(height_bushman)
+                     )
> 
> head(height)
  height_korean height_bushman z.height_korean z.height_bushman
1        179.19         140.60         0.89308         -1.18393
2        164.54         152.70        -0.60892          0.35689
3        184.18         136.76         1.40477         -1.67426
4        196.37         144.26         2.65531         -0.71833
5        162.61         155.72        -0.80706          0.74198
6        158.02         147.19        -1.27775         -0.34510

 

> ## b. z=(x-mean(x))/sd(x)
> height <- transform(height, 
+                     z2.height_korean = (height_korean - mean(height_korean))/sd(height_korean), 
+                     z2.height_bushman = (height_bushman - mean(height_bushman))/sd(height_bushman)
+                     )
> 
> head(height)
  height_korean height_bushman z.height_korean z.height_bushman z2.height_korean z2.height_bushman
1        179.19         140.60         0.89308         -1.18393          0.89308          -1.18393
2        164.54         152.70        -0.60892          0.35689         -0.60892           0.35689
3        184.18         136.76         1.40477         -1.67426          1.40477          -1.67426
4        196.37         144.26         2.65531         -0.71833          2.65531          -0.71833
5        162.61         155.72        -0.80706          0.74198         -0.80706           0.74198
6        158.02         147.19        -1.27775         -0.34510         -1.27775          -0.34510 

 

 

아래 히스토그램은 한국인과 부시맨의 성인 남자 키를 z 표준화 한 값에 대한 히스토그램이 되겠습니다.  둘다 평균이 0, 표준편차가 1인 표준정규분포로 표준화 되었음을 확인할 수 있습니다.

 

> hist(height$z.height_korean, freq=TRUE, main="standized freq. of Korean H")
> hist(height$z.height_bushman, freq=TRUE, main="standized  freq. of Bushman H ")

 

 

 

 

 

(1-2) [0-1] 변환

 

연속형 변수의 값을 '0~1' 사이의 값으로 변환하는 [0-1]변환도 z변환과 함께 많이 쓰이는 표준화 기법입니다.  만약 변수들 간의 scale 이 다른 상태에서 인공신경망 분석을 하려면 [0-1]변환으로 단위를 표준화해준 후에 분석을 시행해야 합니다.  Scale이 다른 두 변수를 [0-1] 변환하게 되면 상호간에 비교가 가능해집니다.

 

[0-1] 변환은  (x-min(x) /(max(x)-min(x)) 의 수식으로 계산하면 됩니다.

 

위의 한국 성인 남성과 부시맨 성인 남성 각 1,000명의 키 데이터를 가지고 이번에는 [0-1] 표준화 변환을 해보도록 하겠습니다.  일단 위 데이터셋 height에서 첫번째와 두번째 변수만 선택하고, 변수명이 너무 길므로 짧게 변수이름을 변경해보겠습니다.

 

> ## [0-1] transformation
> height <- height[,c(1:2)]
> library(reshape)
> height <- rename(height, c(height_korean = "h_kor", height_bushman = "h_bush"))
> head(height)
   h_kor h_bush
1 179.19 140.60
2 164.54 152.70
3 184.18 136.76
4 196.37 144.26
5 162.61 155.72
6 158.02 147.19

 

 

그 다음 [0-1] 변환을 하고 히스토그램을 그려보겠습니다.

 

> height <- transform(height, 
+                     h_kor_01 = (h_kor - min(h_kor))/(max(h_kor) - min(h_kor)), 
+                     h_bush_01 = (h_bush - min(h_bush))/(max(h_bush) - min(h_bush))
+                     )
> 
> head(height)
   h_kor h_bush h_kor_01 h_bush_01
1 179.19 140.60  0.64341   0.27053
2 164.54 152.70  0.41760   0.51072
3 184.18 136.76  0.72034   0.19410
4 196.37 144.26  0.90835   0.34311
5 162.61 155.72  0.38781   0.57074
6 158.02 147.19  0.31705   0.40129
> 
> hist(height$h_kor_01)
> hist(height$h_bush_01)
 
 

 

 

 

한국 성인 남성 키와 부시맨 성인 남성 키가 0~1 사이의 값으로 표준화되었음을 알 수 있습니다.

 

이해가 쉽도록 166cm의 한국 남성과 156cm의 부시맨 남성의 키를 가지고 [0-1] 변환 했을 때의 예시를 개념도로 아래에 작성하였습니다.  참고하시기 바랍니다.

 

 

[0-1] 변환 예시 (한국 남성 166cm, 부시맨 남성 156cm) 

 

 

 

많은 도움 되었기를 바랍니다. 

 

다음번 포스팅에서는 정상화 변환에 대해서 알아보도록 하겠습니다.



여러개의 변수를 가진 DataFrame에 대해서 층화 무작위 추출을 사용해서 Train, Test set 분할을 하는 방법은 아래의 포스팅을 참고하세요. 

==> https://rfriend.tistory.com/515


 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 


728x90
반응형
Posted by Rfriend
,