RosettaCodeData/Task/K-means++-clustering/Phix/k-means++-clustering.phix
2019-09-12 10:33:56 -07:00

182 lines
5.8 KiB
Text

-- demo\rosetta\K_means_clustering.exw
-- Press F5 to restart
include pGUI.e
Ihandle dlg, canvas, timer
cdCanvas cddbuffer, cdcanvas
constant TITLE = "K-means++ clustering"
constant useGoInitialData = false -- (not very well centered)
constant N = 30000, -- number of points
K = 16 -- number of clusters
sequence {Px, Py, Pc} @= repeat(0,N), -- coordinates of points and their cluster
{Cx, Cy} @= repeat(0,K) -- coordinates of centroid of cluster
constant colours = {CD_RED, CD_DARK_RED, CD_BLUE, CD_DARK_BLUE, CD_CYAN, CD_DARK_CYAN,
CD_GREEN, CD_DARK_GREEN, CD_MAGENTA, CD_DARK_MAGENTA, CD_YELLOW,
CD_DARK_YELLOW, CD_DARK_ORANGE, CD_INDIGO, CD_PURPLE, CD_DARK_GREY}
if length(colours)<K then ?9/0 end if
function Centroid()
-- Find new centroids of points grouped with current centroids
bool change = false
for c=1 to K do -- for each centroid...
integer x=0, y=0, count:= 0; -- find new centroid
for i=1 to N do -- for all points
if Pc[i] = c then -- grouped with current centroid...
x += Px[i]
y += Py[i]
count += 1
end if
end for
if count!=0 then
x = floor(x/count)
y = floor(y/count)
if Cx[c]!=x
or Cy[c]!=y then
Cx[c] = x
Cy[c] = y
change:= true
end if
end if
end for
return change
end function
function sq(atom x) return x*x end function
procedure Voronoi() -- Group points with their nearest centroid
integer d2, -- distance squared,
min_d2 -- minimum distance squared
for i=1 to N do -- for each point...
min_d2 := #3FFFFFFF -- find closest centroid
for c=1 to K do
d2 := sq(Px[i]-Cx[c]) + sq(Py[i]-Cy[c])
if d2<min_d2 then
min_d2 := d2
Pc[i] := c -- update closest centroid
end if
end for
end for
end procedure
function rand_xy() -- Return random X,Y biased for polar coordinates
atom d := rand(240)-1, -- distance: 0..239
a := rnd()*2*PI -- angle: 0..2pi
integer x:= floor(d*cos(a))+320, -- rectangular coords centered on screen
y:= floor(d*sin(a))+240 -- (that is, assuming 640x480)
return {x,y}
end function
--This little bit is copied from/based on Go:
constant k = K,
nPoints = N,
xBox = 300,
yBox = 200,
stdv = 30
function genECData()
sequence orig = repeat({0,0}, k),
data = repeat({0,0,0}, nPoints)
integer n = 0, nk = k
for i=1 to k do
integer x := rand(xBox)+320,
y := rand(yBox)+240
orig[i] = {x, y}
for j=1 to floor((nPoints-n)/nk) do
n += 1
atom d := rand(stdv)-1, -- distance: 0..239
a := rnd()*2*PI -- angle: 0..2pi
integer nx:= floor(d*cos(a))+x, -- rectangular coords centered on screen
ny:= floor(d*sin(a))+y -- (that is, assuming 640x480)
data[n] = {nx,ny,i}
end for
nk -= 1
end for
if n!=nPoints then ?9/0 end if
return {orig, data}
end function
--</Go ends>
integer iteration = 0
function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)
integer {w, h} = IupGetIntInt(canvas, "DRAWSIZE")
cdCanvasActivate(cddbuffer)
if iteration=0 then
if useGoInitialData then
sequence {origins,data} = genECData()
{Px, Py, Pc} = columnize(data)
{Cx, Cy} = columnize(origins)
else
for i=1 to N do {Px[i],Py[i]} = rand_xy() end for -- random set of points
for i=1 to K do {Cx[i],Cy[i]} = rand_xy() end for -- random set of cluster centroids
end if
end if
sequence {r,g,b} @ = repeat(0,w*h)
Voronoi()
bool change := Centroid()
for i=1 to N do
integer idx = Px[i]+(Py[i]-1)*w
{r[idx],g[idx],b[idx]} = cdDecodeColor(colours[Pc[i]])
end for
for i=1 to K do
integer idx = Cx[i]+(Cy[i]-1)*w
{r[idx],g[idx],b[idx]} = cdDecodeColor(CD_WHITE)
end for
cdCanvasPutImageRectRGB(cddbuffer, w, h, {r,g,b})
cdCanvasFlush(cddbuffer)
if change then
iteration += 1
IupSetStrAttribute(dlg, "TITLE", "%s (iteration %d)",{TITLE,iteration})
else
IupSetInt(timer,"RUN",0) -- (stop timer)
IupSetStrAttribute(dlg, "TITLE", TITLE)
end if
return IUP_DEFAULT
end function
function timer_cb(Ihandle /*ih*/)
IupUpdate(canvas)
return IUP_IGNORE
end function
function map_cb(Ihandle ih)
cdcanvas = cdCreateCanvas(CD_IUP, ih)
cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
return IUP_DEFAULT
end function
function esc_close(Ihandle /*ih*/, atom c)
if c=K_ESC then return IUP_CLOSE end if
if c=K_F5 then
iteration = 0
IupSetInt(timer,"RUN",1) -- (restart timer)
end if
return IUP_CONTINUE
end function
procedure main()
IupOpen()
canvas = IupCanvas(NULL)
IupSetAttribute(canvas, "RASTERSIZE", "640x480")
IupSetCallback(canvas, "MAP_CB", Icallback("map_cb"))
IupSetCallback(canvas, "ACTION", Icallback("redraw_cb"))
timer = IupTimer(Icallback("timer_cb"), 100)
dlg = IupDialog(canvas,"DIALOGFRAME=YES")
IupSetAttribute(dlg, "TITLE", TITLE)
IupSetCallback(dlg, "K_ANY", Icallback("esc_close"))
IupShow(dlg)
IupSetAttribute(canvas, "RASTERSIZE", NULL)
IupMainLoop()
IupClose()
end procedure
main()