From ddbf8afce61e45577b0cb56d267bc44714946f5c Mon Sep 17 00:00:00 2001 From: khuepp <143700333+khuepp@users.noreply.github.com> Date: Wed, 4 Oct 2023 18:48:00 +0200 Subject: [PATCH] Klaas/clustering (#46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * front without logic implemented * läuft mit einem Fehler * Deutsch * Added Kmeans Service File and Test * Sync test-deployment with main (#29) * Installed Playwright for E2E-Testing * Installed ESLint * Added Linting as GitHub Action * Removed Linting as GitHub Action * csv inport (#24) * front without logic implemented * läuft mit einem Fehler * Deutsch * Update sync-test-deploy-branch.yaml (#25) * Update sync-test-deploy-branch.yaml * Update sync-test-deploy-branch.yaml * Finalized sync-test-deploy-branch.yaml (#28) Should work now. Was tested in separate repo --------- Co-authored-by: david-2031 <113417732+david-2031@users.noreply.github.com> * Daniel/only https (#30) * Installed Playwright for E2E-Testing * Installed ESLint * Added Linting as GitHub Action * Removed Linting as GitHub Action * csv inport (#24) * front without logic implemented * läuft mit einem Fehler * Deutsch * Update sync-test-deploy-branch.yaml (#25) * Update sync-test-deploy-branch.yaml * Update sync-test-deploy-branch.yaml * Finalized sync-test-deploy-branch.yaml (#28) Should work now. Was tested in separate repo * Ensured only HTTPS is used to access * Fixed Routing Bug (Probably) --------- Co-authored-by: david-2031 <113417732+david-2031@users.noreply.github.com> * Inputfeld Manhattan/Euclidean, Input lokale Berechnung * Sync test-deployment with main (#31) * Chart working with Mock-Data * Removed unused import * Installed Playwright for E2E-Testing * Installed ESLint * Added Linting as GitHub Action * Removed Linting as GitHub Action * csv inport (#24) * front without logic implemented * läuft mit einem Fehler * Deutsch * Chart working with Mock-Data * Removed unused import * Update sync-test-deploy-branch.yaml (#25) * Update sync-test-deploy-branch.yaml * Update sync-test-deploy-branch.yaml * Finalized sync-test-deploy-branch.yaml (#28) Should work now. Was tested in separate repo --------- Co-authored-by: cepurwin Co-authored-by: david-2031 <113417732+david-2031@users.noreply.github.com> * Inputfeld Manhattan/Euclidean; Input lokale Berechnung; Hinweis dass K optional ist * Update cloudbuild.yaml * Update app.yaml * Ensured usage of HTTPS * Optionen für die Methode zur Bestiummung von K eingebaut (Ellenbogen, Silhouette); Berechnungsmethode um Methode "Jaccard" ergänzt Alle Optionen wurden in ein Expansion Panel eingebaut * Daniel/fix merge conflicts1 (#36) * Update input.component.css from main for fixing merge conflict * Update input.component.html from main for fixing merge conflict * Update input.component.ts from main for fixing merge conflict * Sync test-deployment with main (#33) * Chart working with Mock-Data * Removed unused import * Installed Playwright for E2E-Testing * Installed ESLint * Added Linting as GitHub Action * Removed Linting as GitHub Action * csv inport (#24) * front without logic implemented * läuft mit einem Fehler * Deutsch * Chart working with Mock-Data * Removed unused import * Update sync-test-deploy-branch.yaml (#25) * Update sync-test-deploy-branch.yaml * Update sync-test-deploy-branch.yaml * Finalized sync-test-deploy-branch.yaml (#28) Should work now. Was tested in separate repo * Dave/import data format (#32) * edited upload text * display uploaded data * Fixed reloading bug (#35) * Update app.yaml * Update cloudbuild.yaml --------- Co-authored-by: cepurwin Co-authored-by: david-2031 <113417732+david-2031@users.noreply.github.com> * Got linting to working and added GitHub action "lint" (#39) * Added Kmeans Service File and Test * Fixed most linting errors * CSV-File Reading implemented * KMeans and JSON-result implementet * Added Elbow Method * Added Elbow Method * Added Elbow Method * changed check for ellbow method * removed useless console.log --------- Co-authored-by: david-2031 Co-authored-by: REDDERD <143699981+REDDERD@users.noreply.github.com> Co-authored-by: david-2031 <113417732+david-2031@users.noreply.github.com> Co-authored-by: cookie.exe Co-authored-by: cepurwin Co-authored-by: Cornelius Daemberg <108803070+Splashed01@users.noreply.github.com> Co-authored-by: cepurwin <113333536+cepurwin@users.noreply.github.com> --- .../home-services/kmeans-local.service.ts | 32 ++++++++++++++++++- src/app/home/home.component.ts | 1 - src/app/home/input/input.component.ts | 8 +++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/app/home/home-services/kmeans-local.service.ts b/src/app/home/home-services/kmeans-local.service.ts index f5b9ea8..f35859a 100644 --- a/src/app/home/home-services/kmeans-local.service.ts +++ b/src/app/home/home-services/kmeans-local.service.ts @@ -18,7 +18,34 @@ export class KmeansLocalService { return pointA.reduce((sum, value, index) => sum + Math.abs(value - pointB[index]), 0) } - async performKMeans (csv: File, k: number, distanceMetric: string): Promise { + elbowMethod (data: number[][], maxClusters: number, distanceMetric: string): number { + const ssd: number[] = [] // Sum of Squared Distances for different cluster numbers + + const distanceFunction = distanceMetric === 'EUCLIDEAN' ? this.euclideanDistance : this.manhattanDistance + + for (let i = 1; i <= maxClusters; i++) { + const result = KMeans(data, i, { distanceFunction }) + let currentSSD = 0 + for (let j = 0; j < data.length; j++) { + const centroid = result.centroids[result.clusters[j]] + currentSSD += distanceFunction(data[j], centroid) ** 2 + } + ssd.push(currentSSD) + } + // Calculate the rate of change of SSD (first derivative) + const ratesOfChange = ssd.slice(1).map((value, index) => ssd[index] - value) + // Calculate the second derivative + const secondDerivative = ratesOfChange.slice(1).map((value, index) => ratesOfChange[index] - value) + // Find the index of the maximum value in the second derivative + const elbowPoint = secondDerivative.indexOf(Math.max(...secondDerivative)) + // Convert SSD values to an array of x and y coordinates + // const coordinates = ssd.map((value, index) => ({ x: index + 1, y: value })) + + // Return the optimal number of clusters + return elbowPoint + 2 // +2 because the index is 0-based and we started from k=1 + } + + async performKMeans (csv: File, k: number, useOptK: boolean, distanceMetric: string): Promise { return await new Promise((resolve, reject) => { const fileReader = new FileReader() @@ -30,6 +57,9 @@ export class KmeansLocalService { .map(row => row.map(value => parseFloat(value))) .filter(row => row.length === this.csvData[1].length) + if (useOptK) { + k = this.elbowMethod(dataAsNumbers, 100, distanceMetric) + } const result = KMeans(dataAsNumbers, k, { distanceFunction: distanceMetric === 'EUCLIDEAN' ? this.euclideanDistance : this.manhattanDistance }) diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 83fe8cd..7012240 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -15,7 +15,6 @@ export class HomeComponent { public handleAPIResponse (response: ResponseInterface): void { this.kmeansResult = response - console.log(this.kmeansResult) } public handleLoading (status: boolean): void { diff --git a/src/app/home/input/input.component.ts b/src/app/home/input/input.component.ts index e61f7c3..588bb20 100644 --- a/src/app/home/input/input.component.ts +++ b/src/app/home/input/input.component.ts @@ -33,9 +33,13 @@ export class InputComponent { submit (): void { if (this.clusterInputFormGroup.value.offlineKmeans === true) { - if ((this.file != null) && (this.clusterInputFormGroup.value.distanceMetric != null) && (this.clusterInputFormGroup.value.k != null)) { + if ((this.file != null) && (this.clusterInputFormGroup.value.distanceMetric != null)) { this.isLoading.emit(true) - this.localKmeans.performKMeans(this.file, Number(this.clusterInputFormGroup.value.k), this.clusterInputFormGroup.value.distanceMetric) + this.localKmeans.performKMeans( + this.file, + Number(this.clusterInputFormGroup.value.k) !== 0 ? Number(this.clusterInputFormGroup.value.k) : 0, + Number(this.clusterInputFormGroup.value.k) === 0, + this.clusterInputFormGroup.value.distanceMetric) .then((result) => { this.kmeansResult.emit(result) this.isLoading.emit(false)