git.iko.soy/distributed-process
on master help sign in
Iterate the local k-means and generate gnuplots
9403fd0Edsko de Vries
distributed-process-demos/src/MapReduce/KMeans.hs+4−3
@@ -1,5 +1,6 @@
1module KMeans
1module KMeans
2 ( Point
2 ( Point
3+ , Cluster
3 , localKMeans
4 , localKMeans
4 ) where
5 ) where
5
6
@@ -27,14 +28,14 @@ nearest p = minimumBy (compare `on` distanceSq p)
27center :: [Point] -> Point
28center :: [Point] -> Point
28center ps = let (xs, ys) = unzip ps in (average xs, average ys)
29center ps = let (xs, ys) = unzip ps in (average xs, average ys)
29
30
30−kmeans :: Array Int Point -> MapReduce (Int, Int) [Cluster] Cluster Point Point
31+kmeans :: Array Int Point -> MapReduce (Int, Int) [Cluster] Cluster Point ([Point], Point)
31kmeans points = MapReduce {
32kmeans points = MapReduce {
32 mrMap = \(lo, hi) cs -> [ let p = points ! i in (nearest p cs, p)
33 mrMap = \(lo, hi) cs -> [ let p = points ! i in (nearest p cs, p)
33 | i <- [lo .. hi]
34 | i <- [lo .. hi]
34 ]
35 ]
35− , mrReduce = \_ ps -> center ps
36+ , mrReduce = \_ ps -> (ps, center ps)
36 }
37 }
37
38
38−localKMeans :: Array Int Point -> [Cluster] -> Map Cluster Point
39+localKMeans :: Array Int Point -> [Cluster] -> Map Cluster ([Point], Point)
39localKMeans points cs =
40localKMeans points cs =
40 localMapReduce (kmeans points) (Map.fromList [(bounds points, cs)])
41 localMapReduce (kmeans points) (Map.fromList [(bounds points, cs)])
distributed-process-demos/src/MapReduce/SimpleLocalnet.hs+45−13
@@ -1,4 +1,5 @@
1import System.Environment (getArgs)
1import System.Environment (getArgs)
2+import System.IO
2import Control.Applicative
3import Control.Applicative
3import Control.Monad
4import Control.Monad
4import System.Random
5import System.Random
@@ -7,7 +8,7 @@ import Control.Distributed.Process.Node (initRemoteTable)
7import Control.Distributed.Process.Backend.SimpleLocalnet
8import Control.Distributed.Process.Backend.SimpleLocalnet
8import Data.Map (Map)
9import Data.Map (Map)
9import Data.Array (Array, listArray)
10import Data.Array (Array, listArray)
10−import qualified Data.Map as Map (fromList)
11+import qualified Data.Map as Map (fromList, toList, size, elems)
11
12
12import qualified CountWords
13import qualified CountWords
13import qualified MapReduce
14import qualified MapReduce
@@ -18,17 +19,6 @@ rtable = MapReduce.__remoteTable
18 . CountWords.__remoteTable
19 . CountWords.__remoteTable
19 $ initRemoteTable
20 $ initRemoteTable
20
21
21−constructInput :: [FilePath] -> IO (Map FilePath CountWords.Document)
22−constructInput files = do
23− contents <- mapM readFile files
24− return . Map.fromList $ zip files contents
25−
26−randomPoint :: IO KMeans.Point
27−randomPoint = (,) <$> randomIO <*> randomIO
28−
29−arrayFromList :: [e] -> Array Int e
30−arrayFromList xs = listArray (0, length xs - 1) xs
31−
32main :: IO ()
22main :: IO ()
33main = do
23main = do
34 args <- getArgs
24 args <- getArgs
@@ -50,9 +40,51 @@ main = do
50 -- Local k-means
40 -- Local k-means
51 "local" : "kmeans" : [] -> do
41 "local" : "kmeans" : [] -> do
52 points <- replicateM 1000 randomPoint
42 points <- replicateM 1000 randomPoint
53− print $ KMeans.localKMeans (arrayFromList points) (take 10 points)
43+ let kmeans = KMeans.localKMeans (arrayFromList points)
44+
45+ let it 0 = kmeans (take 5 points)
46+ it n = let clusters = it (n - 1) in
47+ kmeans (map snd $ Map.elems clusters)
48+
49+ forM_ ([0 .. 4] :: [Int]) $ \n ->
50+ withFile ("plot" ++ show n) WriteMode $
51+ createGnuPlot (it n)
54
52
55 -- Generic slave for distributed examples
53 -- Generic slave for distributed examples
56 "slave" : host : port : [] -> do
54 "slave" : host : port : [] -> do
57 backend <- initializeBackend host port rtable
55 backend <- initializeBackend host port rtable
58 startSlave backend
56 startSlave backend
57+
58+--------------------------------------------------------------------------------
59+-- Auxiliary --
60+--------------------------------------------------------------------------------
61+
62+constructInput :: [FilePath] -> IO (Map FilePath CountWords.Document)
63+constructInput files = do
64+ contents <- mapM readFile files
65+ return . Map.fromList $ zip files contents
66+
67+randomPoint :: IO KMeans.Point
68+randomPoint = (,) <$> randomIO <*> randomIO
69+
70+arrayFromList :: [e] -> Array Int e
71+arrayFromList xs = listArray (0, length xs - 1) xs
72+
73+-- | Create a gnuplot data file for the output of the k-means algorithm
74+--
75+-- To plot the data, use
76+--
77+-- > plot "<<filename>>" u 1:2:3 with points palette
78+createGnuPlot :: Map KMeans.Cluster ([KMeans.Point], KMeans.Point) -> Handle -> IO ()
79+createGnuPlot clusters h =
80+ mapM_ printPoint . flatten . zip colors . Map.toList $ clusters
81+ where
82+ printPoint (x, y, color) =
83+ hPutStrLn h $ show x ++ " " ++ show y ++ " " ++ show color
84+
85+ flatten :: [(Float, (KMeans.Cluster, ([KMeans.Point], KMeans.Point)))]
86+ -> [(Double, Double, Float)]
87+ flatten = concat . map (\(color, (_, (points, _))) -> map (\(x, y) -> (x, y, color)) points)
88+
89+ colors :: [Float]
90+ colors = [0, 1 / fromIntegral (Map.size clusters) .. 1]