Register
Login
Resources
Docs Blog Datasets Glossary Case Studies Tutorials & Webinars
Product
Data Engine LLMs Platform Enterprise
Pricing Explore
Connect to our Discord channel

lesson_15.Rmd 19 KB

You have to be logged in to leave a comment. Sign In
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  1. ---
  2. title: 'K-Means Clustering using Tidymodels and friends'
  3. output:
  4. html_document:
  5. #css: style_7.css
  6. df_print: paged
  7. theme: flatly
  8. highlight: breezedark
  9. toc: yes
  10. toc_float: yes
  11. code_download: yes
  12. ---
  13. ## Explore K-Means clustering using R and Tidy data principles.
  14. ### [**Pre-lecture quiz**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)
  15. In this lesson, you will learn how to create clusters using the Tidymodels package and other packages in the R ecosystem (we'll call them friends 🧑‍🤝‍🧑), and the Nigerian music dataset you imported earlier. We will cover the basics of K-Means for Clustering. Keep in mind that, as you learned in the earlier lesson, there are many ways to work with clusters and the method you use depends on your data. We will try K-Means as it's the most common clustering technique. Let's get started!
  16. Terms you will learn about:
  17. - Silhouette scoring
  18. - Elbow method
  19. - Inertia
  20. - Variance
  21. ### **Introduction**
  22. [K-Means Clustering](https://wikipedia.org/wiki/K-means_clustering) is a method derived from the domain of signal processing. It is used to divide and partition groups of data into `k clusters` based on similarities in their features.
  23. The clusters can be visualized as [Voronoi diagrams](https://wikipedia.org/wiki/Voronoi_diagram), which include a point (or 'seed') and its corresponding region.
  24. ![Infographic by Jen Looper](../../images/voronoi.png)
  25. K-Means clustering has the following steps:
  26. 1. The data scientist starts by specifying the desired number of clusters to be created.
  27. 2. Next, the algorithm randomly selects K observations from the data set to serve as the initial centers for the clusters (i.e., centroids).
  28. 3. Next, each of the remaining observations is assigned to its closest centroid.
  29. 4. Next, the new means of each cluster is computed and the centroid is moved to the mean.
  30. 5. Now that the centers have been recalculated, every observation is checked again to see if it might be closer to a different cluster. All the objects are reassigned again using the updated cluster means. The cluster assignment and centroid update steps are iteratively repeated until the cluster assignments stop changing (i.e., when convergence is achieved). Typically, the algorithm terminates when each new iteration results in negligible movement of centroids and the clusters become static.
  31. <div>
  32. > Note that due to randomization of the initial k observations used as the starting centroids, we can get slightly different results each time we apply the procedure. For this reason, most algorithms use several *random starts* and choose the iteration with the lowest WCSS. As such, it is strongly recommended to always run K-Means with several values of *nstart* to avoid an *undesirable local optimum.*
  33. </div>
  34. This short animation using the [artwork](https://github.com/allisonhorst/stats-illustrations) of Allison Horst explains the clustering process:
  35. ![Artwork by \@allison_horst](../../images/kmeans.gif)
  36. A fundamental question that arises in clustering is this: how do you know how many clusters to separate your data into? One drawback of using K-Means includes the fact that you will need to establish `k`, that is the number of `centroids`. Fortunately the `elbow method` helps to estimate a good starting value for `k`. You'll try it in a minute.
  37. ###
  38. **Prerequisite**
  39. We'll pick off right from where we stopped in the [previous lesson](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb), where we analysed the data set, made lots of visualizations and filtered the data set to observations of interest. Be sure to check it out!
  40. We'll require some packages to knock-off this module. You can have them installed as: `install.packages(c('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork'))`
  41. Alternatively, the script below checks whether you have the packages required to complete this module and installs them for you in case some are missing.
  42. ```{r}
  43. suppressWarnings(if(!require("pacman")) install.packages("pacman"))
  44. pacman::p_load('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork')
  45. ```
  46. Let's hit the ground running!
  47. ## 1. A dance with data: Narrow down to the 3 most popular music genres
  48. This is a recap of what we did in the previous lesson. Let's slice and dice some data!
  49. ```{r message=F, warning=F}
  50. # Load the core tidyverse and make it available in your current R session
  51. library(tidyverse)
  52. # Import the data into a tibble
  53. df <- read_csv(file = "https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/5-Clustering/data/nigerian-songs.csv", show_col_types = FALSE)
  54. # Narrow down to top 3 popular genres
  55. nigerian_songs <- df %>%
  56. # Concentrate on top 3 genres
  57. filter(artist_top_genre %in% c("afro dancehall", "afropop","nigerian pop")) %>%
  58. # Remove unclassified observations
  59. filter(popularity != 0)
  60. # Visualize popular genres using bar plots
  61. theme_set(theme_light())
  62. nigerian_songs %>%
  63. count(artist_top_genre) %>%
  64. ggplot(mapping = aes(x = artist_top_genre, y = n,
  65. fill = artist_top_genre)) +
  66. geom_col(alpha = 0.8) +
  67. paletteer::scale_fill_paletteer_d("ggsci::category10_d3") +
  68. ggtitle("Top genres") +
  69. theme(plot.title = element_text(hjust = 0.5))
  70. ```
  71. 🤩 That went well!
  72. ## 2. More data exploration.
  73. How clean is this data? Let's check for outliers using box plots. We will concentrate on numeric columns with fewer outliers (although you could clean out the outliers). Boxplots can show the range of the data and will help choose which columns to use. Note, Boxplots do not show variance, an important element of good clusterable data. Please see [this discussion](https://stats.stackexchange.com/questions/91536/deduce-variance-from-boxplot) for further reading.
  74. [Boxplots](https://en.wikipedia.org/wiki/Box_plot) are used to graphically depict the distribution of `numeric` data, so let's start by *selecting* all numeric columns alongside the popular music genres.
  75. ```{r select}
  76. # Select top genre column and all other numeric columns
  77. df_numeric <- nigerian_songs %>%
  78. select(artist_top_genre, where(is.numeric))
  79. # Display the data
  80. df_numeric %>%
  81. slice_head(n = 5)
  82. ```
  83. See how the selection helper `where` makes this easy 💁? Explore such other functions [here](https://tidyselect.r-lib.org/).
  84. Since we'll be making a boxplot for each numeric features and we want to avoid using loops, let's reformat our data into a *longer* format that will allow us to take advantage of `facets` - subplots that each display one subset of the data.
  85. ```{r pivot_longer}
  86. # Pivot data from wide to long
  87. df_numeric_long <- df_numeric %>%
  88. pivot_longer(!artist_top_genre, names_to = "feature_names", values_to = "values")
  89. # Print out data
  90. df_numeric_long %>%
  91. slice_head(n = 15)
  92. ```
  93. Much longer! Now time for some `ggplots`! So what `geom` will we use?
  94. ```{r}
  95. # Make a box plot
  96. df_numeric_long %>%
  97. ggplot(mapping = aes(x = feature_names, y = values, fill = feature_names)) +
  98. geom_boxplot() +
  99. facet_wrap(~ feature_names, ncol = 4, scales = "free") +
  100. theme(legend.position = "none")
  101. ```
  102. Easy-gg!
  103. Now we can see this data is a little noisy: by observing each column as a boxplot, you can see outliers. You could go through the dataset and remove these outliers, but that would make the data pretty minimal.
  104. For now, let's choose which columns we will use for our clustering exercise. Let's pick the numeric columns with similar ranges. We could encode the `artist_top_genre` as numeric but we'll drop it for now.
  105. ```{r select_columns}
  106. # Select variables with similar ranges
  107. df_numeric_select <- df_numeric %>%
  108. select(popularity, danceability, acousticness, loudness, energy)
  109. # Normalize data
  110. # df_numeric_select <- scale(df_numeric_select)
  111. ```
  112. ## 3. Computing k-means clustering in R
  113. We can compute k-means in R with the built-in `kmeans` function, see `help("kmeans()")`. `kmeans()` function accepts a data frame with all numeric columns as it's primary argument.
  114. The first step when using k-means clustering is to specify the number of clusters (k) that will be generated in the final solution. We know there are 3 song genres that we carved out of the dataset, so let's try 3:
  115. ```{r kmeans}
  116. set.seed(2056)
  117. # Kmeans clustering for 3 clusters
  118. kclust <- kmeans(
  119. df_numeric_select,
  120. # Specify the number of clusters
  121. centers = 3,
  122. # How many random initial configurations
  123. nstart = 25
  124. )
  125. # Display clustering object
  126. kclust
  127. ```
  128. The kmeans object contains several bits of information which is well explained in `help("kmeans()")`. For now, let's focus on a few. We see that the data has been grouped into 3 clusters of sizes 65, 110, 111. The output also contains the cluster centers (means) for the 3 groups across the 5 variables.
  129. The clustering vector is the cluster assignment for each observation. Let's use the `augment` function to add the cluster assignment the original data set.
  130. ```{r augment}
  131. # Add predicted cluster assignment to data set
  132. augment(kclust, df_numeric_select) %>%
  133. relocate(.cluster) %>%
  134. slice_head(n = 10)
  135. ```
  136. Perfect, we have just partitioned our data set into a set of 3 groups. So, how good is our clustering 🤷? Let's take a look at the `Silhouette score`
  137. ### **Silhouette score**
  138. [Silhouette analysis](https://en.wikipedia.org/wiki/Silhouette_(clustering)) can be used to study the separation distance between the resulting clusters. This score varies from -1 to 1, and if the score is near 1, the cluster is dense and well-separated from other clusters. A value near 0 represents overlapping clusters with samples very close to the decision boundary of the neighboring clusters. [(Source)](https://dzone.com/articles/kmeans-silhouette-score-explained-with-python-exam).
  139. The average silhouette method computes the average silhouette of observations for different values of *k*. A high average silhouette score indicates a good clustering.
  140. The `silhouette` function in the cluster package to compuate the average silhouette width.
  141. > The silhouette can be calculated with any [distance](https://en.wikipedia.org/wiki/Distance "Distance") metric, such as the [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance "Euclidean distance") or the [Manhattan distance](https://en.wikipedia.org/wiki/Manhattan_distance "Manhattan distance") which we discussed in the [previous lesson](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb).
  142. ```{r}
  143. # Load cluster package
  144. library(cluster)
  145. # Compute average silhouette score
  146. ss <- silhouette(kclust$cluster,
  147. # Compute euclidean distance
  148. dist = dist(df_numeric_select))
  149. mean(ss[, 3])
  150. ```
  151. Our score is **.549**, so right in the middle. This indicates that our data is not particularly well-suited to this type of clustering. Let's see whether we can confirm this hunch visually. The [factoextra package](https://rpkgs.datanovia.com/factoextra/index.html) provides functions (`fviz_cluster()`) to visualize clustering.
  152. ```{r fviz_cluster}
  153. library(factoextra)
  154. # Visualize clustering results
  155. fviz_cluster(kclust, df_numeric_select)
  156. ```
  157. The overlap in clusters indicates that our data is not particularly well-suited to this type of clustering but let's continue.
  158. ## 4. Determining optimal clusters
  159. A fundamental question that often arises in K-Means clustering is this - without known class labels, how do you know how many clusters to separate your data into?
  160. One way we can try to find out is to use a data sample to `create a series of clustering models` with an incrementing number of clusters (e.g from 1-10), and evaluate clustering metrics such as the **Silhouette score.**
  161. Let's determine the optimal number of clusters by computing the clustering algorithm for different values of *k* and evaluating the **Within Cluster Sum of Squares** (WCSS). The total within-cluster sum of square (WCSS) measures the compactness of the clustering and we want it to be as small as possible, with lower values meaning that the data points are closer.
  162. Let's explore the effect of different choices of `k`, from 1 to 10, on this clustering.
  163. ```{r}
  164. # Create a series of clustering models
  165. kclusts <- tibble(k = 1:10) %>%
  166. # Perform kmeans clustering for 1,2,3 ... ,10 clusters
  167. mutate(model = map(k, ~ kmeans(df_numeric_select, centers = .x, nstart = 25)),
  168. # Farm out clustering metrics eg WCSS
  169. glanced = map(model, ~ glance(.x))) %>%
  170. unnest(cols = glanced)
  171. # View clustering rsulsts
  172. kclusts
  173. ```
  174. Now that we have the total within-cluster sum-of-squares (tot.withinss) for each clustering algorithm with center *k*, we use the [elbow method](https://en.wikipedia.org/wiki/Elbow_method_(clustering)) to find the optimal number of clusters. The method consists of plotting the WCSS as a function of the number of clusters, and picking the [elbow of the curve](https://en.wikipedia.org/wiki/Elbow_of_the_curve "Elbow of the curve") as the number of clusters to use.
  175. ```{r elbow_method}
  176. set.seed(2056)
  177. # Use elbow method to determine optimum number of clusters
  178. kclusts %>%
  179. ggplot(mapping = aes(x = k, y = tot.withinss)) +
  180. geom_line(size = 1.2, alpha = 0.8, color = "#FF7F0EFF") +
  181. geom_point(size = 2, color = "#FF7F0EFF")
  182. ```
  183. The plot shows a large reduction in WCSS (so greater *tightness*) as the number of clusters increases from one to two, and a further noticeable reduction from two to three clusters. After that, the reduction is less pronounced, resulting in an `elbow` 💪in the chart at around three clusters. This is a good indication that there are two to three reasonably well separated clusters of data points.
  184. We can now go ahead and extract the clustering model where `k = 3`:
  185. > `pull()`: used to extract a single column
  186. >
  187. > `pluck()`: used to index data structures such as lists
  188. ```{r extract_model}
  189. # Extract k = 3 clustering
  190. final_kmeans <- kclusts %>%
  191. filter(k == 3) %>%
  192. pull(model) %>%
  193. pluck(1)
  194. final_kmeans
  195. ```
  196. Great! Let's go ahead and visualize the clusters obtained. Care for some interactivity using `plotly`?
  197. ```{r viz_clust}
  198. # Add predicted cluster assignment to data set
  199. results <- augment(final_kmeans, df_numeric_select) %>%
  200. bind_cols(df_numeric %>% select(artist_top_genre))
  201. # Plot cluster assignments
  202. clust_plt <- results %>%
  203. ggplot(mapping = aes(x = popularity, y = danceability, color = .cluster, shape = artist_top_genre)) +
  204. geom_point(size = 2, alpha = 0.8) +
  205. paletteer::scale_color_paletteer_d("ggthemes::Tableau_10")
  206. ggplotly(clust_plt)
  207. ```
  208. Perhaps we would have expected that each cluster (represented by different colors) would have distinct genres (represented by different shapes).
  209. Let's take a look at the model's accuracy.
  210. ```{r ordinal_encode}
  211. # Assign genres to predefined integers
  212. label_count <- results %>%
  213. group_by(artist_top_genre) %>%
  214. mutate(id = cur_group_id()) %>%
  215. ungroup() %>%
  216. summarise(correct_labels = sum(.cluster == id))
  217. # Print results
  218. cat("Result:", label_count$correct_labels, "out of", nrow(results), "samples were correctly labeled.")
  219. cat("\nAccuracy score:", label_count$correct_labels/nrow(results))
  220. ```
  221. This model's accuracy is not bad, but not great. It may be that the data may not lend itself well to K-Means Clustering. This data is too imbalanced, too little correlated and there is too much variance between the column values to cluster well. In fact, the clusters that form are probably heavily influenced or skewed by the three genre categories we defined above.
  222. Nevertheless, that was quite a learning process!
  223. In Scikit-learn's documentation, you can see that a model like this one, with clusters not very well demarcated, has a 'variance' problem:
  224. ![Infographic from Scikit-learn](../../images/problems.png)
  225. ## **Variance**
  226. Variance is defined as "the average of the squared differences from the Mean" [(Source)](https://www.mathsisfun.com/data/standard-deviation.html). In the context of this clustering problem, it refers to data that the numbers of our dataset tend to diverge a bit too much from the mean.
  227. ✅ This is a great moment to think about all the ways you could correct this issue. Tweak the data a bit more? Use different columns? Use a different algorithm? Hint: Try [scaling your data](https://www.mygreatlearning.com/blog/learning-data-science-with-k-means-clustering/) to normalize it and test other columns.
  228. > Try this '[variance calculator](https://www.calculatorsoup.com/calculators/statistics/variance-calculator.php)' to understand the concept a bit more.
  229. ------------------------------------------------------------------------
  230. ## **🚀Challenge**
  231. Spend some time with this notebook, tweaking parameters. Can you improve the accuracy of the model by cleaning the data more (removing outliers, for example)? You can use weights to give more weight to given data samples. What else can you do to create better clusters?
  232. Hint: Try to scale your data. There's commented code in the notebook that adds standard scaling to make the data columns resemble each other more closely in terms of range. You'll find that while the silhouette score goes down, the 'kink' in the elbow graph smooths out. This is because leaving the data unscaled allows data with less variance to carry more weight. Read a bit more on this problem [here](https://stats.stackexchange.com/questions/21222/are-mean-normalization-and-feature-scaling-needed-for-k-means-clustering/21226#21226).
  233. ## [**Post-lecture quiz**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/30/)
  234. ## **Review & Self Study**
  235. - Take a look at a K-Means Simulator [such as this one](https://user.ceng.metu.edu.tr/~akifakkus/courses/ceng574/k-means/). You can use this tool to visualize sample data points and determine its centroids. You can edit the data's randomness, numbers of clusters and numbers of centroids. Does this help you get an idea of how the data can be grouped?
  236. - Also, take a look at [this handout on K-Means](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html) from Stanford.
  237. Want to try out your newly acquired clustering skills to data sets that lend well to K-Means clustering? Please see:
  238. - [Train and Evaluate Clustering Models](https://rpubs.com/eR_ic/clustering) using Tidymodels and friends
  239. - [K-means Cluster Analysis](https://uc-r.github.io/kmeans_clustering), UC Business Analytics R Programming Guide
  240. - [K-means clustering with tidy data principles](https://www.tidymodels.org/learn/statistics/k-means/)
  241. ## **Assignment**
  242. [Try different clustering methods](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/2-K-Means/assignment.md)
  243. ## THANK YOU TO:
  244. [Jen Looper](https://www.twitter.com/jenlooper) for creating the original Python version of this module ♥️
  245. [`Allison Horst`](https://twitter.com/allison_horst/) for creating the amazing illustrations that make R more welcoming and engaging. Find more illustrations at her [gallery](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).
  246. Happy Learning,
  247. [Eric](https://twitter.com/ericntay), Gold Microsoft Learn Student Ambassador.
  248. ![Artwork by \@allison_horst](../../images/r_learners_sm.jpeg)
  249. ```{r include=FALSE}
  250. library(here)
  251. library(rmd2jupyter)
  252. rmd2jupyter("lesson_14.Rmd")
  253. ```
Tip!

Press p or to see the previous file or, n or to see the next file

Comments

Loading...