efficient way to find neighbors to specified cell? - r

Looking for sensible code to solve the following problem without a stack of "if" comparisons:
dput(acell)
structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0), .Dim = c(5L, 5L))
dput(bcell)
structure(c(0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
1, 0, 1, 0, 0, 0, 0, 1), .Dim = c(5L, 5L))
It's guaranteed by construction (elsewhere in my code) that there is at least one nonzero entry in bcell in the four locations left,right,up,down one cell from the location of the nonzero value ([2,3]) in acell . Is there some better way to return the indices of all such nonzero cells? I'm currently using (pseudocode), for coords i,j referring to the nonzero element of acell ,
if( bcell[i-1,j]>0 ) found_values<-rbind(found_values,c(i-1,j))
if (bcell[i+1,j]>0 ) found_values<-rbind(found_values,c(i+1,j))
and so on.

Another possibility
# index of non-zero in acell
id <- which(acell == 1, arr.ind = TRUE)
# index of neighbors
rows <- id[1] + c(0, 0, -1, 1)
cols <- id[2] + c(-1, 1, 0, 0)
idx <- cbind(rows, cols)
# values of neighbors in bcell
vals <- bcell[idx]
# index of non-zero neighbors
idx[vals != 0, ]
# rows cols
# [1,] 2 2
# [2,] 3 3
Update: An alternative
# index of non-zero in acell
id <- which(acell == 1, arr.ind = TRUE)
id
# create a matrix with cells adjacent to 'id'
# 'non-neighbor' cells are multiplied by zero
m <- matrix(c(0, 1, 0,
1, 0, 1,
0, 1, 0), ncol = 3) *
bcell[id[1] + (-1:1), id[2] + (-1:1)]
# index of non-zero neighbours
idx <- which(m != 0, arr.ind = TRUE)
# 'de-center' the centered indices
idx2 <- sapply(1:2, function(x) id[x] + (idx[ , x] - 2))
colnames(idx2) <- c("rows", "cols")
idx2
# rows cols
# [1,] 2 2
# [2,] 3 3

Here is a start:
dim <- dim(acell)
lookfor <- which(acell != 0)
lookup <- which(bcell != 0)
if (!((lookfor %% dim[1]) %in% c(1L,0L)) &
!((lookfor %% dim[2]) %in% c(1L,0L)) ) {
neighborsR <- lookup[abs(lookfor-lookup) == 1L]
neighborsC <- lookup[(lookfor %% dim[1] == lookup %% dim[1]) &
abs(lookfor %/% dim[1] - lookup %/% dim[1]) == 1L]
}
neighbors <- c(neighborsR, neighborsC)
res <- cbind(neighbors %% dim[1], neighbors %/% dim[1]+1)
colnames(res) <- c("row", "col")
# row col
#[1,] 3 3
#[2,] 2 2
This can only handle one lookfor value, which is not in the first/last row/column.

neighbors <- function(mat) {
nr <- nrow(mat)
nc <- ncol(mat)
ones <- which(mat == 1)
adjacent <- c(
Filter(function(x) x %% nr != 0, ones - 1) ## above
, Filter(function(x) x %% nr != 1, ones + 1) ## below
, ones - nr ## left
, ones + nr ## right
)
adjacent <- unique(Filter(function(x) x > 0 && x <= nr * nc, adjacent))
out <- matrix(FALSE, nr, nc)
out[adjacent] <- TRUE
out
}
which(neighbors(acell) & bcell, arr.ind = TRUE)
## rows cols
## [1,] 2 2
## [2,] 3 3

Related

How to include matrix multiplication in constraint?

