Add Avalonia gui

This commit is contained in:
2022-01-08 13:17:02 +01:00
parent fb05d8887f
commit c317ab897f
19 changed files with 775 additions and 80 deletions

82
KMeansBase/KMeans.cs Normal file
View File

@ -0,0 +1,82 @@
namespace KMeansBase;
public static class KMeans
{
private const int MaxLoops = 30;
public static void KMeansCalculation(IReadOnlyCollection<Point> points, int k, out IReadOnlyCollection<Point> centroids)
{
centroids = InitializeRandomCentroids(points, k).ToArray();
for (var i = 0; i < MaxLoops; i++)
{
AssignPointsToCentroids(points, centroids);
if (!UpdateCentroids(points, centroids)) break;
}
}
private static bool UpdateCentroids(IReadOnlyCollection<Point> points, IEnumerable<Point> centroids)
{
// calculate mean of all points of one cluster.
var updated = false;
foreach (var centroid in centroids)
{
updated |= UpdateClusterCentroid(centroid, points);
}
return updated;
}
private static bool UpdateClusterCentroid(Point centroid, IEnumerable<Point> points)
{
var pointsOfCluster = points.Where(p => p.ClusterId == centroid.ClusterId).ToArray();
var sumX = pointsOfCluster.Sum(p => p.X);
var meanX = sumX / pointsOfCluster.Length;
var sumY = pointsOfCluster.Sum(p => p.Y);
var meanY = sumY / pointsOfCluster.Length;
return centroid.SetCoordinates(meanX, meanY);
}
private static void AssignPointsToCentroids(IEnumerable<Point> points, IReadOnlyCollection<Point> centroids)
{
foreach (var point in points)
{
var id = 0;
var distance = double.MaxValue;
foreach (var centroid in centroids)
{
// calculate euclid distance and assign id.
var currentDistance = Distance(centroid, point);
if (currentDistance > distance) continue;
distance = currentDistance;
id = centroid.ClusterId;
}
point.ClusterId = id;
}
}
private static double Distance(Point centroid, Point point)
{
return Math.Sqrt(Math.Pow(centroid.X - point.X, 2) + Math.Pow(centroid.Y - point.Y, 2));
}
private static IEnumerable<Point> InitializeRandomCentroids(IReadOnlyCollection<Point> points, int k)
{
var minX = points.Min(p => p.X);
var maxX = points.Max(p => p.X);
var minY = points.Min(p => p.Y);
var maxY = points.Max(p => p.Y);
var rnd = new Random();
for (var i = 0; i < k; i++)
{
var x = (minX + maxX) * rnd.NextDouble();
var y = (minY + maxY) * rnd.NextDouble();
var point = new Point(x, y, i);
Console.WriteLine(point);
yield return point;
}
}
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

16
KMeansBase/Point.cs Normal file
View File

@ -0,0 +1,16 @@
namespace KMeansBase;
public record Point(double X, double Y, int ClusterId)
{
public int ClusterId { get; set; } = ClusterId;
public double X { get; private set; } = X;
public double Y { get; private set; } = Y;
public bool SetCoordinates(double x, double y)
{
if (!(Math.Abs(X - x) > 1e-5) || !(Math.Abs(Y - y) > 1e-5)) return false;
X = x;
Y = y;
return true;
}
}