Search code examples
rmatrixcoordinate-systemsr-gridhexagonal-tiles

How can I "natively" store hexagonal grids in R?


If I want to save some square grid in R, that's easily done.

Say,

|    | 0    | 1     | 2     |
|----|------|-------|-------|
| 0  | TRUE | TRUE  | FALSE |
| 1  | NA   | FALSE | TRUE  |
| 2  | TRUE | TRUE  | FALSE |

is stored as cartesian coordinate system

m <- matrix(data = c(TRUE, TRUE, FALSE, NA, FALSE, TRUE, TRUE, TRUE, FALSE), nrow = 3, ncol = 3, byrow = FALSE)

and existing methods and math work as expected:

apply(X = m, MARGIN = 2, FUN = "sum")
# [1]  2 NA  2
print(m)
#      [,1]  [,2]  [,3]
#[1,]  TRUE    NA  TRUE
#[2,]  TRUE FALSE  TRUE
#[3,] FALSE  TRUE FALSE

(I know I can also reshape2::melt this into long form, but I like it wide, because that's what the UI looks like).

So far so good, familiar, intuitive.


Now enter the hexagonal grid.

some grid

I understand from Amit Patel's marvellous/authoritative introduction to hex grids that I really ought to save such a grid using a cube coordinate system, as in the above example, because otherwise (= with 2d cartesian coordinates plus offsets) linear algebra operations don't work anymore, and general code grossness ensues. I get that (I think).

Cube coordinate system

(For more details, again, see Amit Patel's fantastic explainer).

Entering this data in wide form via array() seems completely bonkers, because many cells don't even exist (and NA is already used). So I enter/store this in long form, like so:

df <- rbind(c(1, 0, -1, FALSE), 
        c(0, 1, -1, NA),
        c(1, -1, 0, TRUE),
        c(0, 0, 0, TRUE),
        c(-1, 1, 0, FALSE),
        c(0, -1, 1, NA),
        c(-1, 0, 1, TRUE))
colnames(df) <- c("y", "x", "z", "value")
df
#      y  x  z value
#[1,]  1  0 -1     0
#[2,]  0  1 -1    NA
#[3,]  1 -1  0     1
#[4,]  0  0  0     1
#[5,] -1  1  0     0
#[6,]  0 -1  1    NA
#[7,] -1  0  1     1

This dataframe has all the data, but doesn't "know" in any way, that x, y and z are diagonal coordinates.

How can I:

  1. store this in a wide form where I can easily use linear and matrix algebra,
  2. use established methods (say, colSums(), or apply())
  3. as well as conveniently print to something like this:

screenshot

(SE won't even properly highlight this printed hex grid, hence the screenshot.)

What, in short, would be the elegant / recommended / canonical way to "natively" store such a hexagonal grid in R?

I am vaguely aware that I could probably implement my own S3 OO for this, though I was hoping that this might already exist in some form or another. I did find lots of packages that do hex binning on continuous data, but they didn't seem to deal with storing hex grids, or at least didn't expose those internals.


Solution

  • 1. store this in a wide form where I can easily use linear and matrix algebra

    Probably, the axial coordinates mentioned in http://www.redblobgames.com/grids/hexagons/#coordinates are the best choice here. This could give the option to convert to a standard ij-matrix with i=x and j=y, you just have to specify what to put into the matrix-cells which are not covered by your hex-grid (your own "NA"). Then you can operate along the dimension x and y as usual and have a matrix with just empty triangular upper right and lower left triangles. (This is not bonkers, but the natural way. At best you have to find a different solution for your NA in the your data. Sorry for not fiddling out the code for your example.)

    2. use established methods (say, colSums(), or apply())

    Using the dplyr package you can

    df %>% as_data_frame() %>% group_by(x) %>% summarize(sum(value))

    to sum for fixed x or the like.

    3. as well as conveniently print

    I don't have a solution for print, but displaying labels with ggplot2 can be a solution

    df %>% as_data_frame() %>% ggplot(aes(x=x+0.5*y, y=y, label=value)) + geom_text()

    Notes: The result provided here is a flipped version of what you wanted to have printed, but I hope you can find the right flip yourself. x=x+0.5*y serves here to give you the hex shape you are aiming at. With x=x you just have a skew hexagon.

    What, in short, would be the elegant / recommended / canonical way to "natively" store such a hexagonal grid in R?

    You found it already. For computation you use your df. For storage it is a bit more efficient to use the axial one. You can extract the z coordinate back easily.