2
votes

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)
1
Is there a reason that the plot needs to be to the right of the "Some content" section, or could it go below (in which case options 1 or 3 seem viable)? Some shifting around of elements in the UI could help address the issue...phalteman
Yes, I need to fit a lot of other elements on the page. I just need to get plotly to scale correctly within the designated container, whatever the size of the container might be. With these long labels, it always shifts far to the right and doesn't even use all the available space.JeremyB
Good solution. You should post that as an answer and accept it so that others can see clearly that it was resolved.phalteman

1 Answers

0
votes

Try Below Code -

library(shiny)
library(plotly)
library(tidyverse)
label <- "Very long survey question that has to be spelled out completely "


ui <- fluidPage(

  titlePanel("Problems with Plotly"),

  sidebarLayout(
    sidebarPanel(
      fluidRow(
      radioButtons("tried", "Things I've tried", 
                   c("Adding line breaks" = "breaks", 
                     "Adding 'width' to ggplotly call" = "width", 
                     "Both", 
                     "Neither"), 
                   selected = "Neither")
    ),

    mainPanel(

      fluidRow(
        column(2,HTML("Some content goes on this side"),plotlyOutput("plot"))



      )

    )
  )
)
)

server <- function(input, output) {

  output$plot <- renderPlotly({

    df <- tibble(
      Label = paste0(label, 1:5), 
      Value = sample(5:10, 5, replace = TRUE)
    )

    p <- ggplot(df, aes(Label, Value)) + 
      geom_col() + 
      coord_flip() +
      labs(x = "") +
      scale_x_discrete(labels = function(x) lapply(strwrap(x, width = 20, simplify = FALSE), paste, collapse="\n"))

      ggplotly(p,width = 1000)
  })
}

shinyApp(ui = ui, server = server)

Note: You can adjust ggplot's tick lables using below code chunk-

scale_x_discrete(labels = function(x) lapply(strwrap(x, width = 20, simplify = FALSE), paste, collapse="\n"))