Author

▹ JungHwan Yun
▹ Master Student in Data-Science
▹ Seoul National University of Science & Technology(SeoulTech)
▹ E-mail : junghwan.yun@seoultech.ac.kr




Chapter 01 : 거리개념과 MDS


접근: 여러속성을 가지고 있는 데이터들을 원하는대로 군집시켜주기 위해서는 객체의 속성값이 유사한 형태로 배치되어있는경우를 찾아서 묶어주어야한다. 그러기 위해서는 속성간의 거리를 산출할 수 있어야한다.

분석방법: 거리를 측정하는 방법은 여러가지가 존재한다. 데이터의 속성에 따라 다른 형태의 거리개념을 이용하여 거리를 산출해주면된다.

  • 일반적인 두 점 사이의 거리를 산출 : Euclidean
  • 두 벡터 사이의 거리를 산출 : cosine similarity
  • 2차원 이상의 거리를 산출하는 경우 : MDS - MultiDimension Scaling(다차원척도법)


Ex1) 학원원장인 A씨는 점수가 나오는 비슷한 성향의 학생들을 묶기를 원한다고 한다.

▹ 우선 각각의 점수를 입력받는다

dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input <- "academy.csv"

academy <- read.csv(paste(dir,input,sep = ""), stringsAsFactors = F, header = T, fileEncoding = "cp949", encoding = "UTF-8")
academy <- academy[-1]
kable(head(academy)) %>% kable_styling("bordered")
국어점수평균 수학점수평균 영어점수평균 과학점수평균 학업집중도
90 75 85 60 70
65 90 60 88 80
45 53 48 50 60
77 96 62 92 70
88 89 80 82 90
90 92 90 96 100


▹ 개별 학생들 점수을 유클리디안 거리를 이용하여 산출한다. 이를 cmdscale(2차원 이상의 함수를 2차원으로 도식시켜준다)를 이용하여 보기 쉽게 확인한다

dist_academy <- dist(academy, method = "euclidean")
two_coord <- cmdscale(dist_academy)
plot(two_coord, type = "n")
text(two_coord, as.character(1:52))



Ex2) 순댓국밥집 사장인 B씨는 새로운 메뉴를 개발하고자한다. 기존 손님들이 좋아하는 기존의 음식은 무엇인지 파악하고 이를 기반으로 새로운 형태의 메뉴를 개발하고자 한다.

dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input <- "food.csv"
food <- read.csv(paste(dir,input,sep = ""), stringsAsFactors = F, header = T, fileEncoding = "cp949", encoding = "UTF-8")

food <- food[-1]
kable(head(food)) %>% kable_styling("bordered")
추어탕 갈비탕 김치볶음밥 뼈다귀해장국 북어해장국 순대국 쭈꾸미볶음 김치찌개 쌈밥정식 삼계탕
1 0 0 1 0 1 0 0 0 1
0 0 1 0 0 0 0 0 0 -1
0 0 0 0 1 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 1


▹ 이러한 경우는 앞선 ex1과는 처리방식이 약간 다른 것을 알 수 있다. 점수로 표기된 앞선 예제와는 달리 긍부정으로 표기된 것을 알 수 있다. 이러한 경우 자칫 일반화된 방식으로 유클리디안 거리를 산출할 경우 잘못된 거리가 산출 될 수 있다.
▹ 이러한 경우에는 행렬을 전치하여 곱해주는 형태의 상관행렬을 만들어서 적용해야한다. 음식과 음식사이에 동시출현하는 형태를 상관행렬로 표현하는 방식인 셈이다.

food.multi <- t(as.matrix(food)) %*% as.matrix(food)
kable(head(food.multi)) %>% kable_styling("bordered")
추어탕 갈비탕 김치볶음밥 뼈다귀해장국 북어해장국 순대국 쭈꾸미볶음 김치찌개 쌈밥정식 삼계탕
추어탕 17 3 -1 4 1 5 -1 0 -2 1
갈비탕 3 11 -2 0 0 2 -2 -2 -1 3
김치볶음밥 -1 -2 8 0 0 -1 0 2 1 1
뼈다귀해장국 4 0 0 10 2 6 0 -1 -1 3
북어해장국 1 0 0 2 10 2 1 2 -1 7
순대국 5 2 -1 6 2 12 0 0 -1 4


▹ 이렇게 나온 결과를 plot으로 도식해보겠다.

dist_food <- dist(food.multi, method = "euclidean")
two_coord <- cmdscale(dist_food)
par(family="NanumGothic")
plot(two_coord, type = "n")
text(two_coord, rownames(food.multi))





