Piet93 Piet93 - 23 days ago 5x
R Question

Vectorize calculation of cell number in a hexagonal grid

I know the use of for-loop in R is often unnecessary, because it supports vectorization. I want to program as efficient as possible, there for my question concerning the following example code.

I have a hexagonal grid, and I am calculating the number of the cell, this counts from 1 to 225 in my example starting in the left lower corner, going to the right. So cell 16 is placed a bit offset right above cell 1.
see snapshot:
example of grid I'm working with

Therefor, if I have the Y coordinate, the X coordinate has to be either rounded, or ceiling. In my application the user points out cells, I save this and in a for loop go through the cells to determine the cells he chose as follows, with toy input values for Xcells and Ycells the user would have chosen:

gridsize <- 15

Xcells <-c(0.8066765, 1.8209879, 3.0526517, 0.5893240)
Ycells <-c(0.4577802, 0.4577802, 0.5302311, 1.5445425)

clicks <- length(Xcells)
cells <-vector('list', clicks)

This corresponds to cell 1 2 3 and 16. 4 clicks. Now to determine the cell numbers:

Y <- ceiling(Ycells)
for(i in 1:clicks){
X[i] <- round(Xcells[i])
X[i]<- ceiling(Xcells[i])

#determine the cell numbers and store in predefined list
cells[[i]] <- (Y[i]-1)*gridsize + X[i]

So if the Y is 'even' the X has to be rounded, and if the Y is 'un-even' it has to be the ceiling value.

Is there a way to do this without the for loop, by using the vectorization?


You can vectorize this as follows

(Y - 1) * gridsize + ifelse(Y %% 2 == 1, round(Xcells), ceiling(Xcells))
# [1]  1  2  3 16

(I'm not sure pre-calculating round(Xcells) and ceiling(Xcells) will improve this a bit more - you could try)

Another option (if you want to avoid ifelse) could be

(Y - 1) * gridsize + cbind(ceiling(Xcells), round(Xcells))[cbind(1:length(Xcells), Y %% 2 + 1)]
# [1]  1  2  3 16