You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/ko/5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb

637 lines
30 KiB

{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"anaconda-cloud": "",
"kernelspec": {
"display_name": "R",
"language": "R",
"name": "ir"
},
"language_info": {
"codemirror_mode": "r",
"file_extension": ".r",
"mimetype": "text/x-r-source",
"name": "R",
"pygments_lexer": "r",
"version": "3.4.1"
},
"colab": {
"name": "lesson_14.ipynb",
"provenance": [],
"collapsed_sections": [],
"toc_visible": true
},
"coopTranslator": {
"original_hash": "ad65fb4aad0a156b42216e4929f490fc",
"translation_date": "2025-09-04T02:16:38+00:00",
"source_file": "5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb",
"language_code": "ko"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "GULATlQXLXyR"
},
"source": [
"## R과 Tidy 데이터 원칙을 활용한 K-Means 클러스터링 탐구\n",
"\n",
"### [**강의 전 퀴즈**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)\n",
"\n",
"이 강의에서는 Tidymodels 패키지와 R 생태계의 다른 패키지들(우리는 이들을 친구 🧑‍🤝‍🧑라고 부를 거예요), 그리고 이전에 가져온 나이지리아 음악 데이터셋을 사용하여 클러스터를 생성하는 방법을 배웁니다. 우리는 클러스터링을 위한 K-Means의 기본 개념을 다룰 것입니다. 이전 강의에서 배운 것처럼, 클러스터를 다루는 방법은 여러 가지가 있으며, 사용하는 방법은 데이터에 따라 달라집니다. 우리는 가장 일반적인 클러스터링 기법인 K-Means를 시도해볼 것입니다. 시작해봅시다!\n",
"\n",
"이 강의에서 배우게 될 용어:\n",
"\n",
"- 실루엣 점수(Silhouette scoring)\n",
"\n",
"- 엘보우 방법(Elbow method)\n",
"\n",
"- 관성(Inertia)\n",
"\n",
"- 분산(Variance)\n",
"\n",
"### **소개**\n",
"\n",
"[K-Means 클러스터링](https://wikipedia.org/wiki/K-means_clustering)은 신호 처리 분야에서 유래한 방법입니다. 이 방법은 데이터의 특징 간 유사성을 기반으로 `k개의 클러스터`로 데이터를 나누고 분할하는 데 사용됩니다.\n",
"\n",
"클러스터는 [보로노이 다이어그램](https://wikipedia.org/wiki/Voronoi_diagram)으로 시각화할 수 있으며, 여기에는 점(또는 '시드')과 해당 영역이 포함됩니다.\n",
"\n",
"<p >\n",
" <img src=\"../../images/voronoi.png\"\n",
" width=\"500\"/>\n",
" <figcaption>Jen Looper의 인포그래픽</figcaption>\n",
"\n",
"K-Means 클러스터링의 단계는 다음과 같습니다:\n",
"\n",
"1. 데이터 과학자는 생성할 클러스터의 원하는 개수를 지정합니다.\n",
"\n",
"2. 알고리즘은 데이터셋에서 K개의 관측값을 무작위로 선택하여 클러스터의 초기 중심(즉, 중심점)으로 사용합니다.\n",
"\n",
"3. 나머지 관측값 각각을 가장 가까운 중심점에 할당합니다.\n",
"\n",
"4. 각 클러스터의 새로운 평균을 계산하고 중심점을 평균 위치로 이동시킵니다.\n",
"\n",
"5. 중심점이 재계산되었으므로, 모든 관측값이 다른 클러스터에 더 가까운지 다시 확인합니다. 모든 객체는 업데이트된 클러스터 평균을 사용하여 다시 할당됩니다. 클러스터 할당 및 중심점 업데이트 단계는 클러스터 할당이 더 이상 변경되지 않을 때까지(즉, 수렴이 이루어질 때까지) 반복됩니다. 일반적으로 알고리즘은 각 새로운 반복에서 중심점의 이동이 미미해지고 클러스터가 고정될 때 종료됩니다.\n",
"\n",
"<div>\n",
"\n",
"> 초기 중심점으로 사용되는 k개의 관측값이 무작위로 선택되기 때문에, 절차를 적용할 때마다 약간 다른 결과를 얻을 수 있습니다. 이러한 이유로 대부분의 알고리즘은 여러 번의 *무작위 시작(random starts)*을 사용하고, 가장 낮은 WCSS(클러스터 내 제곱합)를 가진 반복을 선택합니다. 따라서 *nstart* 값을 여러 번 설정하여 K-Means를 실행하는 것이 권장되며, 이는 *원치 않는 지역 최적화(local optimum)*를 피하는 데 도움이 됩니다.\n",
"\n",
"</div>\n",
"\n",
"Allison Horst의 [작품](https://github.com/allisonhorst/stats-illustrations)을 활용한 이 짧은 애니메이션은 클러스터링 과정을 설명합니다:\n",
"\n",
"<p >\n",
" <img src=\"../../images/kmeans.gif\"\n",
" width=\"550\"/>\n",
" <figcaption>@allison_horst의 작품</figcaption>\n",
"\n",
"클러스터링에서 근본적으로 제기되는 질문은 다음과 같습니다: 데이터를 몇 개의 클러스터로 나누어야 할까요? K-Means의 단점 중 하나는 `k`, 즉 `중심점`의 개수를 설정해야 한다는 점입니다. 다행히도 `엘보우 방법`은 `k`의 적절한 시작 값을 추정하는 데 도움을 줍니다. 곧 이를 시도해볼 것입니다.\n",
"\n",
"### \n",
"\n",
"**선행 조건**\n",
"\n",
"[이전 강의](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb)에서 멈췄던 지점부터 바로 시작하겠습니다. 이전 강의에서는 데이터셋을 분석하고, 다양한 시각화를 수행하며, 관심 있는 관측값으로 데이터를 필터링했습니다. 꼭 확인해보세요!\n",
"\n",
"이 모듈을 진행하기 위해 몇 가지 패키지가 필요합니다. 다음 명령어로 설치할 수 있습니다: `install.packages(c('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork'))`\n",
"\n",
"또는 아래 스크립트를 사용하면 필요한 패키지가 설치되어 있는지 확인하고, 누락된 경우 자동으로 설치합니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "ah_tBi58LXyi"
},
"source": [
"suppressWarnings(if(!require(\"pacman\")) install.packages(\"pacman\"))\n",
"\n",
"pacman::p_load('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork')\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "7e--UCUTLXym"
},
"source": [
"자, 바로 시작해봅시다!\n",
"\n",
"## 1. 데이터와의 춤: 가장 인기 있는 음악 장르 3가지를 좁혀보기\n",
"\n",
"이것은 이전 강의에서 했던 내용을 복습하는 시간입니다. 데이터를 잘라보고 분석해봅시다!\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "Ycamx7GGLXyn"
},
"source": [
"# Load the core tidyverse and make it available in your current R session\n",
"library(tidyverse)\n",
"\n",
"# Import the data into a tibble\n",
"df <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/5-Clustering/data/nigerian-songs.csv\", show_col_types = FALSE)\n",
"\n",
"# Narrow down to top 3 popular genres\n",
"nigerian_songs <- df %>% \n",
" # Concentrate on top 3 genres\n",
" filter(artist_top_genre %in% c(\"afro dancehall\", \"afropop\",\"nigerian pop\")) %>% \n",
" # Remove unclassified observations\n",
" filter(popularity != 0)\n",
"\n",
"\n",
"\n",
"# Visualize popular genres using bar plots\n",
"theme_set(theme_light())\n",
"nigerian_songs %>%\n",
" count(artist_top_genre) %>%\n",
" ggplot(mapping = aes(x = artist_top_genre, y = n,\n",
" fill = artist_top_genre)) +\n",
" geom_col(alpha = 0.8) +\n",
" paletteer::scale_fill_paletteer_d(\"ggsci::category10_d3\") +\n",
" ggtitle(\"Top genres\") +\n",
" theme(plot.title = element_text(hjust = 0.5))\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "b5h5zmkPLXyp"
},
"source": [
"🤩 잘 진행되고 있어요!\n",
"\n",
"## 2. 데이터 탐색 더 알아보기\n",
"\n",
"이 데이터는 얼마나 깨끗할까요? 박스 플롯을 사용해 이상치를 확인해 봅시다. 이상치가 적은 숫자형 열에 집중할 예정입니다 (물론, 이상치를 제거할 수도 있습니다). 박스 플롯은 데이터의 범위를 보여주며, 어떤 열을 사용할지 선택하는 데 도움을 줍니다. 참고로, 박스 플롯은 분산을 보여주지 않습니다. 분산은 클러스터링 가능한 좋은 데이터의 중요한 요소입니다. 더 자세한 내용은 [이 논의](https://stats.stackexchange.com/questions/91536/deduce-variance-from-boxplot)를 참고하세요.\n",
"\n",
"[박스 플롯](https://en.wikipedia.org/wiki/Box_plot)은 `숫자형` 데이터의 분포를 시각적으로 나타내는 데 사용됩니다. 그러니 먼저 인기 있는 음악 장르와 함께 모든 숫자형 열을 *선택*하는 것부터 시작해 봅시다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "HhNreJKLLXyq"
},
"source": [
"# Select top genre column and all other numeric columns\n",
"df_numeric <- nigerian_songs %>% \n",
" select(artist_top_genre, where(is.numeric)) \n",
"\n",
"# Display the data\n",
"df_numeric %>% \n",
" slice_head(n = 5)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "uYXrwJRaLXyq"
},
"source": [
"선택 도우미 `where`가 얼마나 쉽게 만들어주는지 보이시죠 💁? 이런 다른 함수들도 [여기](https://tidyselect.r-lib.org/)에서 확인해보세요.\n",
"\n",
"이제 각 숫자형 특징에 대해 박스플롯을 만들 예정인데, 반복문 사용을 피하고 싶다면 데이터를 *더 긴* 형식으로 재구성해야 합니다. 이렇게 하면 `facets`를 활용할 수 있는데, 이는 데이터의 각 하위 집합을 별도의 서브플롯으로 표시할 수 있게 해줍니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "gd5bR3f8LXys"
},
"source": [
"# Pivot data from wide to long\n",
"df_numeric_long <- df_numeric %>% \n",
" pivot_longer(!artist_top_genre, names_to = \"feature_names\", values_to = \"values\") \n",
"\n",
"# Print out data\n",
"df_numeric_long %>% \n",
" slice_head(n = 15)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "-7tE1swnLXyv"
},
"source": [
"훨씬 더 길어졌네요! 이제 `ggplots`를 사용할 시간입니다! 그렇다면 어떤 `geom`을 사용할까요?\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "r88bIsyuLXyy"
},
"source": [
"# Make a box plot\n",
"df_numeric_long %>% \n",
" ggplot(mapping = aes(x = feature_names, y = values, fill = feature_names)) +\n",
" geom_boxplot() +\n",
" facet_wrap(~ feature_names, ncol = 4, scales = \"free\") +\n",
" theme(legend.position = \"none\")\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "EYVyKIUELXyz"
},
"source": [
"이제 이 데이터가 약간의 노이즈를 포함하고 있다는 것을 알 수 있습니다. 각 열을 박스플롯으로 관찰해 보면 이상치가 보입니다. 데이터셋을 살펴보고 이러한 이상치를 제거할 수도 있지만, 그렇게 하면 데이터가 너무 축소될 수 있습니다.\n",
"\n",
"우선, 클러스터링 연습에 사용할 열을 선택해 봅시다. 비슷한 범위를 가진 숫자형 열을 선택해 보겠습니다. `artist_top_genre`를 숫자로 인코딩할 수도 있지만, 지금은 이를 제외하겠습니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "-wkpINyZLXy0"
},
"source": [
"# Select variables with similar ranges\n",
"df_numeric_select <- df_numeric %>% \n",
" select(popularity, danceability, acousticness, loudness, energy) \n",
"\n",
"# Normalize data\n",
"# df_numeric_select <- scale(df_numeric_select)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "D7dLzgpqLXy1"
},
"source": [
"## 3. R에서 k-평균 클러스터링 계산하기\n",
"\n",
"R에서는 내장된 `kmeans` 함수를 사용하여 k-평균 클러스터링을 계산할 수 있습니다. 자세한 내용은 `help(\"kmeans()\")`를 참조하세요. `kmeans()` 함수는 모든 열이 숫자로 구성된 데이터 프레임을 주요 인수로 받습니다.\n",
"\n",
"k-평균 클러스터링을 사용할 때 첫 번째 단계는 최종 결과에서 생성될 클러스터(군집)의 수(k)를 지정하는 것입니다. 데이터셋에서 추출한 3개의 노래 장르가 있다는 것을 알고 있으니, 3을 시도해 봅시다:\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "uC4EQ5w7LXy5"
},
"source": [
"set.seed(2056)\n",
"# Kmeans clustering for 3 clusters\n",
"kclust <- kmeans(\n",
" df_numeric_select,\n",
" # Specify the number of clusters\n",
" centers = 3,\n",
" # How many random initial configurations\n",
" nstart = 25\n",
")\n",
"\n",
"# Display clustering object\n",
"kclust\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "hzfhscWrLXy-"
},
"source": [
"kmeans 객체는 `help(\"kmeans()\")`에서 잘 설명된 여러 정보를 포함하고 있습니다. 지금은 몇 가지에만 집중해 봅시다. 데이터를 65, 110, 111 크기의 3개의 클러스터로 그룹화한 것을 확인할 수 있습니다. 출력에는 또한 5개의 변수에 걸친 3개 그룹의 클러스터 중심(평균)도 포함되어 있습니다.\n",
"\n",
"클러스터링 벡터는 각 관측값에 대한 클러스터 할당을 나타냅니다. `augment` 함수를 사용하여 원본 데이터 세트에 클러스터 할당을 추가해 봅시다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "0XwwpFGQLXy_"
},
"source": [
"# Add predicted cluster assignment to data set\n",
"augment(kclust, df_numeric_select) %>% \n",
" relocate(.cluster) %>% \n",
" slice_head(n = 10)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "NXIVXXACLXzA"
},
"source": [
"완벽합니다! 이제 데이터 세트를 3개의 그룹으로 나누었습니다. 그렇다면, 우리의 클러스터링은 얼마나 잘 되었을까요 🤷? `Silhouette score`를 살펴봅시다.\n",
"\n",
"### **Silhouette score**\n",
"\n",
"[Silhouette 분석](https://en.wikipedia.org/wiki/Silhouette_(clustering))은 생성된 클러스터들 간의 분리 거리를 연구하는 데 사용할 수 있습니다. 이 점수는 -1에서 1 사이의 값을 가지며, 점수가 1에 가까울수록 클러스터가 밀집되어 있고 다른 클러스터와 잘 분리되어 있음을 나타냅니다. 0에 가까운 값은 샘플들이 이웃 클러스터의 경계 근처에 매우 가까이 위치하여 클러스터 간에 겹침이 있음을 나타냅니다. [출처](https://dzone.com/articles/kmeans-silhouette-score-explained-with-python-exam).\n",
"\n",
"평균 Silhouette 방법은 *k*의 다양한 값에 대해 관측치의 평균 Silhouette을 계산합니다. 높은 평균 Silhouette 점수는 좋은 클러스터링을 나타냅니다.\n",
"\n",
"클러스터 패키지의 `silhouette` 함수를 사용하여 평균 Silhouette 폭을 계산할 수 있습니다.\n",
"\n",
"> Silhouette은 [유클리드 거리](https://en.wikipedia.org/wiki/Euclidean_distance \"Euclidean distance\")나 [맨해튼 거리](https://en.wikipedia.org/wiki/Manhattan_distance \"Manhattan distance\")와 같은 [거리](https://en.wikipedia.org/wiki/Distance \"Distance\") 메트릭을 사용하여 계산할 수 있습니다. 이는 [이전 강의](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb)에서 논의한 바 있습니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "Jn0McL28LXzB"
},
"source": [
"# Load cluster package\n",
"library(cluster)\n",
"\n",
"# Compute average silhouette score\n",
"ss <- silhouette(kclust$cluster,\n",
" # Compute euclidean distance\n",
" dist = dist(df_numeric_select))\n",
"mean(ss[, 3])\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "QyQRn97nLXzC"
},
"source": [
"우리의 점수는 **0.549**로, 딱 중간 정도입니다. 이는 우리의 데이터가 이 유형의 클러스터링에 특히 적합하지 않다는 것을 나타냅니다. 이 추측을 시각적으로 확인할 수 있는지 살펴봅시다. [factoextra 패키지](https://rpkgs.datanovia.com/factoextra/index.html)는 클러스터링을 시각화할 수 있는 함수(`fviz_cluster()`)를 제공합니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "7a6Km1_FLXzD"
},
"source": [
"library(factoextra)\n",
"\n",
"# Visualize clustering results\n",
"fviz_cluster(kclust, df_numeric_select)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "IBwCWt-0LXzD"
},
"source": [
"클러스터 간의 겹침은 우리의 데이터가 이 유형의 클러스터링에 특히 적합하지 않음을 나타내지만, 계속 진행해 봅시다.\n",
"\n",
"## 4. 최적 클러스터 수 결정하기\n",
"\n",
"K-Means 클러스터링에서 자주 제기되는 근본적인 질문은 다음과 같습니다. 알려진 클래스 레이블이 없는 상태에서 데이터를 몇 개의 클러스터로 나누어야 할지 어떻게 알 수 있을까요?\n",
"\n",
"이를 알아내는 한 가지 방법은 데이터 샘플을 사용하여 `클러스터 수를 점진적으로 증가시키며` (예: 1-10) 일련의 클러스터링 모델을 생성하고, **Silhouette 점수**와 같은 클러스터링 지표를 평가하는 것입니다.\n",
"\n",
"*클러스터 수*의 다양한 값에 대해 클러스터링 알고리즘을 계산하고 **클러스터 내 제곱합 (WCSS)**을 평가하여 최적의 클러스터 수를 결정해 봅시다. 클러스터 내 제곱합(WCSS)은 클러스터링의 밀집도를 측정하며, 값이 작을수록 데이터 포인트들이 더 가까이 모여 있다는 것을 의미합니다.\n",
"\n",
"이제 `k` 값을 1에서 10까지 다르게 선택했을 때 이 클러스터링에 미치는 영향을 탐구해 봅시다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "hSeIiylDLXzE"
},
"source": [
"# Create a series of clustering models\n",
"kclusts <- tibble(k = 1:10) %>% \n",
" # Perform kmeans clustering for 1,2,3 ... ,10 clusters\n",
" mutate(model = map(k, ~ kmeans(df_numeric_select, centers = .x, nstart = 25)),\n",
" # Farm out clustering metrics eg WCSS\n",
" glanced = map(model, ~ glance(.x))) %>% \n",
" unnest(cols = glanced)\n",
" \n",
"\n",
"# View clustering rsulsts\n",
"kclusts\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "m7rS2U1eLXzE"
},
"source": [
"이제 각 클러스터링 알고리즘에서 중심 *k*에 대한 총 클러스터 내 제곱합 (tot.withinss)을 구했으므로, [엘보우 방법](https://en.wikipedia.org/wiki/Elbow_method_(clustering))을 사용하여 최적의 클러스터 개수를 찾습니다. 이 방법은 클러스터 개수에 따른 WCSS를 그래프로 나타내고, [곡선의 엘보우](https://en.wikipedia.org/wiki/Elbow_of_the_curve \"Elbow of the curve\")를 클러스터 개수로 선택하는 방식으로 이루어집니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "o_DjHGItLXzF"
},
"source": [
"set.seed(2056)\n",
"# Use elbow method to determine optimum number of clusters\n",
"kclusts %>% \n",
" ggplot(mapping = aes(x = k, y = tot.withinss)) +\n",
" geom_line(size = 1.2, alpha = 0.8, color = \"#FF7F0EFF\") +\n",
" geom_point(size = 2, color = \"#FF7F0EFF\")\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "pLYyt5XSLXzG"
},
"source": [
"플롯은 클러스터 수가 하나에서 둘로 증가함에 따라 WCSS가 크게 감소(*더 조밀해짐*)하는 것을 보여줍니다. 그리고 둘에서 셋으로 클러스터가 증가할 때도 눈에 띄는 감소가 나타납니다. 그 이후로는 감소가 덜 두드러지며, 약 세 개의 클러스터에서 차트에 `elbow` 💪가 형성됩니다. 이는 데이터 포인트가 두세 개의 비교적 잘 분리된 클러스터로 나뉘어 있다는 좋은 지표입니다.\n",
"\n",
"이제 `k = 3`인 클러스터링 모델을 추출할 수 있습니다:\n",
"\n",
"> `pull()`: 단일 열을 추출하는 데 사용됩니다\n",
">\n",
"> `pluck()`: 리스트와 같은 데이터 구조를 인덱싱하는 데 사용됩니다\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "JP_JPKBILXzG"
},
"source": [
"# Extract k = 3 clustering\n",
"final_kmeans <- kclusts %>% \n",
" filter(k == 3) %>% \n",
" pull(model) %>% \n",
" pluck(1)\n",
"\n",
"\n",
"final_kmeans\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "l_PDTu8tLXzI"
},
"source": [
"좋아요! 이제 얻어진 클러스터를 시각화해 봅시다. `plotly`를 사용해서 약간의 상호작용을 추가해 보는 건 어떨까요?\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "dNcleFe-LXzJ"
},
"source": [
"# Add predicted cluster assignment to data set\n",
"results <- augment(final_kmeans, df_numeric_select) %>% \n",
" bind_cols(df_numeric %>% select(artist_top_genre)) \n",
"\n",
"# Plot cluster assignments\n",
"clust_plt <- results %>% \n",
" ggplot(mapping = aes(x = popularity, y = danceability, color = .cluster, shape = artist_top_genre)) +\n",
" geom_point(size = 2, alpha = 0.8) +\n",
" paletteer::scale_color_paletteer_d(\"ggthemes::Tableau_10\")\n",
"\n",
"ggplotly(clust_plt)\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "6JUM_51VLXzK"
},
"source": [
"아마도 각 클러스터(다른 색상으로 표시됨)가 각기 다른 장르(다른 모양으로 표시됨)를 가질 것이라고 예상했을 것입니다.\n",
"\n",
"모델의 정확도를 살펴보겠습니다.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "HdIMUGq7LXzL"
},
"source": [
"# Assign genres to predefined integers\n",
"label_count <- results %>% \n",
" group_by(artist_top_genre) %>% \n",
" mutate(id = cur_group_id()) %>% \n",
" ungroup() %>% \n",
" summarise(correct_labels = sum(.cluster == id))\n",
"\n",
"\n",
"# Print results \n",
"cat(\"Result:\", label_count$correct_labels, \"out of\", nrow(results), \"samples were correctly labeled.\")\n",
"\n",
"cat(\"\\nAccuracy score:\", label_count$correct_labels/nrow(results))\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "C50wvaAOLXzM"
},
"source": [
"이 모델의 정확도는 나쁘지 않지만, 뛰어나지도 않습니다. 데이터가 K-Means 클러스터링에 적합하지 않을 가능성이 있습니다. 이 데이터는 너무 불균형적이고, 상관관계가 적으며, 열 값 간의 분산이 너무 커서 클러스터링이 잘 이루어지지 않습니다. 사실, 형성된 클러스터는 우리가 위에서 정의한 세 가지 장르 카테고리에 의해 크게 영향을 받거나 왜곡될 가능성이 높습니다.\n",
"\n",
"그럼에도 불구하고, 꽤나 배울 점이 많은 과정이었습니다!\n",
"\n",
"Scikit-learn의 문서를 보면, 이와 같은 클러스터가 명확히 구분되지 않는 모델은 '분산' 문제를 가지고 있다는 것을 알 수 있습니다:\n",
"\n",
"<p >\n",
" <img src=\"../../images/problems.png\"\n",
" width=\"500\"/>\n",
" <figcaption>Scikit-learn에서 제공하는 인포그래픽</figcaption>\n",
"\n",
"\n",
"\n",
"## **분산**\n",
"\n",
"분산은 \"평균으로부터의 제곱 차이의 평균\"으로 정의됩니다 [출처](https://www.mathsisfun.com/data/standard-deviation.html). 이 클러스터링 문제의 맥락에서, 이는 데이터셋의 숫자가 평균에서 너무 많이 벗어나는 경향을 나타냅니다.\n",
"\n",
"✅ 이 문제를 해결할 방법을 생각해볼 좋은 시점입니다. 데이터를 조금 더 조정해볼까요? 다른 열을 사용해볼까요? 다른 알고리즘을 사용해볼까요? 힌트: 데이터를 [스케일링](https://www.mygreatlearning.com/blog/learning-data-science-with-k-means-clustering/)하여 정규화하고 다른 열을 테스트해보세요.\n",
"\n",
"> '[분산 계산기](https://www.calculatorsoup.com/calculators/statistics/variance-calculator.php)'를 사용해 개념을 조금 더 이해해보세요.\n",
"\n",
"------------------------------------------------------------------------\n",
"\n",
"## **🚀도전 과제**\n",
"\n",
"이 노트북을 활용하여 매개변수를 조정하는 데 시간을 투자해보세요. 데이터를 더 깨끗하게 정리함으로써(예: 이상값 제거) 모델의 정확도를 개선할 수 있나요? 특정 데이터 샘플에 더 많은 가중치를 부여할 수도 있습니다. 더 나은 클러스터를 만들기 위해 무엇을 할 수 있을까요?\n",
"\n",
"힌트: 데이터를 스케일링해보세요. 노트북에는 데이터 열이 범위 면에서 서로 더 비슷하게 보이도록 표준 스케일링을 추가하는 주석 처리된 코드가 있습니다. 실루엣 점수가 낮아지긴 하지만, 엘보 그래프의 '꺾임'이 부드러워집니다. 이는 데이터를 스케일링하지 않으면 분산이 적은 데이터가 더 많은 영향을 미치게 되기 때문입니다. 이 문제에 대해 더 읽어보세요 [여기](https://stats.stackexchange.com/questions/21222/are-mean-normalization-and-feature-scaling-needed-for-k-means-clustering/21226#21226).\n",
"\n",
"## [**강의 후 퀴즈**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/30/)\n",
"\n",
"## **복습 및 자기 학습**\n",
"\n",
"- K-Means 시뮬레이터 [예: 이 도구](https://user.ceng.metu.edu.tr/~akifakkus/courses/ceng574/k-means/)를 살펴보세요. 이 도구를 사용하여 샘플 데이터 포인트를 시각화하고 중심점을 결정할 수 있습니다. 데이터의 무작위성, 클러스터 수, 중심점 수를 편집할 수 있습니다. 데이터가 어떻게 그룹화될 수 있는지에 대한 아이디어를 얻는 데 도움이 되나요?\n",
"\n",
"- 또한 Stanford에서 제공하는 [K-Means 핸드아웃](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html)을 살펴보세요.\n",
"\n",
"새로 습득한 클러스터링 기술을 K-Means 클러스터링에 적합한 데이터셋에 적용해보고 싶으신가요? 다음을 확인해보세요:\n",
"\n",
"- [클러스터링 모델 학습 및 평가](https://rpubs.com/eR_ic/clustering) (Tidymodels 및 관련 도구 사용)\n",
"\n",
"- [K-Means 클러스터 분석](https://uc-r.github.io/kmeans_clustering), UC 비즈니스 분석 R 프로그래밍 가이드\n",
"\n",
"- [정돈된 데이터 원칙을 활용한 K-Means 클러스터링](https://www.tidymodels.org/learn/statistics/k-means/)\n",
"\n",
"## **과제**\n",
"\n",
"[다양한 클러스터링 방법 시도하기](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/2-K-Means/assignment.md)\n",
"\n",
"## 감사의 말씀:\n",
"\n",
"[Jen Looper](https://www.twitter.com/jenlooper) - 이 모듈의 원래 Python 버전을 만들어주셔서 ♥️\n",
"\n",
"[`Allison Horst`](https://twitter.com/allison_horst/) - R을 더 친근하고 매력적으로 만들어주는 멋진 삽화를 제작해주셔서 감사합니다. 그녀의 삽화를 [갤러리](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM)에서 더 찾아보세요.\n",
"\n",
"즐거운 학습 되세요,\n",
"\n",
"[Eric](https://twitter.com/ericntay), Gold Microsoft Learn Student Ambassador.\n",
"\n",
"<p >\n",
" <img src=\"../../images/r_learners_sm.jpeg\"\n",
" width=\"500\"/>\n",
" <figcaption>@allison_horst의 작품</figcaption>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**면책 조항**: \n이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다. \n"
]
}
]
}