▹ 우선 각각의 점수를 입력받는다
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))
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))
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) *
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")
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: 구매 비구매 비구매
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_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