I am trying to make run this model. I am trying to maximize:x[4]
w.r.t Mv = c(0,0,0,0)
lb < v < ub
But I have 2 problems, first matrix multiplication.
library(ompr)
lb <- c(-200, 0, -200, -200)
ub <- c(1000, 1000, 1000, 1000)
M <- matrix(rbind(
c(-1, 0, -1, 0), # A
c(-1, 0, 0, -2), # B
c(1, -2, 0, 0), # C
c(1, 0, 0, 2), # D
c(0, 2, -1, 0), # E
c(0, 0, 1, -1) # F
), nrow = 6)
n <- 4
rhs <- rep(0, n)
model <- MIPModel() %>%
add_variable(x[i], i = 1:n, type = "continuous") %>%
set_objective(x[4]) %>%
add_constraint(M[i, ] %*% x == rhs[i], i = 1:n)
I got the following error.
Error in M[i, ] %*% x : requires numeric/complex matrix/vector
arguments
Second, I am trying to set the bounds in a vectorized way, but I don't know how to do that. I tried the following:
set_bounds(x[i], ub = ub[i], lb = lb[i], i = 1:n)
This gives:
object 'i' not found
Any help would be very useful!
Works like this, but the solution is (0, 0, 0, 0):
library(ompr)
library(ompr.roi)
library(ROI.plugin.glpk)
library(magrittr)
lb <- c(-200, 0, -200, -200)
ub <- c(1000, 1000, 1000, 1000)
M <- matrix(rbind(
c(-1, 0, -1, 0), # A
c(-1, 0, 0, -2), # B
c(1, -2, 0, 0), # C
c(1, 0, 0, 2), # D
c(0, 2, -1, 0), # E
c(0, 0, 1, -1) # F
), nrow = 6)
n <- 4
rhs <- rep(0, n)
model <- MIPModel() %>%
add_variable(x[i], i = 1:n, type = "continuous") %>%
set_objective(x[4], "max") %>%
add_constraint(sum_over(M[i, j] * x[j], j = 1:4) == rhs[i], i = 1:n) %>%
add_constraint(x[i] <= ub[i], i = 1:n) %>%
add_constraint(x[i] >= lb[i], i = 1:n) %>%
solve_model(with_ROI(solver = "glpk"))
get_solution(model, x[i])

constrOptim initial values

I'm attempting to use the constrOptim() function in R to optimise:
2x + 2y + 3z
subject to:
-2x + y + z <= 1
4x - y + 3z <= 3
x, y, z >= 0
What I have so far is this:
ui = matrix(c(2,-1,-1,-4, 1,-3, 1, 0, 0, 0, 1, 0, 0, 0, 1),
nrow = 5,
byrow = T)
ci = c(-1, -3, 0, 0, 0)
theta = c(0, 1, 0)
constrOptim(
theta = theta,
f = func,
ui = ui,
ci = ci)
This gives me the error that "initial value is not in the interior of the feasible region". However, if I run the following as a test:
ui = matrix(c(2,-1,-1,-4, 1,-3, 1, 0, 0, 0, 1, 0, 0, 0, 1),
nrow = 5,
byrow = T)
ci = c(-1, -3, 0, 0, 0)
theta = c(0, 1, 0)
ui %*% theta - ci
I get (0 4 0 1 0), which is definitely >=0.
My question is why do I get an error telling me that ui %*% theta - ci is not >= 0, when it clearly is? What am I missing?
Edit: managed to sort it out thanks to Stéphane Laurent.
Any ideas how best to plot the feasible region in R? Any useful packages?
The starting value must be in the interior of the feasible region, so you need > 0 and not >= 0. You can use theta = c(0.1, 1, 0.1)
ui = matrix(c(2,-1,-1,-4, 1,-3, 1, 0, 0, 0, 1, 0, 0, 0, 1),
nrow = 5,
byrow = T)
ci = c(-1, -3, 0, 0, 0)
theta = c(0.1, 1, 0.1)
all(ui %*% theta - ci > 0) # TRUE
constrOptim(
theta = theta,
f = function(xyz) c(crossprod(c(2,2,3), xyz)),
grad = NULL,
ui = ui,
ci = ci)
BTW, it seems obvious to me that the solution is c(0,0,0).
The cause of error has been explained from the answer by Stéphane Laurent.
An alternative of constrOptim is to use fmincon from package pracma, and you can run the code without any error even with initial values on the boundary, i.e., theta = c(0,1,0)
ui = matrix(c(2,-1,-1,-4, 1,-3, 1, 0, 0, 0, 1, 0, 0, 0, 1),
nrow = 5,
byrow = T)
ci = c(-1, -3, 0, 0, 0)
theta = c(0, 1, 0)
func <- function(v) crossprod(c(2,2,3),v)
res <- pracma::fmincon(theta,
f = func,
A = -ui,
b = -ci)
such that
> res
$par
[1] 0 0 0
$value
[,1]
[1,] 0
$convergence
[1] 0
$info
$info$lambda
$info$lambda$lower
[,1]
[1,] 0
[2,] 0
[3,] 0
$info$lambda$upper
[,1]
[1,] 0
[2,] 0
[3,] 0
$info$lambda$ineqlin
[1] 0 0 2 2 3
$info$grad
[,1]
[1,] 2
[2,] 2
[3,] 3
$info$hessian
[,1] [,2] [,3]
[1,] 1 0.00 0
[2,] 0 0.03 0
[3,] 0 0.00 1