Chapter 02 : Decision Tree


접근 : 아파트 상가내에서 화장품 소매점을 운영하고 있는 김씨는 자주 오는 손님들에게 할인쿠폰을 주는 서비스를 진행했다. 일정기간 서비스를 진행한 뒤 과연 어떤 종류의 사람들이 할인쿠폰에 대한 반응이 좋을까에 대한 궁금점이 생겼다. 이를 조사해서 분석하기로 했다.

분석방법 : Input Data를 입력했을때, Yes / No와 같이 Target Label이 있는 경우에 Target Label이 최대한 잘 나눠질 수 있는 속성 정보들을 나누는 분류 문제이다.

데이터 : 고객별 기본 인적사항들(x-value)과 이에 따른 화장품 쿠폰반응여부(y-value)

dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input <- "skin.csv"
skin <- read.csv(paste(dir,input,sep = ""), header = T, fileEncoding = "cp949", encoding = "UTF-8")
skin <- skin[-1]
kable(head(skin)) %>% kable_styling("bordered")
성별 나이 직장여부 결혼여부 차량보유여부 쿠폰반응여부
30대 NO YES NO NO
20대 YES YES YES NO
20대 YES YES NO NO
40대 NO NO NO NO
30대 NO YES NO NO
30대 NO NO YES NO


▹ 데이터의 구조를 살펴보도록하자 - 분류모형중 의사결정나무에 데이터를 삽입해야하는 경우 데이터의 속성들이 명목형(nominal)이어야한다.

str(skin)
'data.frame':   30 obs. of  6 variables:
 $ 성별        : Factor w/ 2 levels "남","여": 1 2 2 2 2 2 2 2 2 1 ...
 $ 나이        : Factor w/ 3 levels "20대","30대",..: 2 1 1 3 2 2 1 1 2 3 ...
 $ 직장여부    : Factor w/ 2 levels "NO","YES": 1 2 2 1 1 1 1 1 2 2 ...
 $ 결혼여부    : Factor w/ 2 levels "NO","YES": 2 2 2 1 2 1 2 2 2 1 ...
 $ 차량보유여부: Factor w/ 2 levels "NO","YES": 1 2 1 1 1 2 1 2 1 2 ...
 $ 쿠폰반응여부: Factor w/ 2 levels "NO","YES": 1 1 1 1 1 1 1 2 2 1 ...


▹ Decision Tree의 경우 각 변수를 이용하여 Enthropy와 이를 기반으로한 Information Gain을 산출하여 Node를 Split하는 방식을 사용한다. ▹ 엔트로피를 계산하는 예시를 살펴보도록하자. - 아래의 벡터에는 2개의 Blue와 3개의 Red가 들어있는 것을 알 수 있다 - 이는 불순도가 매우 높은 상황으로 실제 엔트로피를 구하면 매우 높은값을 얻을 수 있을 것이다.

x <- c("red","blue","blue","red","red")

info_entropy <- function(x) { 
  
  factor_x <- factor(x) 
  entropy <- 0 
  for(str in levels(factor_x)) {
      pro <- sum(x == str) / length(x) 
      entropy <- entropy - pro * log2(pro) 
  }
  return (entropy)
}

info_entropy(x)
## [1] 0.9709506

▹ 의사결정나무를 직접생성하여 결과를 확인해보자

library(rpart)
tree <- rpart(쿠폰반응여부 ~. ,data = skin, control = rpart.control(minsplit = 4))
par(family="NanumGothic")
plot(tree, compress = T, uniform = T, margin = 0.1)
text(tree, use.n =  T, col = "blue")


▹ 트리가 나뉘어진 기준을 살펴보면 아래와 같다 - 여기서 minsplit은 최소 가지수이다 - 실제로 나뉜 결과를 본다면 결혼여부는 매우 유의미한 변수임을 알 수 있다.

tree
n= 30 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 30 12 NO (0.6000000 0.4000000)  
   2) 결혼여부=NO 10  0 NO (1.0000000 0.0000000) *
   3) 결혼여부=YES 20  8 YES (0.4000000 0.6000000)  
     6) 나이=20대,30대 16  8 NO (0.5000000 0.5000000)  
      12) 성별=남 5  1 NO (0.8000000 0.2000000) *
      13) 성별=여 11  4 YES (0.3636364 0.6363636)  
        26) 나이=20대 5  2 NO (0.6000000 0.4000000)  
          52) 직장여부=YES 2  0 NO (1.0000000 0.0000000) *
          53) 직장여부=NO 3  1 YES (0.3333333 0.6666667) *
        27) 나이=30대 6  1 YES (0.1666667 0.8333333) *
     7) 나이=40대 4  0 YES (0.0000000 1.0000000) *





