Skip to main content
The C-Means Calculator displays several types of results that help you understand how the algorithm is clustering your data. This guide explains how to read and interpret each result component.

Distance Matrix

The distance matrix shows the Euclidean distance between each centroid and each point.

Matrix Structure

The distance matrix is organized as:
  • Rows: Each row represents a centroid (C0, C1, C2, …)
  • Columns: Each column represents a point (P0, P1, P2, …)
  • Values: Each cell contains the distance between that centroid and point
// From MatrixTable.tsx:8-46
const columns = [{
    title: `C`,
    dataIndex: `centroid`,
    key: `centroid`,
    fixed: true
}]

if (matrix.length) {
    columns.push(...matrix[0].map((_point, index) => ({
        title: `P${index}`,
        dataIndex: `${index}`,
        key: index.toString(),
        fixed: false
    }))
    )
}

const dataSource = matrix.map((_row, index) => (
    {
        key: `row${index}`,
        centroid: index,
        ..._row.map((value) => (value.toFixed(4)))
    }
));

Distance Calculation

Distances are calculated using the Euclidean distance formula:
// From CMeans.ts:32-35
export const euclidianDistance = (pointA: Point, pointB: Point) => {
    const distance = Math.sqrt(Math.pow((pointA.x - pointB.x), 2) + Math.pow((pointA.y - pointB.y), 2));
    return distance;
}
The complete distance matrix is computed by iterating over all centroid-point pairs:
// From CMeans.ts:8-23
export const getDistanceMatrix = (points: Point[], centroids: Point[]) => {
    if (points.length === 0 || centroids.length === 0) {
        return [];
    }

    const distanceMatrix = [];
    for (let i = 0; i < centroids.length; i++) {
        const distancesRow = [];
        for (let j = 0; j < points.length; j++) {
            const newDistance = euclidianDistance(centroids[i], points[j]);
            distancesRow.push(newDistance);
        }
        distanceMatrix.push(distancesRow);
    }
    return distanceMatrix;
}
All distance values are displayed with 4 decimal places for precision. Smaller values indicate points that are closer to a centroid.

Reading Distance Values

1

Locate the Point

Find the column for the point you’re interested in (e.g., P3)
2

Compare Across Rows

Look at all values in that column to see distances from each centroid
3

Identify Minimum

The smallest value indicates which centroid the point is closest to

Membership Matrix

The membership matrix shows which cluster each point belongs to. In hard C-Means clustering, this is a binary assignment.

Matrix Structure

  • Rows: Each row represents a centroid/cluster
  • Columns: Each column represents a point
  • Values: Either 1 (point belongs to this cluster) or 0 (point does not belong)

Membership Calculation

The membership is determined by finding the minimum distance:
// From CMeans.ts:43-63
export const getMembershipMatrix = (distanceMatrix: number[][]) => {
    if (distanceMatrix.length === 0) {
        return [];
    }

    const membershipMatrix = [...zeroMatrix(distanceMatrix)];

    for (let j = 0; j < distanceMatrix[0].length; j++) {
        let min = distanceMatrix[0][j];
        let minPosition = 0;

        for (let i = 0; i < distanceMatrix.length; i++) {
            if (distanceMatrix[i][j] < min) {
                min = distanceMatrix[i][j];
                minPosition = i;
            }
        }
        membershipMatrix[minPosition][j] = 1;
    }
    return membershipMatrix;
}
Each column in the membership matrix sums to exactly 1, meaning each point belongs to exactly one cluster.

Interpreting Membership

Value = 1

The point belongs to this cluster and will be used to recalculate the centroid position

Value = 0

The point does not belong to this cluster and won’t influence this centroid’s position

Scatter Plot Visualization

The scatter plot provides an intuitive visual representation of your clustering results.

Visual Elements

1

Points (Small Circles)

Data points are displayed as small circles colored according to their cluster assignment
2

Centroids (Larger Circles)

Centroids appear as larger, more prominent circles with distinct borders
3

