{"id":668,"date":"2009-01-29T11:38:56","date_gmt":"2009-01-29T10:38:56","guid":{"rendered":"http:\/\/www.navision-blog.de\/2009\/01\/29\/kmeans-clustering-with-f\/"},"modified":"2023-01-01T13:18:02","modified_gmt":"2023-01-01T13:18:02","slug":"n-dimensional-k-means-clustering-with-f","status":"publish","type":"post","link":"http:\/\/www.navision-blog.de\/blog\/2009\/01\/29\/n-dimensional-k-means-clustering-with-f\/","title":{"rendered":"n-dimensional k-means clustering with F#"},"content":{"rendered":"<p>The <a href=\"http:\/\/en.wikipedia.org\/wiki\/K-means_algorithm\">K-means-Algorithm<\/a> is one of the simplest unsupervised learning algorithms that solve the well known clustering problem. In this article I will show how we can implement this in F#.<\/p>\n<p>First of all we define an interface for \u201cclusterable\u201d objects:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">type<\/span> IClusterable =<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">abstract<\/span> DimValues: float array <span style=\"color: blue\">with<\/span> get <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">abstract<\/span> Dimensions: int<\/p>\n<\/p>\n<\/div>\n<p>The next step is to define a distance function. We will use n-dimensional <a href=\"http:\/\/en.wikipedia.org\/wiki\/Euclidean_distance\">Euclidean distance<\/a> here.<\/p>\n<p><img loading=\"lazy\" style=\"display: inline\" title=\"Euclidean distance\" alt=\"Euclidean distance\" src=\"http:\/\/www.navision-blog.de\/images\/KMeansClusteringwithF_9D18\/image.png\" width=\"400\" height=\"47\" \/><\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> calcDist (p1:IClusterable) (p2:IClusterable) =<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> sq x = x * x<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">if<\/span> p1.Dimensions &lt;&gt; p2.Dimensions <span style=\"color: blue\">then<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; failwith <span style=\"color: maroon\">&quot;Cluster dimensions aren&#8217;t equal.&quot;<\/span><\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; p2.DimValues<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; |&gt; Array.fold2<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; (<span style=\"color: blue\">fun<\/span> acc x y <span style=\"color: blue\">-&gt;<\/span> x &#8211; y |&gt; sq |&gt; (+) acc) <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0. p1.DimValues <\/p>\n<p style=\"margin: 0px\">&#160;&#160; |&gt; sqrt&#160; <\/p>\n<\/p>\n<\/div>\n<p>Now we define a n-dimensional <a href=\"http:\/\/en.wikipedia.org\/wiki\/Centroid\">Centroid<\/a> type. Our kMeans-Algorithm will minimize the squared distances to k centroids (or \u201cmeans\u201d).<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">type<\/span> Centroid =<\/p>\n<p style=\"margin: 0px\">&#160; {Values: float array;<\/p>\n<p style=\"margin: 0px\">&#160;&#160; dimensions: int}<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">member<\/span> x.Print = printfn <span style=\"color: maroon\">&quot;%A&quot;<\/span> x.Values <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">interface<\/span> IClusterable <span style=\"color: blue\">with<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">member<\/span> x.DimValues = x.Values&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">member<\/span> x.Dimensions = x.dimensions&#160;&#160;&#160; <\/p>\n<\/p>\n<\/div>\n<p>The centroid of a finite set of n-dimensional points x<sub>1<\/sub>, x<sub>2<\/sub>, \u2026, x<sub>n<\/sub> is calculated as<\/p>\n<p><img loading=\"lazy\" style=\"display: inline\" title=\"image\" alt=\"image\" src=\"http:\/\/www.navision-blog.de\/images\/KMeansClusteringwithF_9D18\/image_3.png\" width=\"201\" height=\"40\" \/><\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> calcCentroid (items:&#8217;a list <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable)<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160; (oldCentroid:Centroid) =<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> count = items.Length<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">if<\/span> count = 0 <span style=\"color: blue\">then<\/span> oldCentroid <span style=\"color: blue\">else<\/span><\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> mValues =&#160;&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; [|<span style=\"color: blue\">for<\/span> d <span style=\"color: blue\">in<\/span> 0..oldCentroid.dimensions-1 <span style=\"color: blue\">do<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> sum =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; items <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; |&gt; List.sumBy (<span style=\"color: blue\">fun<\/span> item <span style=\"color: blue\">-&gt;<\/span> item.DimValues.[d])<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style=\"color: blue\">yield<\/span> sum \/ (count |&gt; float)|]<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; { Values = mValues;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; dimensions = oldCentroid.dimensions} <\/p>\n<\/p>\n<\/div>\n<p>We made a small modification &#8211; if we don\u2019t have any items assigned to a cluster the centroid won\u2019t change. This is important for the robustness of the algorithm.<\/p>\n<p>Now we can calculate the squared errors to a centroid:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> calcError centroid (items:&#8217;a list <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable) = <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> calcDiffToCentroid (item:&#8217;a) =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; centroid.Values<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; |&gt; Array.fold2 <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (<span style=\"color: blue\">fun<\/span> acc c i <span style=\"color: blue\">-&gt;<\/span> acc + (c-i)*(c-i)) <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0. item.DimValues<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; items<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; |&gt; List.sumBy calcDiffToCentroid&#160; <\/p>\n<\/p>\n<\/div>\n<p>For storing cluster information we create a cluster type:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">type<\/span> Cluster&lt;&#8216;a&gt; <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable =<\/p>\n<p style=\"margin: 0px\">&#160; {Items: &#8216;a list;<\/p>\n<p style=\"margin: 0px\">&#160;&#160; Count: int;<\/p>\n<p style=\"margin: 0px\">&#160;&#160; Centroid: Centroid;<\/p>\n<p style=\"margin: 0px\">&#160;&#160; Error: float}<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">member<\/span> x.Print = <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160; printfn <span style=\"color: maroon\">&quot;(Items: %d, Centroid: %A, Error: %.2f)&quot;<\/span> <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160; x.Count x.Centroid x.Error<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">member<\/span> CreateCluster dimensions (item:&#8217;a) =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> items = [item]<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> empty = <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; { Values = [||];<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; dimensions = dimensions}<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> centroid = calcCentroid items empty<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; { Items = items;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Count = 1;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Centroid = centroid;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Error = calcError centroid items}<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">member<\/span> x.EvolveCluster (items:&#8217;a list) = <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> l = items.Length<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> centroid = calcCentroid items x.Centroid<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; {x <span style=\"color: blue\">with<\/span> <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Items = items;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Count = l;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Centroid = centroid;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; Error = calcError centroid items}&#160; <\/p>\n<\/p>\n<\/div>\n<p>Now we need a function to initialize the clusters and a function to assign a items to the nearest cluster:    <\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">open<\/span> System<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> rand = <span style=\"color: blue\">new<\/span> Random()&#160; <\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> InitClusters (k:int) dimensions <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; (items:&#8217;a array <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable)&#160; =<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> length = items.Length<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> getInitItem() = items.[rand.Next length]<\/p>\n<p style=\"margin: 0px\">&#160; Array.init k (<span style=\"color: blue\">fun<\/span> _ <span style=\"color: blue\">-&gt;<\/span> <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; getInitItem() <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; |&gt; Cluster&lt;&#8216;a&gt;.CreateCluster dimensions)&#160; <\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> AssignItemsToClusters k dimensions (clusters:Cluster&lt;&#8216;a&gt; array)<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; (items:&#8217;a seq <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable) = <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">if<\/span> k &lt;= 0 <span style=\"color: blue\">then<\/span> failwith <span style=\"color: maroon\">&quot;KMeans needs k &gt; 0&quot;<\/span>&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> findNearestCluster item =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> minDist,nearest,lastPos =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; clusters<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; |&gt; Array.fold<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (<span style=\"color: blue\">fun<\/span> (minDist,nearest,pos) cluster <span style=\"color: blue\">-&gt;<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> distance = calcDist item (cluster.Centroid)<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style=\"color: blue\">if<\/span> distance &lt; minDist <span style=\"color: blue\">then<\/span> <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (distance,pos,pos+1) <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style=\"color: blue\">else<\/span> <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (minDist,nearest,pos+1))<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (Double.PositiveInfinity,0,0)&#160; <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; nearest<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> assigned =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; items <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; |&gt; Seq.map (<span style=\"color: blue\">fun<\/span> item <span style=\"color: blue\">-&gt;<\/span> item,findNearestCluster item) <\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> newClusters = Array.create k []&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">for<\/span> item,nearest <span style=\"color: blue\">in<\/span> assigned <span style=\"color: blue\">do<\/span>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; newClusters.[nearest] &lt;- item::(newClusters.[nearest])<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; clusters<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; |&gt; Array.mapi (<span style=\"color: blue\">fun<\/span> i c <span style=\"color: blue\">-&gt;<\/span> c.EvolveCluster newClusters.[i])&#160;&#160;&#160; <\/p>\n<\/p>\n<\/div>\n<p>The last step is a function which calculates the squared error over all clusters:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> calcClusterError (clusters:Cluster&lt;&#8216;a&gt; array) =<\/p>\n<p style=\"margin: 0px\">&#160; clusters<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; |&gt; Array.sumBy (<span style=\"color: blue\">fun<\/span> cluster <span style=\"color: blue\">-&gt;<\/span> cluster.Error)<\/p>\n<\/p>\n<\/div>\n<p>Now it is an easy task to write the kMeans algorithm:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> kMeansClustering K dimensions epsilon <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160;&#160; (items:&#8217;a array <span style=\"color: blue\">when<\/span> &#8216;a :&gt; IClusterable) =<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> k = <span style=\"color: blue\">if<\/span> K &lt;= 0 <span style=\"color: blue\">then<\/span> 1 <span style=\"color: blue\">else<\/span> K<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">let<\/span> <span style=\"color: blue\">rec<\/span> clustering lastError (clusters:Cluster&lt;&#8216;a&gt; array) =<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> newClusters = <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; AssignItemsToClusters k dimensions clusters items<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">let<\/span> newError = calcClusterError newClusters<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">if<\/span> abs(lastError &#8211; newError) &gt; epsilon <span style=\"color: blue\">then<\/span>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; clustering newError newClusters<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">else<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160;&#160;&#160; newClusters,newError&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">&#160; InitClusters k dimensions items<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; |&gt; clustering Double.PositiveInfinity&#160; <\/p>\n<\/p>\n<\/div>\n<p>We can test this algorithm with random 2-dimensional points:<\/p>\n<div style=\"font-family: courier new; background: white; color: black; font-size: 10pt\">\n<p style=\"margin: 0px\"><span style=\"color: blue\">type<\/span> Point =<\/p>\n<p style=\"margin: 0px\">&#160; {X:float; Y: float}&#160; <\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">member<\/span> x.Print = printfn <span style=\"color: maroon\">&quot;(%.2f,%.2f)&quot;<\/span> x.X x.Y<\/p>\n<p style=\"margin: 0px\">&#160; <span style=\"color: blue\">interface<\/span> IClusterable <span style=\"color: blue\">with<\/span><\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">member<\/span> x.DimValues = [| x.X; x.Y |]<\/p>\n<p style=\"margin: 0px\">&#160;&#160;&#160; <span style=\"color: blue\">member<\/span> x.Dimensions = 2&#160;&#160; <\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> point x y = {X = x; Y = y}<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> getRandomCoordinate() = rand.NextDouble() &#8211; 0.5 |&gt; (*) 10.&#160; <\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> randPoint _ = point (getRandomCoordinate()) (getRandomCoordinate())&#160;&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> points n = Array.init n randPoint<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> items = points 1000<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\">printfn <span style=\"color: maroon\">&quot;Items:&quot;<\/span><\/p>\n<p style=\"margin: 0px\">items |&gt; Seq.iter (<span style=\"color: blue\">fun<\/span> i <span style=\"color: blue\">-&gt;<\/span> i.Print)<\/p>\n<p style=\"margin: 0px\">&#160;<\/p>\n<p style=\"margin: 0px\"><span style=\"color: blue\">let<\/span> clusters,error = kMeansClustering 3 2 0.0001 items<\/p>\n<p style=\"margin: 0px\">printfn <span style=\"color: maroon\">&quot;\\n&quot;<\/span><\/p>\n<p style=\"margin: 0px\">clusters |&gt; Seq.iter (<span style=\"color: blue\">fun<\/span> c <span style=\"color: blue\">-&gt;<\/span> c.Print)&#160;&#160;&#160; <\/p>\n<p style=\"margin: 0px\">printfn <span style=\"color: maroon\">&quot;Error: %A&quot;<\/span> error&#160;&#160;&#160; <\/p>\n<\/p>\n<\/div>\n<p><a href=\"http:\/\/www.navision-blog.de\/2009\/01\/31\/n-dimensional-k-means-clustering-with-fsharp-part-ii\/\">Next time<\/a> I will show how we can simplify the code by using F#&#8217;s built-in type vector instead of array.<\/p>\n<div style=\"position:absolute; left:-8355px;\"><a href=\"https:\/\/edpillsbelgium.com\">https:\/\/edpillsbelgium.com<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The K-means-Algorithm is one of the simplest unsupervised learning algorithms that solve the well known clustering problem. In this article I will show how we can implement this in F#. First of all we define an interface for \u201cclusterable\u201d objects: type IClusterable = &#160; abstract DimValues: float array with get &#160; abstract Dimensions: int The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[9,23,448,8],"tags":[515,514,664,516,513],"_links":{"self":[{"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/posts\/668"}],"collection":[{"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/comments?post=668"}],"version-history":[{"count":13,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/posts\/668\/revisions"}],"predecessor-version":[{"id":2047,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/posts\/668\/revisions\/2047"}],"wp:attachment":[{"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/media?parent=668"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/categories?post=668"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.navision-blog.de\/blog\/wp-json\/wp\/v2\/tags?post=668"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}