Chapter 03 : Clustering


접근 : 앞서 chapter01에서 거리개념을 이용하여 plot시킨 결과를 확인했었다. 이를 기반으로 실제 거리개념을 가지고 군집을 생성하는 결과를 만들어보려고한다.

분석방법 : 거리개념을 기반으로 하여 분류를 하는 문제이다. 하지만 앞서 본 의사결정나무와는 약간 다른점은 Target Label이 없다는 것이다. 즉 어떻게 나뉘는게 정답인지에 대한 정답데이터가 없다는것을 의미한다. 이러한 방식을 비교사학습(Unsupervised Learning)이라고 한다.

군집간의 거리측정방법 : 군집을 계층화하는 과정에서 군집사이의 거리를 측정하고 군집을 생성하는데에는 다양한 방식이 존재한다.

  • 최단연결법(Single-Linkage) : 최단거리를 가지는 군집을 묶는다
  • 최장연결법(Complete-Linkage) : 최장거리를 가지는 군집을 묶는다
  • 평균연결법(Average-Linkage) : 거리의 평균값을 이용하여 군집을 묶는다
  • 중심연결법(Centroid-Linkage)
  • 워드연결법(Ward-Linkage)

<분석예시>

Ex1. 계층형 군집 : hclust() 함수를 사용한다

library(cluster)
hcl <- hclust(dist(academy)^2, method = "single")
plot(hcl, hang = -1 , xlab = "student", ylab = "distance")



Ex2. K-means 군집: k =5인 k-means 군집을 생성해보자

library(graphics)
(kms <- kmeans(academy, 5))
K-means clustering with 5 clusters of sizes 9, 9, 11, 9, 14

Cluster means:
  국어점수평균 수학점수평균 영어점수평균 과학점수평균 학업집중도
1     90.22222     89.66667     87.55556     90.44444   90.00000
2     52.66667     48.88889     52.66667     49.33333   62.22222
3     89.90909     68.27273     84.09091     62.81818   72.72727
4     56.00000     74.88889     62.33333     79.66667   65.55556
5     71.35714     90.14286     70.92857     90.57143   73.57143

Clustering vector:
 [1] 3 5 2 5 1 1 4 5 2 3 5 3 3 2 3 4 5 1 5 5 5 5 2 4 5 4 3 1 4 3 5 1 2 3 2
[36] 4 5 1 1 4 3 1 3 4 2 5 3 4 1 2 2 5

