Saturday, July 15, 2017

Retain legend and square aspect ratio in ggplotly()

Leave a Comment

I am trying to create a plot that contains a legend and a "square" shape with equal aspect ratio. I was able to achieve this in the "p" object in the code below using ggplot2(). However, when I ran ggplotly() on the "p" object, the legend disappeared and the "square" shape with equal aspect ratio also disappeared. Below, I show two images showing the difference. The left image shows the "p" object with the legend and an equal aspect ratio. The x=y line in red perfectly intersects the bottom-left and top-right corners of the image. The right image shows the ggplotly(p) output where the legend is gone and the square aspect ratio is also gone. The x=y line no longer perfectly intersects the bottom-left and top-right corners of the image.

Comparison of the two plots

My MWE code is included below:

library(hexbin) library(ggplot2) library(plotly) set.seed(1) dat <- data.frame(ID = paste0("ID", 1:1010), A.1 = c(rep(0.5, 1000), abs(rnorm(10))), A.2 = c(rep(0.5, 1000), abs(rnorm(10))), B.1 = c(rep(0.5, 1000), abs(rnorm(10))), B.2 = c(rep(0.5, 1000), abs(rnorm(10))), C.1 = c(rep(0.5, 1000), abs(rnorm(10))), C.2 = c(rep(0.5, 1000), abs(rnorm(10))), C.3 = c(rep(0.5, 1000), abs(rnorm(10))), stringsAsFactors = FALSE )  sampleIndex <- which(sapply(colnames(dat), function(x) unlist(strsplit(x,"[.]"))[1]) %in% c("A", "C")) datSel <- dat[,c(1, sampleIndex)]  sampleIndex1 <- which(sapply(colnames(datSel), function(x) unlist(strsplit(x,"[.]"))[1]) %in% c("A")) sampleIndex2 <- which(sapply(colnames(datSel), function(x) unlist(strsplit(x,"[.]"))[1]) %in% c("C")) minVal = min(datSel[,-1]) maxVal = max(datSel[,-1]) maxRange = c(minVal, maxVal) xbins= 10 buffer = (maxRange[2]-maxRange[1])/(xbins/2) x <- c() y <- c() for (i in 1:length(sampleIndex1)){   for (j in 1:length(sampleIndex2)){     x <- c(x, unlist(datSel[,(sampleIndex1[i])]))     y <- c(y, unlist(datSel[,(sampleIndex2[j])]))   } }  h <- hexbin(x=x, y=y, xbins=xbins, shape=1, IDs=TRUE, xbnds=maxRange, ybnds=maxRange) hexdf <- data.frame (hcell2xy (h),  hexID = h@cell, counts = h@count) attr(hexdf, "cID") <- h@cID  my_breaks = c(2, 4, 6, 8, 20, 1000) p <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) + geom_hex(stat="identity") + geom_abline(intercept = 0, color = "red", size = 0.25) + labs(x = "A", y = "C") + coord_fixed(xlim = c(-0.5, (maxRange[2]+buffer)), ylim = c(-0.5, (maxRange[2]+buffer))) + theme(aspect.ratio=1) p <- p + scale_fill_gradient(name = "count", trans = "log", breaks = my_breaks, labels = my_breaks, guide="legend")  ggplotly(p) ggplotly(p) %>% layout(height = 200, width = 200) ggplotly(p, height=400, width=400) 

As you can see, I tried a few different approaches to creating the ggplotly(p) output. I received warnings as follows:

 Warning messages: 1: Aspect ratios aren't yet implemented, but you can manually set a suitable height/width  2: Aspect ratios aren't yet implemented, but you can manually set a suitable height/width  3: Specifying width/height in layout() is now deprecated. Please specify in ggplotly() or plot_ly()  

However, I am uncertain how to resolve this warning and the problem. Any suggestions would be greatly appreciated!

1 Answers

Answers 1

This is a partial solution, it fixes the x = y line and square aspect ratio, but uses a bit of a workaround for the legend problem.

The aspect ratio issue is simple, in the latest version plotly changed so that now height = and width = go in ggplotly(), not layout() as in the previous version. Unfortunately some of the online documentation still seems to specify the old formatting.

I could not get your custom legend and scale to show in plotly, and incompatibility with ggplot legends seems to be a documented plotly bug for some types of ggplots. The best solution I could think of was to create a column in your dataframe for log counts, and then plot log counts so that the default legend showed the colors and scale you wanted.

# add a column for log count so default scale/legend can be used hexdf$log_counts <- log(hexdf$counts)  p <- ggplot(hexdf, aes(x = x, y = y)) +   geom_hex(stat="identity", aes(fill = log_counts)) + # log counts, not  counts   geom_abline(intercept = 0, color = "red", size = 0.25) +   labs(x = "A", y = "C") +   coord_fixed(xlim = c(-0.5, (maxRange[2]+buffer)),               ylim = c(-0.5, (maxRange[2]+buffer))) +   theme(aspect.ratio = 1)  p   # set width > height to allow room for legend # plot looks close to 1:1 to me, but may need to adjust width slightly ggplotly(p, height = 400, width = 500)  

Which produces

enter image description here

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment