简体   繁体   English

R Plotly:将图例标题移动到水平图例位置的图例上方

[英]R Plotly: Move the legend title above the legends in the horizontal legend position

I have a limitation in the window size, which requires me to have my plotly (converted using ggplotly ) figures to have:我的窗口大小有限制,这要求我的plotly (使用ggplotly转换)数字具有:

  1. The legends title to be positioned above the legends (similar to ggplot HERE )图例标题位于图例上方(类似于ggplot HERE
  2. If possible, stack the legends on top of each other in which each row contains two legends, for example.例如,如果可能,将图例堆叠在一起,其中每行包含两个图例。
  3. Is it possible to have one legend for the two plots horizontally positioned on the bottom?底部水平放置的两个地块是否可以有一个图例?

Here is my code which the plotly part needs to be modified only:这是我的代码,只需要修改 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)

Here is the current output:这是当前的输出:

在此处输入图像描述

For the second part of my question, by stacking I mean an output similar like the figure below in which every row contains two legends:对于我的问题的第二部分,堆叠是指类似于下图的输出,其中每一行都包含两个图例:

在此处输入图像描述

UPDATE: In Q1, I also want to know how I can resolve the problem with partially shown legends and titles when we have many legends, as in the picture below, Would it be useful if we could move the title of a legend to the top of the list of legends?更新:在第一季度,我还想知道当我们有很多图例时如何解决部分显示的图例和标题的问题,如下图所示,如果我们可以将图例的标题移到顶部会有用吗传奇列表的? If yes, how?如果是,如何?

在此处输入图像描述

When I run your code, I don't get a legend for either plot.当我运行你的代码时,我没有得到任何一个情节的图例。 However, I can at least answer that yes—you could move the title to the top in a roundabout way.但是,我至少可以回答是——您可以将标题以迂回的方式移至顶部。 However, since your xaxis title is the legend title, you could just make the legend title blank.但是,由于您的xaxis标题是图例标题,您可以将图例标题设为空白。

If the plot is narrow and the data is long, you could achieve a stacked legend, but it will change with the width of the viewing area.如果图很窄,数据很长,你可以实现堆叠图例,但它会随着查看区域的宽度而变化。 (You could control the size of the viewing area.) (您可以控制查看区域的大小。)

Alternatively, it looks like Plotly added a new feature in the last few months, but Plotly for R isn't using the latest version of Plotly.或者,看起来 Plotly 在过去几个月中添加了一项新功能,但 Plotly for R 并未使用最新版本的 Plotly。 You could change the dependency and control the width that way (stacking, all that jazz).您可以更改依赖关系并以这种方式控制宽度(堆叠,所有爵士乐)。

However, whether you control the plot viewing size or the legend entry size, it won't split the legends.但是,无论您控制绘图查看大小还是图例条目大小,它都不会拆分图例。

One legend...I know there are issues with subplot in Plotly and Shiny.一个传奇......我知道subplot和 Shiny 中的子图存在问题。 However, it works for me here.但是,它在这里对我有用。

Q1 Q1

Since the xaxis is the legend title, you could erase the legend title.由于xaxis是图例标题,您可以删除图例标题。

Since I am not getting a legend at all, this also turns it on.因为我根本没有得到图例,所以这也打开了它。

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
}

I modified the call, to add showlegend , modify y in legend and add the call for fixLeg() .我修改了调用,添加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 Q2

A stacked legend, whether it originated as one legend or many legends.一个堆叠的传奇,无论它起源于一个传奇还是多个传奇。 For this, I'm going to change the Plotly dependency to the latest version of Plotly JS, I'll use a function to do that.为此,我将把 Plotly 依赖项更改为最新版本的 Plotly JS,我将使用一个函数来执行此操作。 This function will allow the parameters of entrywidth and entrywidthmode to work in R.此函数将允许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
}

To make sure that Shiny goes to get the CDN from the web, you'll add something else to the user interface ( ui ).为确保 Shiny 从 Web 获取 CDN,您需要向用户界面 ( ui ) 添加其他内容。 This addition requires the use of htmltools .此添加需要使用htmltools The Shiny app may work without this addition. Shiny 应用程序可以在没有此添加的情况下运行。 However, it will be intermittent at best.但是,它充其量是间歇性的。

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")
)

Then with the plots, add an entrywidth .然后在绘图中添加一个entrywidth Additionally, change the function call for fixLeg to fixLeg2 .此外,将fixLeg2 fixLeg

By the way entrywidth is the width in pixels of the text component of a legend entry.顺便说一下, entrywidth是图例条目的文本组件的宽度(以像素为单位)。 The symbol, dot, line, or colored part is item in the legend.符号、点、线或彩色部分是图例中的item You can control that size, too.您也可以控制该尺寸。 I'm pretty sure that control is fairly new, as well.我很确定控件也是相当新的。

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
  })
})

This is how it's changed:这是它的变化方式:

在此处输入图像描述

In your question, you had specifically asked about when there is more than one legend, but your plots in this question only have one.在你的问题中,你特别询问了何时有多个传说,但你在这个问题中的情节只有一个。 I've taken this plot from a previous question.我从上一个问题中获取了这个情节。 (Where df is extremely similar to test in this question.) (其中df与此问题中的test极为相似。)

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
  )

I have to change the call to change the dependencies a bit, because this plot isn't in Shiny.我必须更改调用以稍微更改依赖项,因为该图不在 Shiny 中。 Really, I've just added one new line ( src$file = NULL ).真的,我刚刚添加了一个新行( 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
}

I've added a new parameter entrywidthmode .我添加了一个新参数entrywidthmode This is default pixels .这是默认pixels However you can use fraction as in a fraction of the plot.但是,您可以在情节的一小部分中使用fraction If I set it to.3 (so there's still room for the title), No matter how big I make this plot, only three legend entries are going on a single line.如果我将它设置为 .3(这样标题还有空间),无论我把这个图画得有多大,一行上只有三个图例条目。

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

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

Q3 Q3

When it comes to consolidating the legend, Plotly's subplot can do it for you.在巩固图例方面, subplot的子图可以为您完成。 However, it won't do it without intervention.但是,如果没有干预,它不会这样做。 That's because you can select a legend item it hides or shows in the plot.那是因为您可以选择它在图中隐藏或显示的图例项。 You can use legendgroups to get around it.您可以使用legendgroups来绕过它。 This will still allow entry selection by clicking on a legend item, but it will affect both plots.这仍将允许通过单击图例项来选择条目,但它会影响两个图。

To use legend grouping and have a consolidated legend, you have to designate the legend group of each trace, and you have to hide all but one trace within each legend group from the legend.要使用图例分组并获得统一的图例,您必须指定每条迹线的图例组,并且必须从图例中隐藏每个图例组中除一条迹线以外的所有迹线。 Here's a simpler example.这是一个更简单的例子。 (By the way, hiding it in ggplot does nothing!) (顺便说一下,将它隐藏在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"))

在此处输入图像描述

In Shiny, don't use renderPlotly to show a subplot .在 Shiny 中,不要使用renderPlotly来显示subplot Just use renderUI and call subplot like you would in an R script.只需使用renderUI并像在 R 脚本中一样调用subplot

If you find that a combined legend won't orient horizontally, make sure that traceorder is called before orientation .如果您发现组合图例不会水平定向,请确保在orientation之前调用traceorder It shouldn't matter, but it does.应该无关紧要,但确实如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM