Weiszfeld's Algorithm in R
R code to run Weiszfeld's Algorithm. This algorithm finds a central tendency point for a set of weighted points. It includes a simple animation of the algorithms movement towards the central point.
#Weiszfeld's Algorithm in [R]
# See Francis, R.L., et al, "Facility Layout and Location: an analytical approach",
# Prentice-Hall for algorithm reference
# Find the center of mass for a network of weighted points
# INPUT: a data frame (a,b,w) = a (x) coordinate, b (y) coordinate, weight
# iters = number of iterations (default to 25)
# start = search start point (will default)
# Example weiszfeld(x,iters=20,start=c(a,b))
# OUTPUT: matrix of x,y,f estimates
# The f is the minimized sum, x and y are estimate coordinates.
weiszfeld = function(dat=NULL, iters=25, start=NULL,speed=0.1){
if(is.null(dat)){
### REMOVE this block
cat("Null data. Running in example mode.\n")
mat = matrix(c(2,5,4,1,3,7,8,8,1,1,1,2),nrow=4,ncol=3)
dat = as.data.frame(mat)
names(dat)=c("a","b","w")
}
# check dat col names
cols = c("a","b","w")
for(i in 1:3){
if(cols[i] != names(dat)[i]){
stop("Column names for input dataframe must be: a (x coord), b (y coord), w (weight)")
}
}
if(is.null(start)){
# guess a start value
start=c(mean(dat$a),mean(dat$b))
}
# Make sure you're not starting on top an existing point
go=TRUE
while(go==TRUE){
# Make sure the start point does not duplicate a point in the list.
o = subset(dat,a==start[1] && b==start[2])
if(nrow(o)==0){
go=FALSE
} else {
start[1]=start[1]+0.01
start[2]=start[2]+0.01
}
}
gamma.i = function(x,y,a,b,w){
out = w/sqrt((x-a)^2+(y-b)^2)
return(out)
}
cap.gamma = function(dat,start ){
out = 0
for(i in 1:nrow(dat)){
row = dat[i,]
out = out + gamma.i(start[1],start[2],row$a,row$b,row$w)
}
return(out)
}
calc.f = function(start,dat){
out = 0
for(i in 1:nrow(dat)){
row = dat[i,]
out = out + (row$w*sqrt((start[1]-row$a)^2+(start[2]-row$b)^2))
}
return(out)
}
ret=data.frame(matrix(0,nrow=iters,ncol=3))
names(ret)=c("x","y","f")
j = 1
while(iters > 0){
print(start)
plot(dat$a,dat$b)
points(start[1],start[2],pch="x")
Gamma = cap.gamma(dat,start)
new.x = 0
new.y = 0
for (i in 1:nrow(dat)){
row = dat[i,]
new.x=new.x + ((gamma.i(start[1],start[2],row$a,row$b,row$w)*row$a)/Gamma)
new.y=new.y + ((gamma.i(start[1],start[2],row$a,row$b,row$w)*row$b)/Gamma)
}
# calculate f
ret[j,]=c(start[1], start[2], (calc.f(start,dat)))
start[1]=new.x
start[2]=new.y
iters=iters-1
j = j+1
Sys.sleep(speed)
}
return(ret)
}