I'm creating a Shiny app to display survey results. I want to display the results in a plot with the question text as labels down the y-axis. (The plot is more complicated than the demo version below). I want to use plotly so that the data labels will appear with the mouse hover.
The problem is that the long y-axis labels cause the plot shape to be completely distorted: everything is pushed off to the right, leaving a lot of white space on the left.
I tried adding line breaks manually (using <br>
or \n), but the plot is still pushed to the right. I also specified the "width" in the ggplotly call; this makes it wider, but still pushes it to the right.
Is it possible to control these things, either within plotly or within ggplot2?
UPDATE EDIT: Here is the solution I discovered, in case it helps anyone else. It has two parts:
1) Set the margins manually in the layout()
call after ggplotly()
: https://plot.ly/r/setting-graph-size/ (You can also adjust the overall plot width in the UI inside the plotlyOutput()
call.)
layout(autosize = TRUE, margin = list(l = 300, r = 0, b = 0, t = 0, pad = 4))
2) Use a string wrapping function to split the labels, as suggested by Rushabh in his answer. I like the tidyverse version:
scale_x_discrete(labels = function(x) str_wrap(x, width = 40))
This is a demo where you can view both the problem and solution:
library(shiny)
library(plotly)
library(tidyverse)
ui <- fluidPage(
titlePanel("Problems with Plotly"),
sidebarLayout(
sidebarPanel(
radioButtons("view", "View", choices = c("Problem", "Solution")),
width = 3
),
mainPanel(
fluidRow(
column(6, HTML("Other content fills up this column")),
column(6, plotlyOutput("plot", width = "600px"))
)
)
)
)
server <- function(input, output) {
output$plot <- renderPlotly({
df <- tibble(
Label = paste0(
"Very long survey question that has to be spelled out completely ",
1:5
),
Value = sample(5:10, 5, replace = TRUE)
)
if (input$view == "Problem") {
p <- ggplot(df, aes(Label, Value)) +
geom_col() +
coord_flip() +
labs(x = "")
ggplotly(p) %>%
config(displayModeBar = FALSE)
} else { # input$view == "Solution"
p <- ggplot(df, aes(Label, Value)) +
geom_col() +
coord_flip() +
labs(x = "") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 40))
ggplotly(p) %>%
config(displayModeBar = FALSE) %>%
layout(autosize = TRUE, margin = list(
l = 300, r = 0, b = 0, t = 0, pad = 4
))
}
})
}
shinyApp(ui = ui, server = server)
Here's the original sample showing my attempts that didn't work:
library(shiny)
library(plotly)
library(tidyverse)
label <- "Very long survey question that has to be spelled out completely "
label_break <- "Very long survey question that<br>has to be spelled out completely "
ui <- fluidPage(
titlePanel("Problems with Plotly"),
sidebarLayout(
sidebarPanel(
radioButtons("tried", "Things I've tried",
c("Adding line breaks" = "breaks",
"Adding 'width' to ggplotly call" = "width",
"Both",
"Neither"),
selected = "Neither")
),
mainPanel(
fluidRow(
column(6, HTML("Some content goes on this side")),
column(6, plotlyOutput("plot"))
)
)
)
)
server <- function(input, output) {
output$plot <- renderPlotly({
df <- tibble(
Label = paste0(ifelse(input$tried %in% c("breaks", "Both"),
label_break, label), 1:5),
Value = sample(5:10, 5, replace = TRUE)
)
p <- ggplot(df, aes(Label, Value)) +
geom_col() +
coord_flip() +
labs(x = "")
if (input$tried %in% c("width", "Both")) {
ggplotly(p, width = 1000)
} else {
ggplotly(p)
}
})
}
shinyApp(ui = ui, server = server)