【问题标题】:R Plotly: Move the legend title above the legends in the horizontal legend positionR Plotly:将图例标题移动到水平图例位置的图例上方
【发布时间】:2022-12-01 11:29:06
【问题描述】:

我的窗口大小有限制,这要求我的 plotly(使用 ggplotly 转换)数字具有:

  1. 图例标题位于图例上方(类似于ggplotHERE
  2. 如果可能,将图例堆叠在一起,例如,每行包含两个图例。
  3. 底部水平放置的两个图是否可以有一个图例?

    这是我的代码,只需要修改 plotly 部分:

    library(shiny)
    library(dplyr)
    library(ggplot2)
    library(plotly)
    
    ui <- fluidPage(
      uiOutput("allplots")
    )
    
    server <- function(input, output, session) {
      
    
      output$allplots <- renderUI({
        
        test <-structure(list(
          Day = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
                  2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L),
          Drug = c("A", "B", "B", "C","A", "C", "A", "B", "C", "A", "B", "C", "A", "C", "A", "B", "B","C"), 
          Sex = c("Female", "Male", "Female", "Female", "Male", "Male","Male", "Female", "Male", "Female", "Female",
                  "Male", "Male", "Female", "Female", "Male", "Male", "Female"), 
          Y = c(2.192306074, 4.551912798, 1.574070652, -0.143946163, 5.144422967, 5.724705829,
                2.691617258, -3.0289955, 0.338102762, -0.558581233, -2.942620032, 1.024670497, 2.264980803, 
                2.103722883, 2.091621938, 1.535299922, 1.618399767, 0.136160703), 
          DrugSex = structure(c(1L, 4L, 3L, 5L, 2L, 6L, 2L, 3L, 6L, 1L, 3L, 6L, 2L, 5L, 1L, 4L, 4L, 5L), 
                              levels = c("A,Female", "A,Male", "B,Female", "B,Male", "C,Female", "C,Male"), 
                              class = "factor"),testNo = 1:18), row.names = c(NA,-18L), class = "data.frame")
    
        
        Xs <- c("testNo", "Day")
        plots <- lapply(Xs, function(x){
          
          renderPlotly({
    
            p <-test %>% ggplot(aes_string(x = "DrugSex", y = x)) +
              geom_point(aes(color = Drug, shape = DrugSex))+ 
              labs(x = paste0("DrugSex"), y = x) + 
              scale_shape_manual(values=1:length(unique(test$DrugSex))) + 
              guides(colour = guide_legend(title.position="top", title.hjust = 0.5),
                     shape = guide_legend(title.position="top", title.hjust = 0.5)) + 
              theme(legend.position = 'bottom',
                    legend.key=element_blank() #transparent legend panel
              )
            ggplotly(p) %>%
              layout(
                legend = list(orientation = 'h', xanchor = 'center', x = .5, y = -1.5)) 
          })
        })
          
       
        fluidRow(column(width = 4, plots[[1]]),
                 column(width = 4, plots[[2]]))
        
      })
    }
    
    shinyApp(ui, server)
    

    这是当前的输出:

    对于我的问题的第二部分,堆叠是指类似于下图的输出,其中每一行都包含两个图例:

【问题讨论】:

    标签: r ggplot2 shiny plotly


    【解决方案1】:

    当我运行你的代码时,我没有得到任何一个情节的图例。但是,我至少可以回答是——您可以将标题以迂回的方式移至顶部。但是,由于您的xaxis 标题是图例标题,您可以将图例标题设为空白。

    如果图很窄,数据很长,你可以实现堆叠图例,但它会随着查看区域的宽度而变化。 (您可以控制查看区域的大小。)

    或者,看起来 Plotly 在过去几个月中添加了一项新功能,但 Plotly for R 并未使用最新版本的 Plotly。您可以更改依赖关系并以这种方式控制宽度(堆叠,所有爵士乐)。

    但是,无论您控制绘图查看大小还是图例条目大小,它都不会拆分图例。

    一个传奇……我知道在 Plotly 和 Shiny 中 subplot 存在问题。但是,它在这里对我有用。

    Q1

    由于 xaxis 是图例标题,您可以删除图例标题。

    因为我根本没有得到图例,所以这也打开了它。

    fixLeg <- function(plt){
      plt <- plotly_build(plt)
      nms <- plt$x$layout$xaxis$ticktext
      plt$x$layout$legend$title$text <- ""
      map(1:length(plt$x$data), 
          function(k) {
            plt$x$data[[k]]$name <<- nms[[k]]
            plt$x$data[[k]]$showlegend <<- TRUE
          })
      plt
    }
    

    我修改了调用,添加showlegend,修改legend中的y并添加fixLeg()的调用。

    plots <- lapply(Xs, function(x){
      renderPlotly({
        p <-test %>% ggplot(aes_string(x = "DrugSex", y = x)) +
          geom_point(aes(color = Drug, shape = DrugSex))+ 
          labs(x = paste0("DrugSex"), y = x) + 
          scale_shape_manual(values=1:length(unique(test$DrugSex))) + 
          guides(colour = guide_legend(title.position="top", title.hjust = 0.5),
                 shape = guide_legend(title.position="top", title.hjust = 0.5)) + 
          theme(legend.position = 'bottom',
                legend.key=element_blank()) #transparent legend panel
        ggplotly(p) %>% fixLeg() %>%     # <---- add legend/remove legend title
          layout(showlegend = T,
                 legend = list(orientation = 'h', xanchor = 'center', 
                               x = .5, y = -.3))   # <---- changed y
      })
    })
    

    Q2

    一个堆叠的传奇,无论它起源于一个传奇还是多个传奇。为此,我将把 Plotly 依赖项更改为最新版本的 Plotly JS,我将使用一个函数来执行此操作。此函数将允许 entrywidthentrywidthmode 的参数在 R 中工作。

    fixLeg2 <- function(plt){
      plt <- plotly_build(plt)
      # changes to dependency so that entrywidth/entrywidthmode work
      plt$dependencies[[5]]$src$href = "https://cdn.plot.ly"
      plt$dependencies[[5]]$script = "plotly-2.16.1.min.js"
      plt$dependencies[[5]]$local = FALSE
      plt$dependencies[[5]]$package = NULL
    
      # changes to object
      nms <- plt$x$layout$xaxis$ticktext
      plt$x$layout$legend$title$text <- ""
      map(1:length(plt$x$data), 
          function(k) {
            plt$x$data[[k]]$name <<- nms[[k]]
            plt$x$data[[k]]$legendgroup <<- nms[[k]]
            plt$x$data[[k]]$showlegend <<- TRUE
          })
      plt
    }
    

    为确保 Shiny 从 Web 获取 CDN,您需要向用户界面添加其他内容 (ui)。此添加需要使用htmltools。 Shiny 应用程序可以在没有此添加的情况下运行。但是,它充其量是间歇性的。

    library(shiny)
    library(dplyr)
    library(ggplot2)
    library(plotly)
    library(htmltools)
    
    newDep <- htmlDependency(name = "plotly-latest",
                             version = "2.16.1",
                             src = list(href = "https://cdn.plot.ly"),
                             script = "plotly-2.16.1.min.js") 
    ui <- fluidPage(
      createWebDependency(newDep),
      uiOutput("allplots")
    )
    

    然后在绘图中添加一个entrywidth。此外,将 fixLeg 的函数调用更改为 fixLeg2

    顺便说一句,entrywidth 是图例条目的文本组件的宽度(以像素为单位)。符号、点、线或彩色部分在图例中为item。您也可以控制该尺寸。我很确定控件也是相当新的。

    plots <- lapply(Xs, function(x){
      renderPlotly({
        p <-test %>% ggplot(aes_string(x = "DrugSex", y = x)) +
          geom_point(aes(color = Drug, shape = DrugSex))+ 
          labs(x = paste0("DrugSex"), y = x) + 
          scale_shape_manual(values=1:length(unique(test$DrugSex))) + 
          guides(colour = guide_legend(title.position="top", title.hjust = 0.5),
                 shape = guide_legend(title.position="top", title.hjust = 0.5)) + 
          theme(legend.position = 'bottom',
                legend.key=element_blank() #transparent legend panel
          )
        ggplotly(p) %>% fixLeg2() %>%     # <------- I'm new
          layout(showlegend = T,
                 legend = list(orientation = 'h', xanchor = 'center', 
                               x = .5, y = -.3, entrywidth = 100)) # <- entry width
      })
    })
    

    这是它的变化方式:

    在你的问题中,你特别询问了何时有多个传说,但你在这个问题中的情节只有一个。我从上一个问题中获取了这个情节。 (在这个问题中,dftest 极为相似。)

    p <- df %>% ggplot(aes(x = DrugSex, y = Y)) +
      geom_point(aes(color = Drug, shape = DrugSex)) + 
      geom_segment(data = df_means, aes(x=x-0.25, xend=x+0.25, y=Mean, yend=Mean, color= 
                                          color),inherit.aes = F, show.legend = F)+
      theme(legend.position = 'bottom',
            legend.key=element_blank() #transparent legend panel
      )
    

    我必须更改调用以稍微更改依赖项,因为该图不在 Shiny 中。真的,我刚刚添加了一个新行 (src$file = NULL)。

    fixLeg3 <- function(plt) {
      # changes to dependency so that entrywidth/entrywidthmode work
      plt$dependencies[[5]]$src$file = NULL
      plt$dependencies[[5]]$src$href = "https://cdn.plot.ly"
      plt$dependencies[[5]]$script = "plotly-2.16.1.min.js"
      plt$dependencies[[5]]$local = FALSE
      plt$dependencies[[5]]$package = NULL
      plt
    }
    

    我添加了一个新参数entrywidthmode。这是默认的pixels。但是,您可以在图中的一小部分中使用 fraction。如果我将它设置为 .3(这样标题还有空间),无论我把这个图画得有多大,一行上只有三个图例条目。

    ggplotly(p) %>% fixLeg3() %>% 
      layout(legend = list(orientation = "h", 
                           y = -.3, entrywidthmode = "fraction",
                           entrywidth = .3))
    

    Q3

    在巩固图例方面,Plotly 的subplot 可以为您完成。但是,如果没有干预,它不会这样做。那是因为您可以选择它在图中隐藏或显示的图例项。您可以使用legendgroups 来绕过它。这仍将允许通过单击图例项来选择条目,但它会影响两个图。

    要使用图例分组并获得统一的图例,您必须指定每条迹线的图例组,并且必须从图例中隐藏每个图例组中除一条迹线以外的所有迹线。这是一个更简单的例子。 (顺便说一下,将它隐藏在ggplot 中什么都不做!)

    pp1 <- pp2 <- ggplot(test,  # first plot, keep the legend
                  aes(x = Drug, y = Y, color = Drug, shape = DrugSex)) +
      geom_point()
    
    p2 <- ggplotly(pp2) # hide the legend in this plot
    invisible(lapply(1:length(p2$x$data),
                     function(k) {
                       p2$x$data[[k]]$showlegend <<- FALSE # change in global
                     }))
    
    subplot(ggplotly(pp1), p2) %>%  # put it together
      layout(showlegend = T, 
             legend = list(traceorder = "grouped", # <- make sure this is called first!!
                           orientation = "h", x = .5, xanchor = "center",
                           valign = "bottom"))
    

    在 Shiny 中,不要使用 renderPlotly 来显示 subplot。只需使用 renderUI 并像在 R 脚本中一样调用 subplot

    如果您发现组合图例不会水平定向,请确保在 orientation 之前调用 traceorder。它不应该重要,但确实如此。

    【讨论】:

      猜你喜欢
      • 2019-08-01
      • 1970-01-01
      • 2017-09-22
      • 1970-01-01
      • 1970-01-01
      • 2018-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多