Centroid Areas (Translucent Circles)

Each centroid has a translucent area indicator showing its zone of influence

Color Representation

Colors are assigned based on cluster membership:
// From DataChart.tsx:5-7
const centroidHues = [8, 205, 138, 44, 280, 328, 184, 112];

const getCentroidHue = (index: number) => centroidHues[index % centroidHues.length];

Point Coloring

Points are colored according to their dominant cluster membership:
// From DataChart.tsx:9-39
const getPointColor = (pointIndex: number, membershipMatrix: number[][]) => {
  if (membershipMatrix.length === 0) {
    return {
      background: 'rgba(107, 114, 128, 0.55)',
      border: 'rgba(75, 85, 99, 0.9)',
    };
  }

  let dominantCluster = 0;
  let highestMembership = membershipMatrix[0]?.[pointIndex] ?? 0;

  for (let clusterIndex = 1; clusterIndex < membershipMatrix.length; clusterIndex++) {
    const currentMembership = membershipMatrix[clusterIndex]?.[pointIndex] ?? 0;
    if (currentMembership > highestMembership) {
      highestMembership = currentMembership;
      dominantCluster = clusterIndex;
    }
  }

  const hue = getCentroidHue(dominantCluster);
  const normalizedMembership = Math.max(0, Math.min(highestMembership, 1));
  const saturation = 55 + normalizedMembership * 28;
  const lightness = 82 - normalizedMembership * 30;
  const borderLightness = Math.max(28, lightness - 18);
  const alpha = 0.45 + normalizedMembership * 0.45;

  return {
    background: `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`,
    border: `hsl(${hue}, ${Math.min(100, saturation + 6)}%, ${borderLightness}%)`,
  };
};
Before clustering begins (empty membership matrix), points appear in neutral gray. Once clustering starts, they take on their cluster’s color.

Chart Configuration

The scatter plot uses Chart.js with three datasets:
// From DataChart.tsx:60-89
data={{
  datasets: [
    {
      label: 'Points',
      data: points,
      backgroundColor: pointColors.map(({ background }) => background),
      borderColor: pointColors.map(({ border }) => border),
      borderWidth: 1,
      pointRadius: 4,
    },
    {
      label: 'Centroids',
      data: centroids,
      backgroundColor: centroidColors.map(({ background }) => background),
      borderColor: centroidColors.map(({ border }) => border),
      borderWidth: 2,
      pointRadius: 5,
    },
    {
      label: 'Centroids Area',
      data: centroids,
      backgroundColor: centroidColors.map(({ area }) => area),
      borderColor: centroidColors.map(({ border }) => border),
      borderWidth: 1,
      pointRadius: 20,
    },
  ]
}}

Visual Interpretation Tips

Color Consistency

Each cluster has a unique color. Points and centroids of the same cluster share the same hue.

Spatial Grouping

Points should cluster around their assigned centroid. Large distances suggest suboptimal clustering.

Centroid Movement

After each iteration, centroids move toward the center of their assigned points.

Area Overlap

Overlapping centroid areas indicate clusters that are close together or may need reconfiguration.

Cost Function Display

The cost function value appears prominently between the visualization and control buttons:
// From App.tsx:39-41
<section className="px-3 mt-4 flex justify-center py-2">
  <h2 className="text-xl font-semibold">Cost Function: {costFunction.toFixed(4) ?? 0}</h2>
</section>
The cost function should decrease with each iteration as the algorithm optimizes cluster assignments. Learn more in the Monitoring Convergence guide.

Reading Results Together

For complete understanding, examine all results in combination:
1

Check the Scatter Plot

Get an intuitive sense of cluster formation and centroid positions
2

Review the Membership Matrix

Verify which points belong to which clusters
3

Examine the Distance Matrix

Confirm that assigned points are indeed closest to their centroid
4

Monitor the Cost Function

Track whether the algorithm is still improving

Next Steps

Monitoring Convergence

Learn how to determine when the algorithm has reached an optimal solution

Build docs developers (and LLMs) love