rm(list = ls())


# 실루엣 계수
# 필요한 패키지 설치
# install.packages("cluster")
# install.packages("factoextra")
# 패키지 로드
library(cluster)
library(factoextra)
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# 데이터 준비 (iris 데이터에서 품종 제거)
data(iris)
df <- iris[, -5]  # 군집용 수치형 데이터만 사용

# K-평균 군집 수행 (예: K=3)
set.seed(123)
km_result <- kmeans(df, centers = 3, nstart = 25)
# K-평균 알고리즘은 초기 중심점(centroid)을 무작위로 선택
# 초기 위치에 따라 결과가 좋기도 하고, 나쁘기도 합니다.
# 25번 서로 다른 초기화 시도
# 그 중에서 가장 좋은(오차 제곱합 SSE가 가장 낮은) 결과를 선택합니다.
# nstart의 값을 달리하면 다른 결과가 나올 수 있음

# 군집 결과 확인
km_result$cluster
##   [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
##  [75] 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 3
## [112] 3 3 2 2 3 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 3 3 3 3 2 3 3 3 2 3 3 3 2 3
## [149] 3 2
iris %>% count(Species)
##      Species  n
## 1     setosa 50
## 2 versicolor 50
## 3  virginica 50
# 이 코드를 실행하면:
# df 데이터셋의 각 관측치(행)가
# 군집 번호 1, 2, 3 중 어디에 속하는지
# 그 결과가 벡터 형태로 출력됩니다.
# 실루엣 계수 계산 및 시각화

# 실루엣 계산을 위해 군집 결과를 기반으로 silhouette() 함수 사용
sil <- silhouette(km_result$cluster, dist(df))

# 실루엣 결과 시각화
fviz_silhouette(sil)
##   cluster size ave.sil.width
## 1       1   50          0.80
## 2       2   62          0.42
## 3       3   38          0.45

# 평균 실루엣 계수 (Average silhouette width): 0.55
# 0.5 이상 → 꽤 잘 분리된 군집
# 색상 (빨강, 초록, 파랑) : 군집 번호 (Cluster 1, 2, 3)
# 붉은 점선: 전체 데이터의 평균 실루엣 계수 (0.55) 기준선
# Cluster 3 (파랑)
# 파란색 군집은 setosa로 의사결정나무에서도 쉽게 구분되는 품종이었음
# 대부분 실루엣 계수가 0.6~0.8 이상
# ▶ 가장 응집력 좋고, 잘 분리된 군집으로 판단
# 결론

# 군집 수(K=3)는 꽤 타당하지만, 더 나은 K가 있을 수도 있음
# k=2일때 실루엣 계수를 계산하시오?
# K-평균 군집 수행 (예: K=3)
set.seed(123)
km_result1 <- kmeans(df, centers = 2, nstart = 25)
sil <- silhouette(km_result1$cluster, dist(df))

# 실루엣 결과 시각화
fviz_silhouette(sil)
##   cluster size ave.sil.width
## 1       1   53          0.77
## 2       2   97          0.63

# 결론 -> 군집 수 K는 2개로 하는게 3개로 하는 것보다 더 좋을 수 있다.

# 실습
# datarium 패키지에포함된 예제 데이터

# 변수명          설명
# Youtube         Youtube 광고비(단위 : 1000달러)
# Facebook        Facebook 광고비(단위 : 1000달러)
# Newspaper       Newspaper 광고비(단위 : 1000달러)
# Sales           해당 캠페인의 결과 매출액 (단위: 1000달러)
# 군집 결과가 의미하는 것 (예시)
# Cluster1 : YouTube 비중이 높고, Facebook/신문은 적음  매출(Sales) 평균높음
# -> YouTube 중심 전략이 매우 효과적
# Cluster2 : Facebook 위주, YouTube/신문은 적음   매출(Sales) 낮음  
# -> SNS 광고 단독 전략은 효과 미흡
#  Cluster3 : 신문광고 비중이 높고 YouTube/Facebook 적음  매출액 중간 
# -> 오래된 방식의 전략, 비용 대비 효과 낮음
# marketing 데이터의 군집 분석 결과는 광고 채널 사용 패턴에 따라 
# 마케팅 캠페인을 분류하고,
# 각 그룹의 성과를 비교함으로써, 향후 더 효율적인 마케팅 
# 전략을 수립하는 근거를 제공

