Combining multiple confusion matrices - Tile plot with rectangles instead of tiles?

Viewed 78

I have several confusion matrices and I want to compare them. I thought it would be great to visualize the highest and lowest value for each category in a single plot. I imagine something like this (forgive me the ugly paint doodle):

what I want

For each category, there is an upper rectangle showing the highest value across all confusion matrices, the lower rectangle shows the lowest value across all confusion matrices. The color inidcates, which dataset the rectangle belongs to.

So, how can I achieve this? I figured I might bring it to work using geom_polygon(), but I never used that and I imagine it's quite some work. Is there a way which would allow me to use geom_tile() but to use triangles instead of rectangles? I saw some ggplots with rectangles which were horizontally divided into smaller rectangles, so I thought maybe there is a possibility to do triangles too?

Here would be some dummy data:

datasets <- c("Data Set 1", "Data Set 2", "Data Set 3", "Data Set 4")
df <- data.frame(
  "Truth" = c(rep(c("A","B","C","D"), 8)),
  "Prediction" = c(rep(c("A","B","C","D"), each = 8)),
  "Type" = c(rep(rep(c("min", "max"), each = 4), 4)),
  "Data_Set" = sample(datasets, 8*4, replace = TRUE),
  "Value" = round(runif(8*4)*100))

Making a single plot per maximum / minimum value would be easy. For example:

ggplot(df[df$Type == "min",]) +
  geom_tile(aes(x = Truth, y = Prediction, fill = Data_Set)) +
  geom_text(aes(x = Truth, y = Prediction, label = Value))

enter image description here

But I am looking for a pretty way to combine a tileplot for the maximum and the minimum. I am also open for other ideas. I guess many people wanted to compare several confusion matrices in one plot before me?

1 Answers

I can't think of a way to plot the triangles in ggplot2 without defining them as polygons, which requires a bit of data reshaping. If we start with your original data:

datasets <- c("Data Set 1", "Data Set 2", "Data Set 3", "Data Set 4")

set.seed(2)

df <- data.frame(
  "Truth" = c(rep(c("A","B","C","D"), 8)),
  "Prediction" = c(rep(c("A","B","C","D"), each = 8)),
  "Type" = c(rep(rep(c("min", "max"), each = 4), 4)),
  "Data_Set" = sample(datasets, 8*4, replace = TRUE),
  "Value" = round(runif(8*4)*100))

Now get the centre co-ordinates of each tile by converting factors to numbers, and add a column that records the original row number:

df$x <- as.numeric(factor(df$Truth))
df$y <- as.numeric(factor(df$Prediction))
df$observation <- seq(nrow(df))

Now make three copies of each row:

df <- df[rep(seq(nrow(df)), each = 3),]

And we can calculate the co-ordinates of the triangles' vertices:

polyxmin <- df$x + c(-0.5, 0.5, 0.5)
polyxmax <- df$x + c(-0.5, -0.5, 0.5)
polyymin <- df$y + c(-0.5, -0.5, 0.5)
polyymax <- df$y + c(-0.5, 0.5, 0.5)

df$polyx <- numeric(nrow(df))
df$polyx[df$Type == "min"] <- polyxmin[df$Type == "min"]
df$polyx[df$Type == "max"] <- polyxmax[df$Type == "max"]

df$polyy <- numeric(nrow(df))
df$polyy[df$Type == "min"] <- polyymin[df$Type == "min"]
df$polyy[df$Type == "max"] <- polyymax[df$Type == "max"]

And add locations for the text:

df$x[df$Type == "min"] <- df$x[df$Type == "min"] + 0.25
df$x[df$Type == "max"] <- df$x[df$Type == "max"] - 0.25
df$y[df$Type == "min"] <- df$y[df$Type == "min"] - 0.25
df$y[df$Type == "max"] <- df$y[df$Type == "max"] + 0.25

Then we can plot with geom_polygon:

ggplot(df) +
  geom_tile(aes(x = Truth, y = Prediction, fill = Data_Set)) +
  geom_polygon(aes(x = polyx, y = polyy, group = observation, fill = Data_Set),
               color = "gray30") +
  geom_text(aes(x = x, y = y, label = Value), check_overlap = TRUE) +
  scale_fill_manual(values = c("#a7ca88", "#b4c7e7", "#f4b184", "#fdda65"))
  coord_equal()

enter image description here

Related