JavaScript is utilized to implement the k-means clustering algorithm, which demonstrates convergence yet lacks stability in its

Although I understand the concept of convergence, I am puzzled by the fact that the results vary each time the algorithm is refreshed, even when using the same dataset. Can someone point out where my methodology might be incorrect? I've been struggling to pinpoint the error in the process.

function customKMeans(kValue, dataArray, canvasElement, convergeFunction) {
    this.canvas = jsHS.GetDimensions(canvasElement);
    this.k = kValue;
    this.centroids = []; 
    this.centroidsToCompare = [];
    this.data = dataArray;
    this.converge = convergeFunction;
    this.initialize();
}
customKMeans.prototype.calculateDistance = function () {
    var difference = 0,
        isArray = jsHS.isArray(arguments);
    if (isArray) {
        if (arguments.length > 2) {
            for (var i = 0; i < arguments.length; i+2) {
                var pointA = arguments[i],
                    pointB = arguments[i + 1];
                difference += Math.pow(pointA[0] - pointB[0], 2);
                difference += Math.pow(pointA[1] - pointB[1], 2);
            }
        }
        else {
            var pointDataA = arguments[0],
                pointDataB = arguments[1];
            difference += Math.pow(pointDataA[0] - pointDataB[0], 2);
            difference += Math.pow(pointDataA[1] - pointDataB[1], 2);
        }
    }
    return Math.sqrt(difference);
};
customKMeans.prototype.calculateMean = function (array) {
    var totalSum = 0;
    [].forEach.call(array, function(element){
        totalSum += element;
    });
    return totalSum / array.length;
};
customKMeans.prototype.initialize = function () {
    for (var l = 0; l < this.k; l++) {
        var dataItem = this.data[Math.floor(Math.random() * this.data.length)];
        this.centroids.push(dataItem);
    }
    for (var i = 0; i < this.centroids.length; i++) {
        if (i > 0) {
            var distance = this.calculateDistance(this.centroids[i], this.centroids[i - 1]);
            console.log(distance);
        }
    }
    this.clusterCentroids(); // return centroid center after calculating means.
};
customKMeans.prototype.clusterCentroids = function () {
    var dataPoints = [];
    this.centroidsToCompare = this.centroids;

    for (var d = 0; d < this.data.length; d++) {
        var distancesArray = [];
        for (var cIndex = 0; cIndex < this.k; cIndex++) {
            var dist = this.calculateDistance(this.centroids[cIndex], this.data[d]);
            distancesArray.push({ 'centroidID': cIndex, 'distance': dist });
        }
        var minResult = distancesArray.reduce((closestCentroid, obj) => {
            return obj.distance < closestCentroid.distance ? obj : closestCentroid;
        });
        dataPoints.push({ 'id': d, 'datapoint': this.data[d], 'centroid': minResult.centroidID });
    }

    var centroidGroups = [];
    for (var c = 0; c < this.k; c++) {
        var group = [];
        for (var p = 0; p < dataPoints.length; p++) {
            if (c === dataPoints[p].centroid) {
                group.push(dataPoints[p]);
            }
        }
        centroidGroups.push(group);
    }
    
    this.centroids = [];
    for (var groupIndex = 0; groupIndex < centroidGroups.length; groupIndex++) {
        var xAxisValues = [],
            yAxisValues = [],
            currentGroup = centroidGroups[groupIndex];
        [].forEach.call(currentGroup, function (dataPoint) {
            xAxisValues.push(dataPoint.datapoint[0]);
            yAxisValues.push(dataPoint.datapoint[1]);
        });

        var meanXValue = this.calculateMean(xAxisValues);
        var meanYValue = this.calculateMean(yAxisValues);
        this.centroids.push([meanXValue, meanYValue]);
    }

    if (JSON.stringify(this.centroidsToCompare) !== JSON.stringify(this.centroids)) {
        this.centroidsToCompare = [];
        dataPoints = [];
        this.clusterCentroids();
    }
    else {
        this.converge(centroidGroups, this.centroids);
    }
};
window['jsHS']['customKMeans'] = customKMeans;

Example Usage

var exampleKMeans = new jsi.customKMeans(5, Array50, canvas, function (convergenceResults, finalCentroids) {
        var count50 = 0;

        var mark = {
            x: 0,
            y: 0,
            radius: 0,
            color: null,
            setCircle: function () {
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
                ctx.fillStyle = this.color;
                ctx.fill();
            }
        };
        [].forEach.call(finalCentroids, (center) => {
            mark.x = center[0];
            mark.y = center[1];
            mark.color = '#0B6623';
            mark.radius = 25;
            mark.setCircle();
        });
    });

This scenario accurately plots centroids on a canvas area but undergoes changes in centroids upon refreshing the browser.

Answer №1

While I haven't delved deeply into your code, it's worth noting that the k-means algorithm often produces varied results with multiple runs. This variability stems from the initial placement of centroids, which are chosen randomly at the start. The algorithm may converge to a local minimum and halt prematurely, failing to reach the global minimum on the first attempt.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the world of technology with jQuery, harnessing the power of JSON objects

I'm seeking assistance with creating JSON using JQuery, sending it via Ajax to a .NET webservice, and then deserializing it in .NET in order to save the data to a database. If anyone could provide guidance on where I might be going wrong, that would ...

Is there a way for me to submit numerous requests to the Game Coordinator at once?

I am currently utilizing the node-globaloffensive library and I am facing an issue where my code is repeating itself and only returning one request back from the gc. My goal is to send multiple requests in order to receive every rank from all users in my d ...

How can you attach a d3 graphic to a table that was created automatically?

Calling all experts in d3, I require urgent assistance!! On this web page, a JSON is fetched from the server containing 50 different arrays of numbers and related data such as 90th percentiles, averages, etc. A table is dynamically created with the basic ...

What is the best way to show a message within a specific HTML division with JavaScript?

Here's my attempt at solving the issue: <head> <script> function validateForm() { var username = document.forms["login"]["uname"].value; var password = document.forms["login"]["pwd"].value; if (username == "" || p ...

Guide to setting the first tab as the default tab using Thymeleaf, Css, and Bootstrap

I am currently working on a project where I need to dynamically create tabs based on a list retrieved from my Spring backend using Thymleaf and Bootstrap. While I have managed to successfully create the tabs and content, I am facing an issue where the fi ...

Filtering data in an antd table by searching

Just starting out with React hooks, specifically using TypeScript, and I'm struggling to implement a search filter with two parameters. Currently, the search filter is only working with one parameter which is 'receiver?.name?'. However, I wo ...

The scrolling feature induces a contraction to the left side

Recently, I implemented the code below to fix my menu when scrolling the page: var num = 5; $(window).bind('scroll', function () { if ($(window).scrollTop() > num) { $('.scroll').css({'position':'fixed&apo ...

Javascript navigation menu failing to accurately display all pages

As I continue to enhance my website at , I have encountered an issue with the menu functionality. The menu is dynamically generated through JavaScript, scanning a folder for pages and populating them into an array. While this system functions smoothly ove ...

creating dynamic column headers with JavaScript

I am looking for a way to make the column names dynamic so that I don't have to manually update them every time. Here is my code snippet: jqGrid11.prototype = { display : function() { $('body').append(this.html.join("")); $("#jqGrid").j ...

Tips for Enhancing JavaScript Performance

Is there a way to enhance my JavaScript code like this: $(window).scroll(function() { if ($('#sec1').isVisible()) { $('.js-nav a.m1').addClass('active'); } else { $('.js-nav a.m1').removeClas ...

Inserting duplicate rows from CSV using JavaScript

Currently, I am utilizing Papaparse to sum up rows with duplicate SKUs in my CSV file. I'm striving to achieve this task without the use of additional JS libraries such as D3. Here is a snippet of how my CSV data is structured: SKU,Daily total,Weekly ...

Is it possible to recognize when the mouse button is held down and the cursor is outside the viewport by using mouseleave detection?

Is there a way to detect when a user moves the mouse outside of the view-port, even if they are holding down the mouse button (for example, if the mouse is on the browser address bar)? In the code below, I am currently using mouseout and mouseleave to det ...

How can we use jQuery to extract an HTML element's external stylesheet and add it to its "style" attribute?

My goal is to extract all CSS references from an external stylesheet, such as <link rel="stylesheet" href="css/General.css">, and add them to the existing styling of each HTML element on my page (converting all CSS to inline). The reason for this re ...

Tips for incorporating MUI into your Redwood JS project

Trying to integrate MUI into Redwood JS has been a challenge for me. I attempted to run the following command in the project directory: yarn add @mui/material Unfortunately, an error message appeared in the console stating: An error Running this command w ...

What is the best way to apply styling to an image that is contained within the document.write function?

I'm looking to customize the design of this cat image, but I'm struggling to locate where to incorporate the div class. It's likely a basic step, but as a beginner in JavaScript, I'm hoping that someone can assist me. document.write(&ap ...

Instructions on keeping a numerical counter at its current value for all site visitors

Recently, I integrated a number counter into my website. However, I am facing an issue where the count resets to zero whenever a new visitor accesses the site. I'd like the count to remain persistent and update based on the previous count. For instanc ...

AngularJS and numerous parallel indexed tables side by side

I need help with displaying two tables, one for profiles and the other for rates. The rates are connected to profiles through the rate name in the JSON data. Is there a way to show these two tables side by side? And when a row is selected in the profile ta ...

Breaking Down the Process of Exporting a Next.js Static Website into Manageable Parts

I am facing memory issues while building my website using Next.js's Static HTML Export. The site has a large number of static pages, approximately 10 million. Is it feasible to export these pages in batches, like exporting 100k pages in each build ite ...

I'm encountering an issue in my node application where it is unable to access the push

I am a beginner in the world of node.js and express. Whenever I try to start my application using the command npm start, I encounter an error message saying Cannot Read property push of undefined from my index.js file. The problematic code snippet looks l ...

Linking query branches without encountering the "Exceeded the number of hooks rendered during the previous render" error

This apollo client utilizes a rest link to interact with 2 APIs. The first API returns the value and ID of a record, while the second API provides additional information about the same record. I combine this information to render the content without using ...