# 패키지 설치 (최초 1회만)
# install.packages("datarium")
# install.packages("factoextra")

# 패키지 로드
library(datarium)
library(factoextra)

# 데이터 불러오기
data("marketing")
df <- marketing
library(dplyr)
glimpse(df)
## Rows: 200
## Columns: 4
## $ youtube   <dbl> 276.12, 53.40, 20.64, 181.80, 216.96, 10.44, 69.00, 144.24, …
## $ facebook  <dbl> 45.36, 47.16, 55.08, 49.56, 12.96, 58.68, 39.36, 23.52, 2.52…
## $ newspaper <dbl> 83.04, 54.12, 83.16, 70.20, 70.08, 90.00, 28.20, 13.92, 1.20…
## $ sales     <dbl> 26.52, 12.48, 11.16, 22.20, 15.48, 8.64, 14.16, 15.84, 5.76,…
# 광고비만 선택 (Sales는 제외)
df_cluster <- df[,c("youtube", "facebook", "newspaper")]

# 정규화 (평균 0, 표준편차 1)
df_scaled <- scale(df_cluster)
set.seed(123)  # 재현 가능성 확보
km_result <- kmeans(df_scaled, centers = 3, nstart = 25)

# 군집 결과 확인
km_result$cluster  # 각 관측치의 군집 번호
##   [1] 1 1 1 1 1 1 3 3 3 2 3 2 1 3 1 1 1 1 3 2 1 2 3 2 3 2 2 2 2 3 2 3 3 2 3 2 2
##  [38] 1 3 1 2 1 2 2 3 2 3 2 2 3 2 3 1 1 2 1 3 3 1 2 3 1 2 3 1 3 3 3 2 1 1 3 3 3
##  [75] 2 1 3 3 3 3 3 2 3 1 1 1 3 1 1 1 3 3 1 1 3 1 2 2 1 1 2 1 2 2 2 1 3 3 3 2 2
## [112] 2 2 2 1 1 3 3 1 3 1 3 2 3 1 3 1 3 2 3 3 2 3 1 1 3 3 1 3 2 3 1 1 3 3 3 2 1
## [149] 3 3 2 3 2 1 2 3 1 3 1 3 2 1 2 2 3 1 3 2 1 2 3 1 3 2 2 1 2 2 2 2 2 2 3 1 2
## [186] 1 3 2 2 3 3 3 3 2 2 3 3 2 1 2
fviz_cluster(km_result, data = df_scaled,
             palette = "jco", 
             ggtheme = theme_minimal(),
             main = "K=3 광고비 기준 K-Means 군집 결과")

# 6. 원본 데이터에 군집 번호 추가
df$Cluster <- as.factor(km_result$cluster)

# 7. dplyr로 군집별 평균 광고비 + 매출 요약
df %>%
  group_by(Cluster) %>%
  summarise(across(c(youtube, facebook, newspaper, sales),
                   mean, .names = "avg_{.col}")) %>%
  arrange(Cluster)
## # A tibble: 3 × 5
##   Cluster avg_youtube avg_facebook avg_newspaper avg_sales
##   <fct>         <dbl>        <dbl>         <dbl>     <dbl>
## 1 1             198.          45.2          66.3      21.3
## 2 2             264.          22.1          22.4      18.9
## 3 3              83.4         19.1          25.3      11.5
# 실무적 해석
# Cluster 1: YouTube 광고 중심 → 매출도 가장 높음
# Cluster 2: Facebook + Newspaper 혼합 → 중간 매출
# Cluster 3: 신문 위주 전통형 광고 → 가장 낮은 매출

# 실루엣 계수 
# 1. 거리 행렬 계산
dist_matrix <- dist(df_scaled)

# 2. 실루엣 객체 계산
library(cluster)
sil <- silhouette(km_result$cluster, dist_matrix)

