5

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){
        if(Y[i]%%2==1){
           X[i] <- round(Xcells[i])
        }
      else{
         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?

Henrik
  • 65,555
  • 14
  • 143
  • 159
Piet93
  • 169
  • 1
  • 13
  • You haven't defined `X` anywhere. Except for that, kudos for the nice question and for not asking for a dplyr solution rather addressing the problem itself- 1 in a million on SO these days... – David Arenburg Sep 28 '16 at 06:18
  • i think this reference may help: http://www.redblobgames.com/grids/hexagons/ – baptiste Sep 28 '16 at 06:39
  • @DavidArenburg, is this necessary? Not sure if I'm allowed to say it this way but: doesn't X define itself in the statement `X[i] <- round(..)` – Piet93 Sep 28 '16 at 06:54
  • `X[i] <- round(..)` won't work if you won't predefine `X <- ceiling(Xcells)` like you did with `Y`. So not sure what you mean by *necessary* – David Arenburg Sep 28 '16 at 07:30
  • Well, Xcells is predefined, and X is assigned as the round/ceiling of Xcells in the example I posted. I agree not very subtle but it does "work" (I mean I get a vector of X at the end... ). But of course the reason I post this question is to **not** do it this way. Thank you for the help David. – Piet93 Sep 28 '16 at 08:26

1 Answers1

2

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
David Arenburg
  • 91,361
  • 17
  • 137
  • 196
  • I actually like the `ifelse` here, it looks really clean and you can see what this line of code is supposed to do very easily (for less experienced coders I think that is an advantage). Thanks. – Piet93 Sep 28 '16 at 06:58
  • 1
    `ifelse` is indeed clean but it has [some issues](http://stackoverflow.com/questions/16275149/does-ifelse-really-calculate-both-of-its-vectors-every-time-is-it-slow). I'm trying to avoid it whenever I can – David Arenburg Sep 28 '16 at 07:36