使用 Shiny App 的气泡图 - 标签和气泡不同步

Bubble Chart using Shiny App - Labels and Bubbles out of sync

首先是我的数据:

 df <- structure(list(rowname = c(
      "AA - 2018", "AA - 2019", "AA - 2020",
      "AB - 2018", "AB - 2019", "AB - 2020"
    ), Class = c(
      "AA", "AA",
      "AA", "AB", "AB", "AB"
    ), Year = c(
      2018L, 2019L, 2020L, 2018L,
      2019L, 2020L
    ), Vol = c(0.3, 0.4, 0.5, 0.2, 0.4, 0.7), Profit = c(
      -0.1,
      -0.4, 0.2, 0.5, 0.2, -0.9
    ), Amount = c(1L, 1L, 2L, 3L, 4L, 5L), Division = c("Bonds","Bonds","Bonds","Equity","Equity","Equity")), class = "data.frame", row.names = c(NA, -6L))

我正在尝试创建一个气泡图,其中气泡的大小取决于金额,并根据 x 轴上的盈利能力(占金额的百分比)和波动率(作为% 的数量)在 y 轴上。我添加了一个使用年份列的滑块,这样气泡就可以随着滑块年份的变化而变化。

我正在开发一个带有下拉框的闪亮应用程序,允许使用 select 一个部门或所有部门。我有两个问题:

  1. 我希望轴随下拉框一起改变 selection 并且这有效但不幸的是标签和气泡不同步(在某些情况下消失)我移动滑块。当我注释掉 RenderPlotly 部分开头的条件时,气泡和标签不会不同步。

  2. 有没有更好的方法来编写条件部分而不使用 if 语句?

     divvar = c("All"="All","Bond"="Bond","Equity"="Equity")
    
     Bound <- 0.1
    
     y_high <- max(df$Vol) + Bound
     y_low <- 0
    
     x_high <-max(df$Profit) + Bound
     x_low <- min(df$Profit) -Bound
    
     ui <- shinyUI(fluidPage(
    
     titlePanel("Title"), # Application title
     sidebarLayout(
    
     sidebarPanel(     
    
       selectInput("Division","Select Division",divvar,selected = "All"),width=2),
    
     mainPanel(plotlyOutput("plot", width = "100%"))
     )
     )
     )
    
    server <- function(input, output) {
    
    
     output$plot <- renderPlotly({
    
     #Labels and Bubbles are in sync if below section is commented:
    
     #----
    
     fil_table <- df %>% select(Vol,Profit,Division) %>% filter (Division == input$Division) 
    
     if(input$Division == "All") {}
    
     else
    
     {y_high <- max(fil_table$Vol) + Bound
     x_high <- max(fil_table$Profit) + Bound
     x_low <- min(fil_table$Profit) + Bound
     }
                      #----
    
                      ggplotly(ggplot( if(input$Division == "All") {df}
    
                      else
    
                      {df %>% filter (Division == input$Division)},
                      aes(x = Profit, y = Vol, size = Amount, color = Class, frame = Year)) +
                geom_point(alpha = 0.2) + scale_size(range = c(5,40)) +
                geom_text(aes(label = Class), size = 5)+
                geom_vline(xintercept = 0, linetype="dotted", color = "black", size=0.75)+
    
                scale_y_continuous(labels = scales::percent,expand = c(0, 0), limits = c(0,y_high)) +
    
    
                scale_x_continuous(labels = scales::percent,expand = c(0, 0), limits = c(x_low,x_high)) +
                labs(title = "Profitability vs Volatility", 
                     x = "Profitability %", 
                     y = "Volatility %")
       })
    
     }
    
    shinyApp(ui=ui, server=server)
    

如果选择 Bounds/Equity,您计算的下限是错误的,您使用 + 而不是 -。一般来说,使用if/else是可以的,但我不会直接在ggplot调用中使用它,所以我简化了一点。我建议您查看并使用 风格指南来编写可读性更好的代码,例如 tidyverse style guide.

df <- structure(list(rowname = c(
  "AA - 2018", "AA - 2019", "AA - 2020",
  "AB - 2018", "AB - 2019", "AB - 2020"
), Class = c(
  "AA", "AA",
  "AA", "AB", "AB", "AB"
), Year = c(
  2018L, 2019L, 2020L, 2018L,
  2019L, 2020L
), Vol = c(0.3, 0.4, 0.5, 0.2, 0.4, 0.7), Profit = c(
  -0.1,
  -0.4, 0.2, 0.5, 0.2, -0.9
), Amount = c(1L, 1L, 2L, 3L, 4L, 5L), Division = c("Bonds","Bonds","Bonds","Equity","Equity","Equity")), class = "data.frame", row.names = c(NA, -6L))

divvar = c("All"="All","Bonds"="Bonds","Equity"="Equity")

Bound <- 0.1

y_high <- max(df$Vol) + Bound
y_low <- 0

x_high <-max(df$Profit) + Bound
x_low <- min(df$Profit) -Bound

library(shiny)
library(plotly)

ui <- shinyUI(fluidPage(
  
  titlePanel("Title"), # Application title
  sidebarLayout(
    
    sidebarPanel(     
      
      selectInput("Division","Select Division",divvar,selected = "All"),width=2),
    
    mainPanel(plotlyOutput("plot", width = "100%"))
  )
)
)

server <- function(input, output) {
  
  
  output$plot <- renderPlotly({

    if (input$Division == "All") {
      plot_data <- df
    } else {
      plot_data <- df %>% filter(Division == input$Division)
      y_high <- max(plot_data$Vol) + Bound
      x_high <- max(plot_data$Profit) + Bound
      x_low <- min(plot_data$Profit) - Bound
    }
    
    ggplotly(ggplot(plot_data,
                    aes(x = Profit, y = Vol, size = Amount, color = Class, frame = Year)) +
               geom_point(alpha = 0.2) + scale_size(range = c(5,40)) +
               geom_text(aes(label = Class), size = 5)+
               geom_vline(xintercept = 0, linetype="dotted", color = "black", size=0.75)+
               
               scale_y_continuous(labels = scales::percent,expand = c(0, 0), limits = c(0,y_high)) +
               
               
               scale_x_continuous(labels = scales::percent,expand = c(0, 0), limits = c(x_low,x_high)) +
               labs(title = "Profitability vs Volatility", 
                    x = "Profitability %", 
                    y = "Volatility %"))
  })
    
}

shinyApp(ui=ui, server=server)