Add Avalonia gui
This commit is contained in:
82
KMeansBase/KMeans.cs
Normal file
82
KMeansBase/KMeans.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
9
KMeansBase/KMeansBase.csproj
Normal file
9
KMeansBase/KMeansBase.csproj
Normal 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
16
KMeansBase/Point.cs
Normal 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user