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.
642 lines
27 KiB
642 lines
27 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-04T08:07:57+00:00",
|
|
"source_file": "5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb",
|
|
"language_code": "da"
|
|
}
|
|
},
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "GULATlQXLXyR"
|
|
},
|
|
"source": [
|
|
"## Udforsk K-Means clustering med R og principperne for Tidy data.\n",
|
|
"\n",
|
|
"### [**Quiz før lektionen**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)\n",
|
|
"\n",
|
|
"I denne lektion vil du lære, hvordan man opretter klynger ved hjælp af Tidymodels-pakken og andre pakker i R-økosystemet (vi kalder dem venner 🧑🤝🧑) samt det nigerianske musikdatasæt, du importerede tidligere. Vi vil dække det grundlæggende i K-Means til clustering. Husk, som du lærte i den tidligere lektion, at der er mange måder at arbejde med klynger på, og metoden afhænger af dine data. Vi vil prøve K-Means, da det er den mest almindelige clustering-teknik. Lad os komme i gang!\n",
|
|
"\n",
|
|
"Begreber, du vil lære om:\n",
|
|
"\n",
|
|
"- Silhouettescore\n",
|
|
"\n",
|
|
"- Elbow-metoden\n",
|
|
"\n",
|
|
"- Inerti\n",
|
|
"\n",
|
|
"- Varians\n",
|
|
"\n",
|
|
"### **Introduktion**\n",
|
|
"\n",
|
|
"[K-Means Clustering](https://wikipedia.org/wiki/K-means_clustering) er en metode, der stammer fra signalbehandling. Den bruges til at opdele og gruppere data i `k klynger` baseret på ligheder i deres egenskaber.\n",
|
|
"\n",
|
|
"Klyngerne kan visualiseres som [Voronoi-diagrammer](https://wikipedia.org/wiki/Voronoi_diagram), som inkluderer et punkt (eller 'frø') og dets tilsvarende område.\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/voronoi.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Infografik af Jen Looper</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"K-Means clustering har følgende trin:\n",
|
|
"\n",
|
|
"1. Dataforskeren starter med at specificere det ønskede antal klynger, der skal oprettes.\n",
|
|
"\n",
|
|
"2. Derefter vælger algoritmen tilfældigt K observationer fra datasættet som de oprindelige centre for klyngerne (dvs. centroid).\n",
|
|
"\n",
|
|
"3. Herefter tildeles hver af de resterende observationer til det nærmeste centroid.\n",
|
|
"\n",
|
|
"4. Derefter beregnes de nye gennemsnit for hver klynge, og centroid flyttes til gennemsnittet.\n",
|
|
"\n",
|
|
"5. Når centrene er blevet genberegnet, kontrolleres hver observation igen for at se, om den måske er tættere på en anden klynge. Alle objekter tildeles igen ved hjælp af de opdaterede klyngegennemsnit. Trinnene med klyngetildeling og opdatering af centroid gentages iterativt, indtil klyngetildelingerne stopper med at ændre sig (dvs. når konvergens er opnået). Typisk afsluttes algoritmen, når hver ny iteration resulterer i ubetydelig bevægelse af centroids, og klyngerne bliver statiske.\n",
|
|
"\n",
|
|
"<div>\n",
|
|
"\n",
|
|
"> Bemærk, at på grund af tilfældigheden i valget af de oprindelige k observationer, der bruges som startcentroids, kan vi få lidt forskellige resultater hver gang vi anvender proceduren. Af denne grund bruger de fleste algoritmer flere *tilfældige starter* og vælger iterationen med den laveste WCSS. Derfor anbefales det stærkt altid at køre K-Means med flere værdier af *nstart* for at undgå et *uønsket lokalt optimum.*\n",
|
|
"\n",
|
|
"</div>\n",
|
|
"\n",
|
|
"Denne korte animation, der bruger [illustrationer](https://github.com/allisonhorst/stats-illustrations) af Allison Horst, forklarer clustering-processen:\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/kmeans.gif\"\n",
|
|
" width=\"550\"/>\n",
|
|
" <figcaption>Illustration af @allison_horst</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Et grundlæggende spørgsmål, der opstår i clustering, er dette: Hvordan ved du, hvor mange klynger dine data skal opdeles i? En ulempe ved at bruge K-Means er, at du skal fastlægge `k`, det vil sige antallet af `centroids`. Heldigvis hjælper `elbow-metoden` med at estimere en god startværdi for `k`. Du vil prøve det om lidt.\n",
|
|
"\n",
|
|
"### \n",
|
|
"\n",
|
|
"**Forudsætning**\n",
|
|
"\n",
|
|
"Vi fortsætter lige der, hvor vi slap i [den forrige lektion](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb), hvor vi analyserede datasættet, lavede en masse visualiseringer og filtrerede datasættet til interessante observationer. Sørg for at tjekke det ud!\n",
|
|
"\n",
|
|
"Vi skal bruge nogle pakker for at komme i gang med dette modul. Du kan installere dem som: `install.packages(c('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork'))`\n",
|
|
"\n",
|
|
"Alternativt kan scriptet nedenfor tjekke, om du har de nødvendige pakker til at gennemføre dette modul, og installere dem for dig, hvis nogle mangler.\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": [
|
|
"Lad os komme i gang!\n",
|
|
"\n",
|
|
"## 1. En dans med data: Indsnævr til de 3 mest populære musikgenrer\n",
|
|
"\n",
|
|
"Dette er en opsummering af, hvad vi gjorde i den forrige lektion. Lad os analysere nogle data!\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": [
|
|
"🤩 Det gik godt!\n",
|
|
"\n",
|
|
"## 2. Mere dataudforskning.\n",
|
|
"\n",
|
|
"Hvor ren er denne data? Lad os tjekke for outliers ved hjælp af boksplot. Vi vil fokusere på numeriske kolonner med færre outliers (selvom du kunne fjerne outliers). Boksplot kan vise dataens rækkevidde og hjælpe med at vælge, hvilke kolonner der skal bruges. Bemærk, at boksplot ikke viser varians, som er et vigtigt element for god data, der kan grupperes. Se venligst [denne diskussion](https://stats.stackexchange.com/questions/91536/deduce-variance-from-boxplot) for yderligere læsning.\n",
|
|
"\n",
|
|
"[Boksplot](https://en.wikipedia.org/wiki/Box_plot) bruges til grafisk at afbilde fordelingen af `numerisk` data, så lad os starte med at *vælge* alle numeriske kolonner sammen med de populære musikgenrer.\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": [
|
|
"Se hvordan udvælgelseshjælperen `where` gør dette nemt 💁? Udforsk andre lignende funktioner [her](https://tidyselect.r-lib.org/).\n",
|
|
"\n",
|
|
"Da vi skal lave et boksplot for hver numerisk egenskab og ønsker at undgå at bruge loops, lad os omformatere vores data til et *længere* format, der giver os mulighed for at udnytte `facets` - underplots, der hver viser et delmængde af dataene.\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": [
|
|
"Meget længere! Nu er det tid til nogle `ggplots`! Så hvilken `geom` vil vi bruge?\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": [
|
|
"Easy-gg!\n",
|
|
"\n",
|
|
"Nu kan vi se, at disse data er lidt støjende: ved at observere hver kolonne som et boxplot kan du se outliers. Du kunne gennemgå datasættet og fjerne disse outliers, men det ville gøre dataene ret minimale.\n",
|
|
"\n",
|
|
"Lad os for nu vælge, hvilke kolonner vi vil bruge til vores klyngeøvelse. Lad os vælge de numeriske kolonner med lignende intervaller. Vi kunne kode `artist_top_genre` som numerisk, men vi dropper den for nu.\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. Beregning af k-means clustering i R\n",
|
|
"\n",
|
|
"Vi kan beregne k-means i R ved hjælp af den indbyggede `kmeans`-funktion, se `help(\"kmeans()\")`. `kmeans()`-funktionen accepterer en data frame med alle numeriske kolonner som dens primære argument.\n",
|
|
"\n",
|
|
"Det første trin, når man bruger k-means clustering, er at angive antallet af klynger (k), der vil blive genereret i den endelige løsning. Vi ved, at der er 3 musikgenrer, som vi har udtrukket fra datasættet, så lad os prøve med 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-objektet indeholder flere oplysninger, som er godt forklaret i `help(\"kmeans()\")`. For nu vil vi fokusere på nogle få. Vi ser, at dataene er blevet grupperet i 3 klynger med størrelserne 65, 110, 111. Outputtet indeholder også klyngecentrene (gennemsnit) for de 3 grupper på tværs af de 5 variabler.\n",
|
|
"\n",
|
|
"Klyngevektoren er klyngetildelingen for hver observation. Lad os bruge `augment`-funktionen til at tilføje klyngetildelingen til det oprindelige datasæt.\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": [
|
|
"Perfekt, vi har netop opdelt vores datasæt i 3 grupper. Så, hvor god er vores klyngedannelse 🤷? Lad os tage et kig på `Silhouette score`.\n",
|
|
"\n",
|
|
"### **Silhouette score**\n",
|
|
"\n",
|
|
"[Silhouette-analyse](https://en.wikipedia.org/wiki/Silhouette_(clustering)) kan bruges til at undersøge afstanden mellem de resulterende klynger. Denne score varierer fra -1 til 1, og hvis scoren er tæt på 1, er klyngen tæt og godt adskilt fra andre klynger. En værdi tæt på 0 repræsenterer overlappende klynger med prøver, der ligger meget tæt på beslutningsgrænsen for de nærliggende klynger. [kilde](https://dzone.com/articles/kmeans-silhouette-score-explained-with-python-exam).\n",
|
|
"\n",
|
|
"Den gennemsnitlige silhouette-metode beregner den gennemsnitlige silhouette for observationer ved forskellige værdier af *k*. En høj gennemsnitlig silhouette-score indikerer en god klyngedannelse.\n",
|
|
"\n",
|
|
"Funktionen `silhouette` i cluster-pakken bruges til at beregne den gennemsnitlige silhouette-bredde.\n",
|
|
"\n",
|
|
"> Silhouetten kan beregnes med enhver [distance](https://en.wikipedia.org/wiki/Distance \"Distance\")-metrik, såsom [Euklidisk distance](https://en.wikipedia.org/wiki/Euclidean_distance \"Euclidean distance\") eller [Manhattan distance](https://en.wikipedia.org/wiki/Manhattan_distance \"Manhattan distance\"), som vi diskuterede i [den forrige lektion](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": [
|
|
"Vores score er **0,549**, altså lige midt imellem. Dette indikerer, at vores data ikke er specielt velegnede til denne type klyngedannelse. Lad os se, om vi kan bekræfte denne mistanke visuelt. [factoextra-pakken](https://rpkgs.datanovia.com/factoextra/index.html) tilbyder funktioner (`fviz_cluster()`) til at visualisere klyngedannelse.\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": [
|
|
"Overlap i klynger indikerer, at vores data ikke er særligt velegnede til denne type klyngedannelse, men lad os fortsætte.\n",
|
|
"\n",
|
|
"## 4. Bestemmelse af optimale klynger\n",
|
|
"\n",
|
|
"Et grundlæggende spørgsmål, der ofte opstår i K-Means klyngedannelse, er dette - uden kendte klasselabels, hvordan ved du, hvor mange klynger du skal opdele dine data i?\n",
|
|
"\n",
|
|
"En måde, vi kan forsøge at finde ud af det på, er at bruge en datasample til at `oprette en række klyngemodeller` med et stigende antal klynger (f.eks. fra 1-10) og evaluere klyngemålinger såsom **Silhouette-scoren.**\n",
|
|
"\n",
|
|
"Lad os bestemme det optimale antal klynger ved at beregne klyngealgoritmen for forskellige værdier af *k* og evaluere **Within Cluster Sum of Squares** (WCSS). Den samlede within-cluster sum of square (WCSS) måler klyngens kompakthed, og vi ønsker, at den skal være så lille som muligt, hvor lavere værdier betyder, at datapunkterne er tættere på hinanden.\n",
|
|
"\n",
|
|
"Lad os undersøge effekten af forskellige valg af `k`, fra 1 til 10, på denne klyngedannelse.\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": [
|
|
"Nu hvor vi har den samlede within-cluster sum-of-squares (tot.withinss) for hver klyngedannelsesalgoritme med center *k*, bruger vi [albue-metoden](https://en.wikipedia.org/wiki/Elbow_method_(clustering)) til at finde det optimale antal klynger. Metoden består i at plotte WCSS som en funktion af antallet af klynger og vælge [kurvens albue](https://en.wikipedia.org/wiki/Elbow_of_the_curve \"Albue på kurven\") som det antal klynger, der skal bruges.\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": [
|
|
"Plottet viser en markant reduktion i WCSS (altså større *tæthed*), når antallet af klynger stiger fra én til to, og en yderligere mærkbar reduktion fra to til tre klynger. Herefter bliver reduktionen mindre markant, hvilket resulterer i en `knækpunkt` 💪 i diagrammet omkring tre klynger. Dette er en god indikation på, at der er to til tre rimeligt veladskilte klynger af datapunkter.\n",
|
|
"\n",
|
|
"Vi kan nu gå videre og udtrække klynge-modellen, hvor `k = 3`:\n",
|
|
"\n",
|
|
"> `pull()`: bruges til at udtrække en enkelt kolonne\n",
|
|
">\n",
|
|
"> `pluck()`: bruges til at indeksere datastrukturer som lister\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": [
|
|
"Fantastisk! Lad os gå videre og visualisere de opnåede klynger. Har du lyst til lidt interaktivitet med `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": [
|
|
"Måske ville vi have forventet, at hver klynge (repræsenteret ved forskellige farver) ville have tydelige genrer (repræsenteret ved forskellige former).\n",
|
|
"\n",
|
|
"Lad os tage et kig på modellens nøjagtighed.\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": [
|
|
"Denne models nøjagtighed er ikke dårlig, men heller ikke fantastisk. Det kan skyldes, at dataene ikke egner sig særlig godt til K-Means Clustering. Disse data er for ubalancerede, har for lidt korrelation, og der er for stor variation mellem kolonneværdierne til at danne gode klynger. Faktisk er de klynger, der dannes, sandsynligvis stærkt påvirket eller skævvredet af de tre genrekategorier, vi definerede ovenfor.\n",
|
|
"\n",
|
|
"Ikke desto mindre var det en lærerig proces!\n",
|
|
"\n",
|
|
"I Scikit-learns dokumentation kan du se, at en model som denne, hvor klyngerne ikke er særlig veldefinerede, har et 'varians'-problem:\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/problems.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Infografik fra Scikit-learn</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"## **Varians**\n",
|
|
"\n",
|
|
"Varians defineres som \"gennemsnittet af de kvadrerede afvigelser fra gennemsnittet\" [kilde](https://www.mathsisfun.com/data/standard-deviation.html). I konteksten af dette klyngeproblem refererer det til data, hvor værdierne i vores datasæt har en tendens til at afvige lidt for meget fra gennemsnittet.\n",
|
|
"\n",
|
|
"✅ Dette er et godt tidspunkt til at overveje alle de måder, du kunne løse dette problem på. Justere dataene lidt mere? Bruge andre kolonner? Bruge en anden algoritme? Tip: Prøv at [skalere dine data](https://www.mygreatlearning.com/blog/learning-data-science-with-k-means-clustering/) for at normalisere dem og teste andre kolonner.\n",
|
|
"\n",
|
|
"> Prøv denne '[variansberegner](https://www.calculatorsoup.com/calculators/statistics/variance-calculator.php)' for at forstå konceptet lidt bedre.\n",
|
|
"\n",
|
|
"------------------------------------------------------------------------\n",
|
|
"\n",
|
|
"## **🚀Udfordring**\n",
|
|
"\n",
|
|
"Brug noget tid på denne notebook og juster parametrene. Kan du forbedre modellens nøjagtighed ved at rense dataene mere (for eksempel fjerne outliers)? Du kan bruge vægte til at give mere vægt til bestemte datasamples. Hvad kan du ellers gøre for at skabe bedre klynger?\n",
|
|
"\n",
|
|
"Tip: Prøv at skalere dine data. Der er kommenteret kode i notebooken, der tilføjer standard skalering for at få datakolonnerne til at ligne hinanden mere i forhold til rækkevidde. Du vil opdage, at selvom silhuetscoren falder, udglattes 'knækket' i albuegrafen. Dette skyldes, at hvis dataene ikke skaleres, får data med mindre varians mere vægt. Læs lidt mere om dette problem [her](https://stats.stackexchange.com/questions/21222/are-mean-normalization-and-feature-scaling-needed-for-k-means-clustering/21226#21226).\n",
|
|
"\n",
|
|
"## [**Quiz efter forelæsning**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/30/)\n",
|
|
"\n",
|
|
"## **Gennemgang & Selvstudie**\n",
|
|
"\n",
|
|
"- Tag et kig på en K-Means Simulator [som denne](https://user.ceng.metu.edu.tr/~akifakkus/courses/ceng574/k-means/). Du kan bruge dette værktøj til at visualisere eksempler på datapunkter og bestemme deres centroid. Du kan redigere dataenes tilfældighed, antal klynger og antal centroid. Hjælper dette dig med at få en idé om, hvordan dataene kan grupperes?\n",
|
|
"\n",
|
|
"- Tag også et kig på [dette handout om K-Means](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html) fra Stanford.\n",
|
|
"\n",
|
|
"Vil du prøve dine nyerhvervede klyngefærdigheder på datasæt, der egner sig godt til K-Means clustering? Se venligst:\n",
|
|
"\n",
|
|
"- [Træn og evaluer klyngemodeller](https://rpubs.com/eR_ic/clustering) ved hjælp af Tidymodels og lignende\n",
|
|
"\n",
|
|
"- [K-means Cluster Analysis](https://uc-r.github.io/kmeans_clustering), UC Business Analytics R Programming Guide\n",
|
|
"\n",
|
|
"- [K-means clustering med tidy data-principper](https://www.tidymodels.org/learn/statistics/k-means/)\n",
|
|
"\n",
|
|
"## **Opgave**\n",
|
|
"\n",
|
|
"[Prøv forskellige klynge-metoder](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/2-K-Means/assignment.md)\n",
|
|
"\n",
|
|
"## TAK TIL:\n",
|
|
"\n",
|
|
"[Jen Looper](https://www.twitter.com/jenlooper) for at skabe den originale Python-version af dette modul ♥️\n",
|
|
"\n",
|
|
"[`Allison Horst`](https://twitter.com/allison_horst/) for at skabe de fantastiske illustrationer, der gør R mere imødekommende og engagerende. Find flere illustrationer i hendes [galleri](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n",
|
|
"\n",
|
|
"God læring,\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>Kunstværk af @allison_horst</figcaption>\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Ansvarsfraskrivelse**: \nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.\n"
|
|
]
|
|
}
|
|
]
|
|
} |