packages feed

kmeans-vector 0.3.1 → 0.3.2

raw patch · 4 files changed

+106/−103 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Math.KMeans: instance Foldable Cluster
+ Math.KMeans: instance Functor Cluster
+ Math.KMeans: instance Monoid (Cluster a)
+ Math.KMeans: instance Traversable Cluster

Files

Math/KMeans.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE BangPatterns #-}  {- |@@ -37,6 +40,12 @@ import qualified Data.List as L import Data.Function (on) +#if !MIN_VERSION_base(4, 8, 0)+import Data.Foldable+import Data.Monoid+import Data.Traversable+#endif+ -- | A distance on vectors type Distance = V.Vector Double -> V.Vector Double -> Double @@ -68,9 +77,9 @@ type Centroids  = G.Vector (V.Vector Double)  -- | A 'Cluster' of points is just a list of points-newtype Cluster a = +newtype Cluster a =   Cluster { elements :: [a] -- ^ elements that belong to that cluster-          } deriving (Eq, Show)+          } deriving (Eq, Show, Functor, Monoid, Foldable, Traversable)  clusterAdd :: Cluster a -> a -> Cluster a clusterAdd (Cluster c) x = Cluster (x:c)
+ bench/OldKMeans.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}++{- |+Module      :  Math.KMeans+Copyright   :  (c) Alp Mestanogullari, Ville Tirronen, 2011-2014+License     :  BSD3+Maintainer  :  Alp Mestanogullari <alpmestan@gmail.com>+Stability   :  experimental++An implementation of the k-means clustering algorithm based on the efficient vector package.++-}++module OldKMeans (kmeans, Point, Cluster(..), computeClusters) where++import qualified Data.Vector.Unboxed as V+import qualified Data.Vector as G+import qualified Data.List as L+import Data.Function (on)++--- * K-Means clustering algorithm++-- | Type holding an object of any type and its associated feature vector+type Point a = (V.Vector Double, a)++-- | Type representing a cluster (group) of vectors by its center and an id+data Cluster = Cluster {+  cid    :: {-# UNPACK #-} !Int, -- ^ an identifier for the cluster+  center :: !(V.Vector Double)   -- ^ the 'position' of the center of the cluster+  } -- deriving (Show,Eq)++-- genVec = V.fromList `fmap` vectorOf 3 arbitrary+-- genPts = (flip zip) [0..] `fmap` replicateM 10 genVec+-- genClusters = do+--    cs <- replicateM 5 genVec+--    return (zipWith Cluster [0.. ] cs)+--+-- prop_regroup = forAll genClusters $ \c ->+--                forAll genPts $ \v ->+--                  s (regroupPoints c v) == s (regroupPoints' c v)+--    where+--     same xs = length (L.nub xs) == length xs+--     s = map L.sort+++{-# INLINE distance #-}+distance :: Point a -> V.Vector Double -> Double+distance (u,_) v = V.sum $ V.zipWith (\a b -> (a - b)^2) u v++partition :: Int -> [a] -> [[a]]+partition k vs = go vs+  where go vs = case L.splitAt n vs of+          (vs', []) -> [vs']+          (vs', vss) -> vs' : go vss+        n = (length vs + k - 1) `div` k++{-#INLINE computeClusters#-}+computeClusters :: [[V.Vector Double]] -> [Cluster]+computeClusters = zipWith Cluster [0..] . map f+  where f (x:xs) = let (n, v) = L.foldl' (\(k, s) v' -> (k+1, V.zipWith (+) s v')) (1, x) xs+                   in V.map (\x -> x / (fromIntegral n)) v++{-#INLINE regroupPoints#-}+regroupPoints :: forall a. [Cluster] -> [Point a] -> [[Point a]]+regroupPoints clusters points = L.filter (not.null) . G.toList . G.accum (flip (:)) (G.replicate (length clusters) []) . map closest $ points+ where+   closest p = (cid (L.minimumBy (compare `on` (distance p . center)) clusters),p)++regroupPoints' :: [Cluster] -> [Point a] -> [[Point a]]+regroupPoints' clusters points = go points+  where go points = map (map snd) . L.groupBy ((==) `on` fst) . L.sortBy (compare `on` fst) $ map (\p -> (closest p, p)) points+        closest p = cid $ L.minimumBy (compare `on` (distance p . center)) clusters++kmeansStep :: [Point a] -> [[Point a]] -> [[Point a]]+kmeansStep points pgroups = +  regroupPoints (computeClusters . map (map fst) $ pgroups) points++kmeansAux :: [Point a] -> [[Point a]] -> [[Point a]]+kmeansAux points pgroups = let pss = kmeansStep points pgroups in+  -- has anything changed since the last step?+  -- even a point jumping from one cluster to another is enough to+  -- enter the 'False' case+  case map (map fst) pss == map (map fst) pgroups of+  True -> pgroups -- nothing's changed, we're done+  False -> kmeansAux points pss -- something has changed, so let's try again++-- | Performs the k-means clustering algorithm+--   trying to use 'k' clusters on the given list of points+kmeans :: Int -> [Point a] -> [[Point a]]+kmeans k points = kmeansAux points pgroups+  where pgroups = partition k points+{-# INLINE kmeans #-}+
− bench/OldKmeans.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}--{- |-Module      :  Math.KMeans-Copyright   :  (c) Alp Mestanogullari, Ville Tirronen, 2011-2014-License     :  BSD3-Maintainer  :  Alp Mestanogullari <alpmestan@gmail.com>-Stability   :  experimental--An implementation of the k-means clustering algorithm based on the efficient vector package.---}--module OldKMeans (kmeans, Point, Cluster(..), computeClusters) where--import qualified Data.Vector.Unboxed as V-import qualified Data.Vector as G-import qualified Data.List as L-import Data.Function (on)----- * K-Means clustering algorithm---- | Type holding an object of any type and its associated feature vector-type Point a = (V.Vector Double, a)---- | Type representing a cluster (group) of vectors by its center and an id-data Cluster = Cluster {-  cid    :: {-# UNPACK #-} !Int, -- ^ an identifier for the cluster-  center :: !(V.Vector Double)   -- ^ the 'position' of the center of the cluster-  } -- deriving (Show,Eq)---- genVec = V.fromList `fmap` vectorOf 3 arbitrary--- genPts = (flip zip) [0..] `fmap` replicateM 10 genVec--- genClusters = do---    cs <- replicateM 5 genVec---    return (zipWith Cluster [0.. ] cs)------ prop_regroup = forAll genClusters $ \c ->---                forAll genPts $ \v ->---                  s (regroupPoints c v) == s (regroupPoints' c v)---    where---     same xs = length (L.nub xs) == length xs---     s = map L.sort---{-# INLINE distance #-}-distance :: Point a -> V.Vector Double -> Double-distance (u,_) v = V.sum $ V.zipWith (\a b -> (a - b)^2) u v--partition :: Int -> [a] -> [[a]]-partition k vs = go vs-  where go vs = case L.splitAt n vs of-          (vs', []) -> [vs']-          (vs', vss) -> vs' : go vss-        n = (length vs + k - 1) `div` k--{-#INLINE computeClusters#-}-computeClusters :: [[V.Vector Double]] -> [Cluster]-computeClusters = zipWith Cluster [0..] . map f-  where f (x:xs) = let (n, v) = L.foldl' (\(k, s) v' -> (k+1, V.zipWith (+) s v')) (1, x) xs-                   in V.map (\x -> x / (fromIntegral n)) v--{-#INLINE regroupPoints#-}-regroupPoints :: forall a. [Cluster] -> [Point a] -> [[Point a]]-regroupPoints clusters points = L.filter (not.null) . G.toList . G.accum (flip (:)) (G.replicate (length clusters) []) . map closest $ points- where-   closest p = (cid (L.minimumBy (compare `on` (distance p . center)) clusters),p)--regroupPoints' :: [Cluster] -> [Point a] -> [[Point a]]-regroupPoints' clusters points = go points-  where go points = map (map snd) . L.groupBy ((==) `on` fst) . L.sortBy (compare `on` fst) $ map (\p -> (closest p, p)) points-        closest p = cid $ L.minimumBy (compare `on` (distance p . center)) clusters--kmeansStep :: [Point a] -> [[Point a]] -> [[Point a]]-kmeansStep points pgroups = -  regroupPoints (computeClusters . map (map fst) $ pgroups) points--kmeansAux :: [Point a] -> [[Point a]] -> [[Point a]]-kmeansAux points pgroups = let pss = kmeansStep points pgroups in-  -- has anything changed since the last step?-  -- even a point jumping from one cluster to another is enough to-  -- enter the 'False' case-  case map (map fst) pss == map (map fst) pgroups of-  True -> pgroups -- nothing's changed, we're done-  False -> kmeansAux points pss -- something has changed, so let's try again---- | Performs the k-means clustering algorithm---   trying to use 'k' clusters on the given list of points-kmeans :: Int -> [Point a] -> [[Point a]]-kmeans k points = kmeansAux points pgroups-  where pgroups = partition k points-{-# INLINE kmeans #-}-
kmeans-vector.cabal view
@@ -1,13 +1,7 @@ Name:                kmeans-vector-Version:             0.3.1+Version:             0.3.2 Synopsis:            An implementation of the kmeans clustering algorithm based on the vector package Description:         Provides a simple (but efficient) implementation of the k-means clustering algorithm. The goal of this algorithm is to, given a set of n-dimensional points, regroup them in k groups, such that each point gets to be in the group to which it is the closest to (using the 'center' of the group).-                     .-                     CHANGELOG-                     .-                     0.3: total rewrite of the code, the code scales much better on big inputs and is overall-                     consistently faster than the other kmeans implementations on hackage, on my laptop.-                     0.2: supports having feature vectors associated to objects, and thus computing kmeans on these vectors, letting you recover the initial objects. Homepage:            http://github.com/alpmestan/kmeans-vector Bug-reports:	       https://github.com/alpmestan/kmeans-vector/issues License:             BSD3@@ -34,7 +28,7 @@  benchmark bench   main-is:           bench.hs-  other-modules:     OldKmeans+  other-modules:     OldKMeans   hs-source-dirs:    bench   ghc-options:       -O2 -funbox-strict-fields   type:              exitcode-stdio-1.0