r Counting occurences of specific number between specific number

I have a matrix where each element is eather 0 or 1.
I would like to obtain the frequencies of consecutive occurences of 0's in each row, given the last 0 in the sequence is followed by a 1.
For example:
A row with: 0, 1 , 0, 1, 0, 0
The expected result should be:
Consecutive 0's of length: 1
Frequency : 2
Another row with: 0, 1, 0, 0, 1, 0, 0, 0, 1
The expected result:
Consecutive 0's of length: 1 2 3
Frequency: 1 1 1
A further objective is then to sum the frequencies of the same length in order to know how many times a single 0 was followed by a 1, two consecutive 0's where followed by a 1 etc.
Here is an exemplary matrix on which I would like to apply the routine:
m = matrix( c(1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 0, 1, 0, 0, 0, 0,
1, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 0,
1, 0, 0, 0, 0, 0, 1, 1, 0, 0),
ncol = 10, nrow = 6, byrow=TRUE)
The expected result should then be like the matrix below:
result = matrix( c(3, 0, 1, 0, 3, 0, 0, 0, 0, 0), ncol=10, nrow=1)
colnames(result) <- c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
Where the column names are the lengths of consecutive 0's (followed by a 1) and the matrix entries the corresponding frequencies.
Note that I have a very large data matrix, and if possible I'd like to avoid loops. Thanks for any hints, comments and propositions.
Using base functions. The complication is you are discarding trailing zeros that do not end with 1.
Explanation in line.
set.seed(13L)
numRows <- 10e4
numCols <- 10
m <- matrix(sample(c(0L, 1L), numRows*numCols, replace=TRUE),
byrow=TRUE, ncol = numCols, nrow = numRows)
#add boundary conditions of all zeros and all ones
m <- rbind(rep(0L, numCols), rep(1L, numCols), m)
#head(m)
rStart <- Sys.time()
lens <- unlist(apply(m, 1, function(x) {
#find the position of the last 1 while handling boundary condition of all zeros
idx <- which(x==1)
endidx <- if (length(idx) == 0) length(x) else max(idx)
beginidx <- if(length(idx)==0) 1 else min(idx)
#tabulate the frequencies of running 0s.
runlen <- rle(x[beginidx:endidx])
list(table(runlen$lengths[runlen$values==0]))
}))
#tabulating results
res <- aggregate(lens, list(names(lens)), FUN=sum)
ans <- setNames(res$x[match(1:ncol(m), res$Group.1)], 1:ncol(m))
ans[is.na(ans)] <- 0
ans
# 1 2 3 4 5 6 7 8 9 10
#100108 43559 18593 7834 3177 1175 387 103 0 106
rEnd <- Sys.time()
print(paste0(round(rEnd - rStart, 2), attr(rEnd - rStart, "units")))
#[1] "27.67secs"
Do let me know the performance after running on the large matrix.

Add columns of matrix to columns of other matrix in R

I have a simple question, but couldn't figure out a good solution. So hope, somebody can help :)
I want to add each column of a matrix B to a column of a matrix A, where an index vector specifies, to which column of A this column should be added to. So it is possible that more than one column of B is added to the same column of A. I want to accumulate these changes and not replace these.
This is a working solution with a for loop:
A <- matrix(0, ncol = 4, nrow = 4)
B <- matrix(c(1, 0, 0, 0, 2, 0, 1, 2, 0, 1, 1, 0), ncol = 3)
cols <- c(1, 2, 2)
for (i in seq_len(ncol(B))) {
A[, cols[i]] <- A[, cols[i]] + B[, i]
}
print(A)
I thought I could write this without a for loop using
A <- matrix(0, ncol = 4, nrow = 4)
B <- matrix(c(1, 0, 0, 0, 2, 0, 1, 2, 0, 1, 1, 0), ncol = 3)
cols <- c(1, 2, 2)
A[, cols] <- A[, cols] + B
print(A)
But this doesn't return the same matrix, because it adds the second column of B to the second column of A but then in the next step replaces this with the third column of B instead of adding both of the replacements together.
I'm looking for a fast and general solution, which works also for different index vectors and matrices.
This doesn't eliminate the loop, but it does make it shorter:
A <- matrix(0, ncol = 4, nrow = 4)
B <- matrix(c(1, 0, 0, 0, 2, 0, 1, 2, 0, 1, 1, 0), ncol = 3)
cols <- c(1, 2, 2)
for(i in unique(cols)){
A[, i] <- A[, i] + apply(as.matrix(B[, cols == i]), 1, sum)
}
print(A)
Try this
ans = A[,cols] + B
ans = sapply(split(1:NCOL(ans), cols), function(i) rowSums(ans[, i, drop = FALSE]))
inds = cbind(rep(1:NROW(A), length(unique(cols))), rep(unique(cols), each = NROW(A)))
replace(A, inds, ans[inds])
# [,1] [,2] [,3] [,4]
#[1,] 1 2 0 0
#[2,] 0 1 0 0
#[3,] 0 2 0 0
#[4,] 0 2 0 0

Creating a vector with certain values at specific positions based on another vector

If I start with vector1, and test to see which items equal 1:
vector1 <- c(0, 1, 1, 1, 0, 1, 1, 1, 0, 1)
test <- which(vector1 == 1)
test now equals: 2, 3, 4, 6, 7, 8, 10
then, I want to randomly choose two of the items in test:
sample_vector <- sample(test, 2, replace = FALSE)
the above code generated a sample_vector: 6, 3
My question is how do I take sample_vector and turn it into:
vector2 <- 0, 0, 1, 0, 0, 1, 0, 0, 0, 0
I'm essentially looking to assign only the items in sample_vector to equal 1, and the remaining items from vector1 are assigned to equal 0 (i.e. so it looks like vector2). vector2 needs to have the same length at vector1 (10 items).
Thanks!
vector2 <- rep(0, length(vector1))
vector2[sample_vector] <- 1
set.seed(44)
vector1 <- c(0, 1, 1, 1, 0, 1, 1, 1, 0, 1)
test <- which(vector1 == 1)
sample_vector <- sample(test, 2, replace = FALSE)
sample_vector
#[1] 8 3
replace(tabulate(seq_along(vector1)) - 1, sample_vector, 1)
#[1] 0 0 1 0 0 0 0 1 0 0
Use this code.
vector2 <- rep(0,len(vector1))
vector2[sample_vector] = 1

Resources