Reputation: 802
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.
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.
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
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