# 3. 실루엣 시각화
library(factoextra)
fviz_silhouette(sil)
##   cluster size ave.sil.width
## 1       1   60          0.26
## 2       2   65          0.31
## 3       3   75          0.33

# 4. 평균 실루엣 계수 출력
mean(sil[, 3])  # 세 번째 열이 실루엣 계수 Si
## [1] 0.3050502
# 엘보우 기법
library(factoextra)

# 엘보우 기법으로 최적 군집 수 찾기
fviz_nbclust(df_scaled, kmeans, method = "wss") + 
  geom_vline(xintercept = 3, linetype = 2, color = "red") +
  labs(title = "엘보우 기업을 통한 최적 군지비 수 찾기(WSS 기준)",
       x = "군지비 수 K", y = "군집 내 제곱합(WSS)")

# 혼합분포군집
# 1. 패키지 로드 및 데이터 준비
# install.packages("mclust")
library(mclust)
## Package 'mclust' version 6.1.1
## Type 'citation("mclust")' for citing this R package in publications.
data("faithful")
glimpse(faithful)
## Rows: 272
## Columns: 2
## $ eruptions <dbl> 3.600, 1.800, 3.333, 2.283, 4.533, 2.883, 4.700, 3.600, 1.95…
## $ waiting   <dbl> 79, 54, 74, 62, 85, 55, 88, 85, 51, 85, 54, 84, 78, 47, 83, …
# eruptions 분출 시간 (분 단위) – 
# Old Faithful 간헐천이 한 번 분출하는 데 걸리는 시간
# waiting 다음 분출까지의 대기 시간 (분 단위) – 
# 분출이 끝난 뒤 다음 분출까지 기다리는 시간

# 데이터 로드
data(faithful)

# 산점도 시각화
plot(faithful,
     main = "Old Faithful 간헐천 분출 유형 시각화",
     xlab = "분출 시간 (minutes)",
     ylab = "대기 시간 (minutes)",
     pch = 19,
     col = "steelblue")

# 좌하단: 짧은 분출, 짧은 대기
# 우상단: 긴 분출, 긴 대기
# 두 개의 뚜렷한 패턴(=혼합 분포 구조)이 시각적으로 확인
# -> 혼합분포 군집
# 의미 간헐천 활동이 약한 경우-> 다음도 짧을 가능성 높음
# 예측 긴 분출 후 → 다음도 긴 대기 예상
# ➡ 운영·예측·관광 안내 등 실용적 응용 가능

# 2. 혼합모형 군집(Gaussian Mixture Model) 수행
model <- Mclust(faithful)
# 혼합분포군집(Mixture Model Clustering)을 실행하는 핵심 명령어

# 3. 모델 요약
summary(model)
## ---------------------------------------------------- 
## Gaussian finite mixture model fitted by EM algorithm 
## ---------------------------------------------------- 
## 
## Mclust EEE (ellipsoidal, equal volume, shape and orientation) model with 3
## components: 
## 
##  log-likelihood   n df       BIC       ICL
##       -1126.326 272 11 -2314.316 -2357.824
## 
## Clustering table:
##   1   2   3 
##  40  97 135
# mclust EEE (ellipsoidal, equal volume, shape and orientation) model with 3 components:
# 3개의 군집이 모두 : 타원형 분포
# 같은 크기(volume), 같은 모양(shape), 같은 방향(orientation)
# → 군집이 비슷한 구조로 형성되었음을 의미
# 실용적 해석 예시 (가능한 가설)

# 군집 1: 짧은 분출 + 짧은 대기 (아주 빠른 작동)
# 군집 2: 중간 분출 + 대기 (중간 유형)
# 군집 3: 긴 분출 + 긴 대기 (일반적인 작동 패턴)
# (→ faithful은 사실 2개 혼합구조로 알려졌지만, 3개로도 충분히 유의미한 구분 가능)
# 로그 가능도는 클수록(=덜 음수일수록) 좋습니다.
# 로그 가능도 최대 도달: EM 알고리즘이 더 이상 개선 불필요하다고 판단 → 수렴 완료

# 4. 군집 결과 시각화
plot(model, what = "classification")  # 각 관측치를 군집별 색상으로