AWaddington
AWaddington

Reputation: 802

Create a Space Adjacency Matrix in ggplot

I wish to create a Space Adjacency Matrix in R, preferably using ggplot/tidyverse for consistency with other scripts, but I am open to other solutions.

Here is what I am looking for.

A Space Adjacency Matrix is used in interior and architectural design to illustrate relationships (adjacencies) between spaces within a building.

Each space in the building has a relationship (or lack of relationship) to every other space.

The input data is likely formatted similarly to this:

rel.ABCD <- data.frame(
  id = c(1,2,3,4,5,6),
  x1 = c("A","A","A","B","B","C"),
  x2 = c("B","C","D","C","D","D"),
  relation = c(1,2,1,3,2,1)
)

rel.ABCD
#>   id x1 x2 relation
#> 1  1  A  B        1
#> 2  2  A  C        2
#> 3  3  A  D        1
#> 4  4  B  C        3
#> 5  5  B  D        2
#> 6  6  C  D        1

Created on 2022-04-11 by the reprex package (v2.0.1)

Four spaces (A, B, C and D) exist in the example above. Each has a type of relation with the other spaces. Spaces A and B (id 1) have a relation type of 1, spaces B and C (id 4) have a relation type of 3, etc.

In some ways, the Space Adjacency Matrix is similar to a correlation table (in format, not function), matching lists of entities intersect and the value for the relationship is shown at the intersection. The difference is that instead of labels existing on both the x-axes and y-axes, they exist on the y-axes only, like in the example below from vectorworks.net.

sample space adjacency matrix from vectorworks

The relation is displayed at the intersecting grid for each room pair (e.g. dispatch and office have a relationship type 5, storage and shop have a relationship type 1, etc.). The relation is typically depicted as an icon, number or fill colour.

How can I generate this graph?

Upvotes: 5

Views: 376

Answers (1)

teunbrand
teunbrand

Reputation: 37953

I don't know of any package that implements this. But it is good to keep in mind that you can basically plot anything in ggplot2, as long as you can translate what you're plotting to polygons. That said, here is how you can translate this particular problem to polygons.

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.1.3

rel.ABCD <- data.frame(
  id = c(1,2,3,4,5,6),
  x1 = c("A","A","A","B","B","C"),
  x2 = c("B","C","D","C","D","D"),
  relation = c(1,2,1,3,2,1)
)

# Encode categorical values as numeric
union <- with(rel.ABCD, union(x1, x2))
rel.ABCD <- transform(
  rel.ABCD,
  x = match(x1, union),
  y = match(x2, union)
)

# Expand observation to rectangle polygons
new <- rel.ABCD[rep(seq_len(nrow(rel.ABCD)), each = 4),]
xpand <- c(-1, -1, 1, 1) * 0.5
ypand <- c(-1, 1, 1, -1) * 0.5
new <- transform(
  new,
  x = x + xpand,
  y = y + ypand
)

# Rotate coordinates 45 degrees
rotmat <- matrix(c(-0.5, 0.5, 0.5, 0.5), ncol = 2)
new[, c("x", "y")] <- t(rotmat %*% t(as.matrix(new[, c("x", "y")])))

# Plot
ggplot(new, aes(x, y, group = id)) +
  geom_polygon(aes(fill = factor(relation))) +
  scale_y_continuous(breaks = seq_along(union),
                     labels = union) +
  coord_equal()

If you want the labels more like your example, you can also coerce these to polygons.

# Make dataframe for labels
labels <- data.frame(
  label = union,
  x = 0, y = seq_along(union)
)

# Write offset for label polygons
size <- 1
xoffset <- c(-size, -size, 0, 0.5, 0)
yoffset <- c(-0.5, 0.5, 0.5, 0, -0.5)

# Expand every label to a polygon
labels <- labels[rep(seq_len(nrow(labels)), each = 5), ]
labels <- transform(
  labels,
  x = x + xoffset,
  y = y + yoffset
)

ggplot(new, aes(x, y)) +
  geom_polygon(aes(fill = factor(relation), group = id), 
               colour = "black") +
  geom_polygon(data = labels, aes(group = label), 
               colour = "black", fill = NA) +
  annotate(
    "text", 
    x = 0, y = seq_along(union), label = union,
    hjust = 1
  ) +
  coord_equal() +
  guides(x = "none", y = "none")

Created on 2022-04-11 by the reprex package (v2.0.1)

Upvotes: 6

Related Questions