3

I have two sets of data, one is coordinates of machines, one is coordinates of the nearest repair shop.

I have a working model that has assigned each machine to the nearest store. However one store only has 1 machine and another has 7 machines assigned to it.

What I want is to add a condition so that each store is assigned at least 2 machines but no more than 4.

library(geosphere)
library(ggplot2)
#machine Locations
machine.x <- c(-122.37, -111.72,    -111.87,    -112.05,    -87.17, -86.57, -86.54, -88.04, -86.61, -88.04, -86.61)
machine.y <- c(37.56,   35.23,  33.38,  33.57,  30.36,  30.75,  30.46,  30.68,  30.42,  30.68,  30.42)
machines <- data.frame(machine.x, machine.y)
#store locations
store.x <- c(-121.98, -112.17, -86.57)
store.y <- c(37.56, 33.59, 30.75)
stores <- data.frame(store.x, store.y)


centers<-data.frame(x=stores$store.x, y=stores$store.y)
pts<-data.frame(x=(machines$machine.x), y=(machines$machine.y))

#allocate space
distance<-matrix(-1, nrow = length(pts$x), ncol= length(centers$x))
#calculate the dist matrix - the define centers to each point
#columns represent centers and the rows are the data points
dm<-apply(data.frame(1:length(centers$x)), 1, function(x){ replace(distance[,x], 1:length(pts$x), distGeo(centers[x,], pts))})

#find the column with the smallest distance
closestcenter<-apply(dm, 1, which.min)


#color code the original data for verification
colors<-c(stores)
#create a scatter plot of assets color coded by which fe they belong to
plot(pts, col=closestcenter, pch=9)

enter image description here

So what I want is for each group to have a minimum count of 2 and a max count of 4, I tried adding a if else statement in the closest center variable but it didn't get even close to working out the way I thought it would. and i've looked around on line but can't find any way to add a counting condition to the which.min statement.

Note:My actual data set has several thousand machines and over 100 stores.

10 Rep
  • 2,217
  • 7
  • 19
  • 33
Coopa
  • 213
  • 1
  • 12
  • In your actual data, if there are only 100 stores, and a max of 4 machines per store, then that only allows for 400 machines. What do you expect for the output for the thousands of other machines? – AidanGawronski Jan 09 '18 at 22:48
  • Yes, you are correct that these parameters would not work in my actual data set. I'm looking for a general idea of how to solve this problem and/or code that works in my example that I posted. I posted the note so people could keep in mind that the concept needs to work on different scales other than the example I posted. – Coopa Jan 09 '18 at 23:06

1 Answers1

3

If M is an 11 x 3 zero-one matrix where M[i,j] = 1 if machine i is assigned to store j and 0 otherwise then the rows of M must each sum to 1 and the columns must each sum to 2 to 4 inclusive and we want to choose such an M which minimizes the sum of the distances sum(M * dm), say. This would give us the 0-1 linear program shown below. Below A is such that A %*% c(M) is the same as rowSums(M). Also B is such that B %*% c(M) is the same as colSums(M).

library(lpSolve)

k <- 3
n <- 11

dir <- "min"
objective.in <- c(dm)
A <- t(rep(1, k)) %x% diag(n)
B <- diag(k) %x% t(rep(1, n))
const.mat <- rbind(A, B, B)
const.dir <- c(rep("==", n), rep(">=", 3), rep("<=", 3))
const.rhs <- c(rep(1, n), rep(2, k), rep(4, k))
res <- lp(dir, objective.in, const.mat, const.dir, const.rhs, all.bin = TRUE)

res
## Success: the objective function is 9025807 

soln <- matrix(res$solution, n, k)

and this solution:

> soln
      [,1] [,2] [,3]
 [1,]    1    0    0
 [2,]    1    0    0
 [3,]    0    1    0
 [4,]    0    1    0
 [5,]    0    1    0
 [6,]    0    0    1
 [7,]    0    0    1
 [8,]    1    0    0
 [9,]    0    0    1
[10,]    0    1    0
[11,]    0    0    1

or in terms of the vector of store numbers assigned to each machine:

c(soln %*% (1:k))
## [1] 1 1 2 2 2 3 3 1 3 2 3
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • I know this is super old, but I can't figure out what const.rhs is. or more specifically what it is doing. I know its the Right Hand Side Constraint but what does rep(2,k) do? – Coopa Mar 10 '18 at 02:40
  • 1
    Try `help("rep")` and make sure you try all the examples on that page as well. – G. Grothendieck Mar 10 '18 at 02:49
  • Awesome, that helps alot! Thank you! I thought knowing this would solve my :Error in lp(dir, objective.in, const.mat, const.dir, const.rhs, all.bin = TRUE) : (list) object cannot be coerced to type 'double', However it has not resolved that error. I'm going to try and create a reproducible example and post as a new question. Thank you. – Coopa Mar 10 '18 at 03:10