I had exactly the same problem and I managed to figure it out with some small hacks. This question seem the most relevant to post the answer. As for now, Plotly won't let you do that explicitly, that is to specify exact colors to categorical (discrete) heatmap values. This is documented in this github issue. A pure JS solution is linked there and it took me a while to reproduce it in R (using Plotly 4.7.1).
First, instead of one matrix holding all the data, you need to have one matrix per category, each filled with ones and NA's. Each matrix must be the same size. Let's make up some data.
greenSeriesData <- matrix(c(1,1,NA,NA,1,NA,NA,NA,1), nrow = 3)
redSeriesData <- matrix(c(NA,NA,1,1,NA,NA,NA,NA,NA), nrow = 3)
blueSeriesData <- matrix(c(NA,NA,NA,NA,NA,1,1,1,NA), nrow = 3)
Then for each category you need to have a separate data.frame holding color for given category. What's important is that those data.frames needs to have no colnames.
greenColor <- data.frame(x = c(0,1), y = c("#63a803", "#63a803"))
colnames(greenColor) <- NULL
redColor <- data.frame(x = c(0,1), y = c("#a80b03", "#a80b03"))
colnames(redColor) <- NULL
blueColor <- data.frame(x = c(0,1), y = c("#035da8", "#035da8"))
colnames(blueColor) <- NULL
And you're ready to plot specifying each category as a separate trace:
plot_ly(
type = "heatmap"
) %>% add_trace(
z = greenSeriesData,
colorscale = greenColor
) %>% add_trace(
z = redSeriesData,
colorscale = redColor
) %>% add_trace(
z = blueSeriesData,
colorscale = blueColor
)
If you want to further adjust the legend to look good, you would need to add a colorbar
argument to each trace:
plot_ly(
type = "heatmap"
) %>% add_trace(
z = greenSeriesData,
colorscale = greenColor,
colorbar = list(
len = 0.3,
y = 1,
yanchor = 'top',
title = 'Green series',
tickvals = ''
)
) %>% add_trace(
z = redSeriesData,
colorscale = redColor,
colorbar = list(
len = 0.3,
y = 1,
yanchor = 'top',
title = 'Red series',
tickvals = ''
)
) %>% add_trace(
z = blueSeriesData,
colorscale = blueColor,
colorbar = list(
len = 0.3,
y = 1,
yanchor = 'top',
title = 'Blue series',
tickvals = ''
)
)