Saturday, August 5, 2017

Keep x and y scales same (so square plot) in ggplotly

Leave a Comment

I created a plot that has the same x and y limits, same scale for x and y ticks, hence guaranteeing the actual plot is perfectly square. Even with a legend included, the code below seems to keep the static plot (sp object) itself perfectly square even when the window in which it is positioned is rescaled:

library(ggplot2) library(RColorBrewer) set.seed(1) x = abs(rnorm(30)) y = abs(rnorm(30)) value = runif(30, 1, 30) myData <- data.frame(x=x, y=y, value=value) cutList = c(5, 10, 15, 20, 25) purples <- brewer.pal(length(cutList)+1, "Purples") myData$valueColor <- cut(myData$value, breaks=c(0, cutList, 30), labels=rev(purples)) sp <- ggplot(myData, aes(x=x, y=y, fill=valueColor)) + geom_polygon(stat="identity") + scale_fill_manual(labels = as.character(c(0, cutList)), values = levels(myData$valueColor), name = "Value") + coord_fixed(xlim = c(0, 2.5), ylim = c(0, 2.5)) 

However, I am now attempting to transfer this static plot (sp) into an interactive plot (ip) through ggplotly() that can be used in a Shiny app. I notice now that the interactive plot (ip) is no longer square-shaped. The MWE to show this is below:

ui.R

library(shinydashboard) library(shiny) library(plotly) library(ggplot2) library(RColorBrewer)  sidebar <- dashboardSidebar(   width = 180,   hr(),   sidebarMenu(id="tabs",     menuItem("Example plot", tabName="exPlot", selected=TRUE)   ) )  body <- dashboardBody(   tabItems(     tabItem(tabName = "exPlot",       fluidRow(         column(width = 8,           box(width = NULL, plotlyOutput("exPlot"), collapsible = FALSE, background = "black", title = "Example plot", status = "primary", solidHeader = TRUE))))))  dashboardPage(   dashboardHeader(title = "Title", titleWidth = 180),   sidebar,   body ) 

server.R

library(shinydashboard) library(shiny) library(plotly) library(ggplot2) library(RColorBrewer)  set.seed(1) x = abs(rnorm(30)) y = abs(rnorm(30)) value = runif(30, 1, 30)  myData <- data.frame(x=x, y=y, value=value)  cutList = c(5, 10, 15, 20, 25) purples <- brewer.pal(length(cutList)+1, "Purples") myData$valueColor <- cut(myData$value, breaks=c(0, cutList, 30), labels=rev(purples))  # Static plot sp <- ggplot(myData, aes(x=x, y=y, fill=valueColor)) + geom_polygon(stat="identity") + scale_fill_manual(labels = as.character(c(0, cutList)), values = levels(myData$valueColor), name = "Value") + coord_fixed(xlim = c(0, 2.5), ylim = c(0, 2.5))  # Interactive plot ip <- ggplotly(sp, height = 400)  shinyServer(function(input, output, session){    output$exPlot <- renderPlotly({     ip   })  }) 

It seems there may not be a built-in/clear solution at this time (Keep aspect ratio when using ggplotly). I have also read about a HTMLwidget.resize object that might help solve a problem like this (https://github.com/ropensci/plotly/pull/223/files#r47425101), but I was unsuccessful determining how to apply such syntax to the current problem.

Any advice would be appreciated!

1 Answers

Answers 1

I tried playing with fixed axis ratio to no avail.

Setting the plot margins to create a square plot worked for me.

enter image description here

The plot is kept square even when the axis range changes.

enter image description here

When the axis ratio should be identical (i.e. the units are square but the plot is not), one would need to adjust the code a little bit (answer will be updated soon).

library(ggplot2) library(RColorBrewer) set.seed(1) x = abs(rnorm(30)) y = abs(rnorm(30)) value = runif(30, 1, 30) myData <- data.frame(x=x, y=y, value=value) cutList = c(5, 10, 15, 20, 25) purples <- brewer.pal(length(cutList)+1, "Purples") myData$valueColor <- cut(myData$value, breaks=c(0, cutList, 30), labels=rev(purples)) sp <- ggplot(myData, aes(x=x, y=y, fill=valueColor)) + geom_polygon(stat="identity") + scale_fill_manual(labels = as.character(c(0, cutList)), values = levels(myData$valueColor), name = "Value") + coord_fixed(xlim = c(0, 2.5), ylim = c(0, 2.5)) sp  #set the height and width of the plot (including legends, etc.) height <- 500 width <- 500 ip <- ggplotly(sp, height = height, width = width)  #distance of legend margin_layout <- 100 #minimal distance from the borders margin_min <- 50  #calculate the available size for the plot itself available_width <- width - margin_min - margin_layout available_height <- height - 2 * margin_min  if (available_width > available_height) {   available_width <- available_height } else {   available_height <- available_width } #adjust the plot margins margin <- list(b=(height - available_height) / 2,                t=(height - available_height) / 2,                l=(width - available_width) / 2 - (margin_layout - margin_min),                r=(width - available_width) / 2 + (margin_layout - margin_min))  ip <- layout(ip, margin=margin) ip 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment