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

What strategies can I use to prevent making multiple API calls inside setInterval when initializing a new connection in a socket?

I am currently working on implementing a socket system where users can request a function with an ID, and after receiving the ID, I send requests to an API every second and emit the response back to the user. Issue: Every time a new user requests the same ...

Send image data in base64 format to server using AJAX to save

My goal is to store a base64 image on a php server using the webcam-easy library (https://github.com/bensonruan/webcam-easy). I added a button to the index.html file of the demo: <button id="upload" onClick="postData()" style=" ...

Prevent users from selecting elements on the mobile site

Hey there! I'm currently working on preventing users from selecting items on my mobile site. While I've been successful in doing so on a regular website using the CSS class below .unselectable { -moz-user-select: -moz-none; -khtml-user-s ...

How to locate the position of a specific word on a webpage using jQuery

In my div, I am struggling to search like an editor. Despite multiple attempts, I have not found a solution yet. Can someone please advise me on how to resolve this issue? <div id="content"> <p> Lorem Ipsum is simply dumm ...

Iterate through the array to verify that the specified conditions are satisfied for each individual item

I am working with an array of items and need to check if they all meet specific criteria. I've created a for loop to iterate through the array, but I'm concerned about its efficiency. Is there a more optimal way to achieve this? let match = 0; ...

What is the best way to transfer a value to v-model using emit?

I'm having trouble passing the selected value from a select field to v-model. Currently, the code only seems to work with a v-text-field. <script> export default { name: 'FormSelect', props: { model ...

Enhancing Accessibility for the jQuery Countdown Plugin

Seeking to enhance the accessibility of my website's jQuery countdown, I am striving to adhere to WAI-ARIA guidelines. The specified requirements are as follows: Ensure the area is live so it updates dynamically with the countdown display. Avoid re ...

Activate the popup for sharing or bookmarking by clicking on a regular link

I am currently utilizing a Share/Bookmark script: <div class="singles-right"> <a href="#" class="bookmark"></a> <script type="text/javascript" src="http://static.addinto.com/ai/ai2_bkmk.js"></script> <a href="#" clas ...

Exploring how to utilize optional URL parameters within Express.js

When using Express.js version 4.14, I implemented the following route: app.get('/show/:name/:surname?/:address?/:id/:phone?', function(req, res) { res.json({ name: req.params.name, surname: req.params.surname, address ...

What could be causing the slow loading time of my Shopify App developed using Next.js (React)?

I recently followed a tutorial at However, I am facing severe performance issues with my app. It loads extremely slowly when changing tabs, whether it's running on ngrok, localhost, or deployed on app engine. I'm new to React, Next.js, and Shop ...

Tracker.gg's API for Valorant

After receiving help with web scraping using tracker.gg's API and puppeteer, I encountered an error message when the season changed {"errors":[{"code":"CollectorResultStatus::InvalidParameters","message":" ...

Having trouble getting the Pokemon modal to show both the type and image?

HTML: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My First JS App</title> <lin ...

Should you opt for returning [something] or (nothing) in Javascript with ExpressJS?

Is there a distinct contrast between returning something and returning nothing? Let's take a look at an example: user.save(function(err){ if ( err && err.code !== 11000 ) { console.log(err); console.log(err.code); res.send(&apo ...

What is the most effective method for postponing the loading of JavaScript?

Incorporating a bootstrap theme into my project has required me to include several javascript files. The challenge arises when some pages load dynamic content from the server, resulting in the entire HTML not being present when the javascript files are exe ...

Hide elements forever once the form is submitted

I'm seeking help to figure out how to make certain elements disappear after a form submission on my website's dashboard page. Specifically, I need to hide three elements once the user has submitted a form. Elements that need to be hidden: .vc_t ...

Using a nested loop in Javascript to fetch JSON data

My goal is to display Categories and their corresponding subcategories in a specific order. However, my current method of using loops within loops is not producing the desired outcome: Category(Mobile) Category(Laptop) Subcategory(Iphone4) Subcategory(Iph ...

Slick Slider fails to load on web browsers

Hi everyone, I have a snippet of HTML code that I need help with: <!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.css"/> </head> <body> ...

Strangely compressed HTML image

I am facing an issue with using a base64-encoded png retrieved from a server in WebGL. To incorporate the encoded png, I load it into an html Image object. For my specific needs, it is crucial that the png data remains completely lossless, however, I&apos ...

What is the method for interacting with an embedded alert using SeleniumIDE?

When working with HTML, I came across the following code snippet (it's not from my website): <script> alert('1'); </script> My challenge now is to test this code using Selenium IDE and ensure that the alert will be clicked. How ...

AngularJS directive created for validation on blur

I am striving to replicate some of the features found in Angular 1.3.X for the app I am developing, with a particular focus on ensuring compatibility with IE 8. Unfortunately, this constraint means that I cannot utilize version 1.3.X. I have encountered di ...