Reputation: 3
Folks, I'm probably making a mountain out of a molehill, but I'm stuck with something on R. Here is what I want to do: Solve a linear programming problem for multiple weights on x1 and x2, using a loop. The loop and the function work just fine, but I am unable to store the data in a vector and that's frustrating. Note: I am a beginner/novice in R
library(lpSolve)
# Set up problem to maximize
# a1*x1 + a2*x2
# subject to
# x1 + x2 <= 500
# 8x1 - 9x2 = 0
#Creating the constraint matrix
f.con <- matrix (c(1, 1, 8, -9), nrow=2, byrow=TRUE)
#Creating the vector for operators
f.dir <- c("<=", "=")
#Creating the Right hand side of the constraints in the above problem.
f.rhs <- c(500, 0)
#
# Now run the program
#
#Create vector to store maximized objective values
ans <- vector('numeric')
#Create loop sequences for a1 and a2
a1 <- seq(0.1,1,0.1)
a2 <- seq(0.1,1,0.1)
#Run for lop with lp function
for(i in a1){
for(j in a2){
if(i+j)==1{
ans[i] <- lp("max", c(i,j), f.con, f.dir, f.rhs)$objval
}
}
}
Nothing gets stored in ans, it should be a vector of length 10. What am I doing wrong? :(
Upvotes: 0
Views: 467
Reputation: 160447
There are a few things with your code:
as @MillionC pointed out, your conditional is syntactically wrong, it should have more enclosing parens as in if ((i+j)==1) {...
the first time lp(...)
is called, it is being assigned to ans[0.1]
, which is nonsensical, you probably need to iterate over the indices of a1
(and optionally a2
)
your pre-allocation of ans
is not necessarily wrong, but pre-allocating does very little unless you tell it its size; I suggest ans <- vector('numeric', length(a1))
or just ans <- numeric(length(a1))
perhaps you trivialized the example for the question, but just in case ... you do not need two loops, when your conditional allows you to know ahead of time which combinations of i
and j
are relevant; in this case, you only need j
when it is perfectly 1-i
, so you can remove a loop entirely.
You could use:
ans <- vector('numeric', length(a1))
for (indi in seq_along(a1)) {
for (indj in seq_along(a2)) {
if ((a1[indi]+a2[indj]) == 1) {
ans[indi] <- 99 + a1[indi]
}
}
}
ans
# [1] 99.1 99.2 99.3 99.4 99.5 99.6 99.7 99.8 99.9 0.0
or slightly better:
ans <- vector('numeric', length(a1))
for (indi in seq_along(a1)) {
ans[indi] <- 99 + a1[indi]
}
ans
# [1] 99.1 99.2 99.3 99.4 99.5 99.6 99.7 99.8 99.9 100.0
(I don't have lpSolve
, so I'm masquerading with "99".) The big difference here is the value of the last call: in your double-loop, there is never a value in a2
that when added to the a1
value of 1 equals 1. However, the second run does not check for that, so ... it does something. (Only you know which method is correct.)
Nested loops are not necessarily wrong, but in general I suggest you think about how to vectorize something. Instead of looping over things in ways you'd expect to in C or Java (and sometimes Python), think about doing things to a vector all at once. Sometimes this is as simple as replacing
vec <- 1:10
for (ind in seq_along(vec)) vec[ind] <- vec^2
to
vec <- 1:10
vec <- vec^2
as long as the function supports a vector as an input. When it is not supported or is not as simple as thing, then sapply
and lapply
are sort of like a for
loop but capturing each return value, so you can do something like
ans <- sapply(seq_along(a1), function(indi) 99 + a1[indi])
ans
# [1] 99.1 99.2 99.3 99.4 99.5 99.6 99.7 99.8 99.9 100.0
Care must be taken to not go too literally with this. For instance, converting your conditioned double-lool, we would get:
ans <- sapply(seq_along(a1), function(indi) {
sapply(seq_along(a2), function(indj) {
if ((a1[indi]+a2[indj]) == 1) {
99 + a1[indi]
} else {
# something here?
}
})
})
ans
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,] NULL NULL NULL NULL NULL NULL NULL NULL 99.9 NULL
# [2,] NULL NULL NULL NULL NULL NULL NULL 99.8 NULL NULL
# [3,] NULL NULL NULL NULL NULL NULL 99.7 NULL NULL NULL
# [4,] NULL NULL NULL NULL NULL 99.6 NULL NULL NULL NULL
# [5,] NULL NULL NULL NULL 99.5 NULL NULL NULL NULL NULL
# [6,] NULL NULL NULL 99.4 NULL NULL NULL NULL NULL NULL
# [7,] NULL NULL 99.3 NULL NULL NULL NULL NULL NULL NULL
# [8,] NULL 99.2 NULL NULL NULL NULL NULL NULL NULL NULL
# [9,] 99.1 NULL NULL NULL NULL NULL NULL NULL NULL NULL
# [10,] NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
... which is likely not correct for you. It is not hard to correct for this, but in general you probably want to run the loop only where needed, not 10x the runs.
In another extreme solution, you know the values of a2
that are relevant for each in a1
, then consider pre-allocating them and using a cousin of sapply
:
myfunc <- function(i, j) lp("max", c(i,j), f.con, f.dir, f.rhs)$objval
ans <- mapply(myfunc, a1, a2)
(I pre-defined myfunc
for demonstration purposes; it's legit and common to put the function(i,j)...
directly as the first argument to mapply
, as I did for the second argument to sapply
above.)
This is a "zipper" like function, where the calls are effectively
myfunc(i[1], j[1])
myfunc(i[2], j[2])
myfunc(i[3], j[3])
...
myfunc(i[10], j[10])
captured into a vector. This method might be more appropriate for you, where you know the pairs of arguments that need to be given as a vector to the second argument in lp(..., c(i,j), ...)
.
(Note: mapply
can take an arbitrary number of arguments, so it could also do
mapply(function(w,x,y,z) { ... },
list(...), list(...), list(...), list(...))
The only requirement is that each list is either the same length or length 1.)
Upvotes: 2
Reputation: 692
It looks like the ending parentheses of your if statement should be around the "1" and not the "j". Like:
if(i+j==1){
Upvotes: 0