Within cluster sum of squares by cluster:
[1]  808.000 4776.444 2527.818 1577.111 2082.714
 (between_SS / total_SS =  80.7 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"    
[5] "tot.withinss" "betweenss"    "size"         "iter"        
[9] "ifault"      
par(family="NanumGothic")
plot(academy, col = kms$cluster)



Ex3. 최적의 군집개수 구하기

▹ Elbow Point라는 개념을 사용한다.
▹ K = 6일때가 가장 완만한 변화를 보이는 부분이다. 이를 Elbow Point라고 칭하고 군집의 개수가 6개일때가 가장 적절하다고 판단한다.

wss <- 0
for(i in 1:10) wss[i] <- sum(kmeans(academy,centers = i)$withinss)
plot(1:10, wss, type = "b", xlab = "Number of Clusters", ylab = "Within group sum of squares")





Chapter 04 : K-nearst neighbors


접근 : 혜영씨는 남자친구를 만들기위해 여러번의 소개팅 중이다. 혜영씨에게 지영씨는 여럿의 남자를 소개시켜주지만 번번히 실패하는 것을 알 수 있었다. 이를 극복하기 위해서 새로운 사람을 소개 받았을때 그 주변사람들의 정보를 이용하여 새로 소개받은 사람의 정보를 추정해보는 방법이다. 유유상종이라는 개념이 제대로 적용된 방법론이라고 할 수 있다.

분석방법 : 어떤 데이터의 라벨을 정의 할 때에 그 데이터의 주변 반경 x안의 데이터들의 라벨들을 조사하여 다수결로 k개 이상이며 가장 많은 것의 라벨로 정의하는 방식이다.

데이터 : 혜영씨의 과거 소개팅 데이터를 수치화 작업을 한 데이터, 총 14건


Ex1. K-NN : 새롭게 입력되는 데이터의 라벨을 예측해보자

dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input <- "like.csv"
like <- read.csv(paste(dir,input,sep = ""), stringsAsFactors = F ,header = T, fileEncoding = "cp949", encoding = "UTF-8")
colnames(like) <- c("talk","book","travel","school","tall","skin","muscle","label")
kable(head(like)) %>% kable_styling("bordered")
talk book travel school tall skin muscle label
30 80 40 40 90 90 50 1타입
60 50 70 50 60 60 90 2타입
50 40 80 60 50 70 80 2타입
90 50 60 30 40 20 30 3타입
50 70 30 70 70 80 60 1타입
70 30 60 40 40 40 30 3타입


▹ 이런얘기는 좀 그렇지만… 새롭게 소개를 시켜주고 싶은 사람의 스펙(?)을 조금 정량화해본다.

new.input <- data.frame(talk=70, book = 50, travel = 30, school = 70, tall = 70, skin = 40, muscle = 50)
new.input
  talk book travel school tall skin muscle
1   70   50     30     70   70   40     50


▹ 이제 K-NN을 적용해보도록하자. 과연 혜영씨의 이전데이터를 기반으로 한다면 새롭게 소개팅을 받을 사람은 어떠한 평가를 받을 수 있을 것인가?!
▹ Train 데이터와 평가데이터를 분할하며, K가 3,4일때를 예시로 하여 모델링을 해보자

library(class)
train <- like[,-8]
label <- like[,8]
knn.pred.k3 <- knn(train, new.input, label, k =3, prob = T)
knn.pred.k4 <- knn(train, new.input, label, k =4, prob = T)


▹ 이럴수가.. 새롭게 소개팅을 받을사람은 어떻게 해도 Type3가 나온다.. 안맞을 운명이가부다…

knn.pred.k3
[1] 3타입
attr(,"prob")
[1] 0.6666667
Levels: 1타입 2타입 3타입
knn.pred.k4
[1] 3타입
attr(,"prob")
[1] 0.75
Levels: 1타입 2타입 3타입


Ex2. 입력변수들의 속성이 제각각인 경우의 K-NN을 수행해보자
▹ 각각의 데이터의 단위가 다른 경우, 변수들을 표준화해주는 과정이 필요하다. 표준화와 정규화는 다른개념이다. (정규화는 변수를 정규분포의 형태로 바꿔주는 과정이다)

▹ 나이와 월수입을 표준화하여 새로운 변수로 넣어준다.

dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input <- "buy.csv"
buy <- read.csv(paste(dir,input,sep = ""), stringsAsFactors = F ,header = T, fileEncoding = "cp949", encoding = "UTF-8")
buy$age <- scale(buy$나이)
buy$pay <- scale(buy$월수입)

kable(head(buy)) %>% kable_styling("bordered")
나이 월수입 상품구매여부 age pay
26 160 구매 -0.6602858 -0.7924375
35 210 비구매 0.9904287 -0.2533644
26 220 비구매 -0.6602858 -0.1455498
29 260 구매 -0.1100476 0.2857088
22 110 비구매 -1.3939367 -1.3315107
32 210 비구매 0.4401905 -0.2533644


▹ 이제 새로운 고객이 들어왔을때를 가정해보자 ▹ 나이는 44살이며, 월급은 400만원인 사람일때 이 고객이 제품을 구매할 확률은 과연 얼마나 될 것 인가?

test <- data.frame(age = 44, pay = 400)
kable(test) %>% kable_styling("bordered")
age pay
44 400


▹ 새롭게 입력받은 데이터 역시 표준화를 거친뒤에 K-NN으로 결과를 확인해보도록하자

train <- buy[,c(4,5)]
labels <- buy[,3]

test$age <- (test$age - mean(buy$나이)) / sd(buy$나이)
test$pay <- (test$pay - mean(buy$월수입)) / sd(buy$월수입)
knnpred1 <- knn(train , test , labels , k=5 , prob=TRUE) 
knnpred2 <- knn(train , test , labels , k=6 , prob=TRUE) 


▹ K=5,6일때 모두 구매할 확률이 매우 높은 것을 알 수 있다. 백화점에게 아주 도움이 되는 고객일거야 ..

knnpred1
[1] 구매
attr(,"prob")
[1] 0.8
Levels: 구매 비구매 비구매 
knnpred2
[1] 구매
attr(,"prob")
[1] 0.6666667
Levels: 구매 비구매 비구매 





Chapter 05: Regression


접근: 김원일씨는 주식에 관심이 많ㅇ다. 자신의 성향은 좀 보수적인 편이다. 너무 공격적으로 주식을 매입하다가 안타깝게도 돈을 많이 읽었기 때문이다. 안정적이고 보수적인 주식을 어떻게 알 수 있을까? 그것을 수치로 평가한다면 적당히 조절해서 주식을 매입하고 싶다. 경기에 너무 민감해서 들쭉날쭉한 등락률을 가진 주식은 정신적으로 감당할 자신이 없기 때문이다.

분석방법 : 회귀분석은 매우 오래된 통계분석이론이다. 독립변수와 종속변수 그리고 잔차의 관계로 종속변수와 높은 관계을 나타내는 독립변수를 찾아내는 과정이다.

데이터 : 한달동안의 코스피 지수를 이용하여 코스피지수의 등락률과 위험의 선형관계를 증명하고자 한다.

Domain Knowledge

  • 등락률 : (오늘코스피지수 - 전일코스피지수)/(전일코스피지수) *100
  • 기간t의 기대수익률 : 등락률의 t기간동안의 평균
  • 기간t의 위험도 : 등락률의 t기간동안의 표준편차
dir <- "/Users/junghwan/Google Drive/Machine_Learing_Data/TrainData/"
input.index <- "K_index.csv"
input.s_stock <- "S_stock.csv"
input.h_stock <- "H_stock.csv"

k_index <- read.csv(paste(dir,input.index,sep = ""), stringsAsFactors = F ,header = T, fileEncoding = "cp949", encoding = "UTF-8")
s_stock <- read.csv(paste(dir,input.s_stock,sep = ""), stringsAsFactors = F ,header = T, fileEncoding = "cp949", encoding = "UTF-8")
h_stock <- read.csv(paste(dir,input.h_stock,sep = ""), stringsAsFactors = F ,header = T, fileEncoding = "cp949", encoding = "UTF-8")

all.data <- merge(merge(k_index,s_stock),h_stock)
kable(head(all.data)) %>% kable_styling("bordered")
date kospi k_rate s_price s_rate h_price h_rate
2015-03-25 2042.81 NA 1485000 NA 45500 NA
2015-03-26 2022.56 -0.991 1421000 -4.310 45900 0.879
2015-03-27 2019.80 -0.136 1421000 0.000 45000 -1.961
2015-03-30 2030.04 0.507 1428000 0.493 45600 1.333
2015-03-31 2041.03 0.541 1441000 0.910 46000 0.877
2015-04-01 2028.45 -0.616 1423000 -1.249 45950 -0.109


▹ 다음은 입력데이터에서 s전자의 100일동안의 코스피 일일변동량과 그 값을 산점도로 나타낸 것이다.

all.data$idx <- 1:249
attach(all.data) 
plot(idx[2:100] , k_rate[2:100] , type = "l" , xlab = "date" , ylab = "rate")
lines(idx[2:100] , s_rate[2:100] , col="blue")
abline(h=mean(k_rate[2:100]) , lty=2 , col="black") 
abline(h=mean(s_rate[2:100]) , lty=2 , col="blue") 

plot(k_rate , s_rate) 

▹ 일반적으로 사용되는 회귀분석은 일반적인 선형방정식에서 비용함수(Cost Function)가 최소가 되는 최소제곱추정의 방식을 사용한다. ▹ lm()을 이용해 선형방정식을 도출하고 그 결과를 살펴보자 - s전자의 결과를 살펴보면 k_rate의 Estimate Std.값이 1.00133으로 전체적인 코스피와 유사한 주가임을 알 수 있다.

s_lm <- lm(s_rate ~ k_rate , data = all.data) 
summary(s_lm) 

Call:
lm(formula = s_rate ~ k_rate, data = all.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.6504 -0.8373 -0.0862  0.7410  7.9590 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.03500    0.09273  -0.377    0.706    
k_rate       1.00133    0.10647   9.404   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.46 on 246 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.2644,    Adjusted R-squared:  0.2615 
F-statistic: 88.44 on 1 and 246 DF,  p-value: < 2.2e-16
  • h전기의 결과를 살펴보면 k_rate의 Estimate Std.값이 0.6348로 다소 방어적인 종목임을 알 수 있다.
h_lm <- lm(h_rate ~ k_rate , data = all.data) 
summary(h_lm) 

Call:
lm(formula = h_rate ~ k_rate, data = all.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.1433 -1.0825 -0.0597  0.8846  4.7971 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.1263     0.1021   1.237    0.217    
k_rate        0.6348     0.1173   5.414 1.47e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.608 on 246 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.1065,    Adjusted R-squared:  0.1028 
F-statistic: 29.31 on 1 and 246 DF,  p-value: 1.466e-07