【问题标题】:Plotly: Annotate outliers with sample names in boxplotPlotly:在箱线图中用样本名称注释异常值
【发布时间】:2017-11-27 19:15:58
【问题描述】:

我正在尝试使用 ggplot 和数据集 airquality 创建箱线图,其中 Month 在 x 轴上,Ozone 值在 y 轴上。我的目标是对绘图进行注释,以便当我将鼠标悬停在异常点上时,除了臭氧值之外,它还应该显示 Sample 名称:

library(tidyverse)
library(plotly)
library(datasets)
data(airquality)

# add months
airquality$Month <- factor(airquality$Month,
                           labels = c("May", "Jun", "Jul", "Aug", "Sep"))

# add sample names
airquality$Sample <- paste0('Sample_',seq(1:nrow(airquality)))

# boxplot
p <- ggplot(airquality, aes(x = Month, y = Ozone)) +
  geom_boxplot()
p <- plotly_build(p)
p

这是创建的情节:

默认情况下,当我将鼠标悬停在每个框上时,它会显示 x 轴变量的基本汇总统计信息。但是,我还想看看异常样本是什么。例如当悬停在 May 上时,它会显示异常值 115,但并未显示它实际上是 Sample_30

如何将 Sample 变量添加到离群点,使其同时显示离群值和样本名称?

【问题讨论】:

    标签: r ggplot2 plotly boxplot


    【解决方案1】:

    我们可以几乎这样得到它:

    library(ggplot2)
    library(plotly)
    library(datasets)
    data(airquality)
    # add months
    airquality$Month <- factor(airquality$Month,
                               labels = c("May", "Jun", "Jul", "Aug", "Sep"))
    # add sample names
    airquality$Sample <- paste0('Sample_',seq(1:nrow(airquality)))
    # boxplot
    gg <- ggplot(airquality, aes(x = Month, y = Ozone)) +
      geom_boxplot()
    ggly <- ggplotly(gg)
    # add hover info
    hoverinfo <- with(airquality, paste0("sample: ", Sample, "</br></br>", 
                                         "month: ", Month, "</br>",
                                         "ozone: ", Ozone))
    ggly$x$data[[1]]$text <- hoverinfo
    ggly$x$data[[1]]$hoverinfo <- c("text", "boxes")
    
    ggly
    

    不幸的是,悬停不适用于第一个箱形图...

    【讨论】:

      【解决方案2】:

      此方法将达到相同的结果,但不显示箱线图摘要统计悬停。删除异常值并悬停在箱线图图层上,并覆盖仅包含悬停信息的异常值的 geom_point 图层。 plotly 的异常值定义为here。在处理更复杂的图表(例如并排分组的箱线图)时,这种方法比其他解决方案效果更好。有趣的是,该数据的 ggplotly 箱线图与 ggplot 图不同。 ggplotly 中 8 月的上栅须比 8 月的 ggplot 上栅须延伸得更远。

      library(dplyr)
      library(plotly)
      library(datasets)
      library(ggplot2)
      data(airquality)
      
      # manipulate data
      mydata = airquality %>% 
          # add months
          mutate(Month = factor(airquality$Month,labels = c("May", "Jun", "Jul", "Aug", "Sep")),
          # add sample names
                 Sample = paste0('Sample_',seq(1:n())))%>%
          # label if outlier sample by Month
          group_by(Month) %>% 
          mutate(OutlierFlag = ifelse((Ozone<quantile(Ozone,1/3,na.rm=T)-1.5*IQR(Ozone,na.rm=T)) | (Ozone>quantile(Ozone,2/3,na.rm=T)+1.5*IQR(Ozone,na.rm=T)),'Outlier','NotOutlier'))%>%
          group_by()
      
      
      # boxplot
      p <- ggplot(mydata, aes(x = Month, y = Ozone)) +
          geom_boxplot()+
          geom_point(data=mydata %>% filter(OutlierFlag=="Outlier"),aes(group=Month,label1=Sample,label2=Ozone),size=2)
      
      output = ggplotly(p, tooltip=c("label1","label2"))
      
      # makes boxplot outliers invisible and hover info off
      for (i in 1:length(output$x$data)){
          if (output$x$data[[i]]$type=="box"){
              output$x$data[[i]]$marker$opacity = 0  
              output$x$data[[i]]$hoverinfo = "none"
          }
      }
      
      # print end result of plotly graph
      output
      

      【讨论】:

      • 这真的很好用!谢谢 - 接受这个作为答案!
      【解决方案3】:

      我已经设法通过 Shiny 实现了这一点。

      library(plotly)
      library(shiny)
      library(htmlwidgets)
      library(datasets)
      
      # Prepare data ----
      data(airquality)
      # add months
      airquality$Month <- factor(airquality$Month,
                                 labels = c("May", "Jun", "Jul", "Aug", "Sep"))
      # add sample names
      airquality$Sample <- paste0('Sample_', seq(1:nrow(airquality)))
      
      # Plotly on hover event ----
      addHoverBehavior <- c(
        "function(el, x){",
        "  el.on('plotly_hover', function(data) {",
        "    if(data.points.length==1){",
        "      $('.hovertext').hide();",
        "      Shiny.setInputValue('hovering', true);",
        "      var d = data.points[0];",
        "      Shiny.setInputValue('left_px', d.xaxis.d2p(d.x) + d.xaxis._offset);",
        "      Shiny.setInputValue('top_px', d.yaxis.l2p(d.y) + d.yaxis._offset);",
        "      Shiny.setInputValue('dx', d.x);",
        "      Shiny.setInputValue('dy', d.y);",
        "      Shiny.setInputValue('dtext', d.text);",
        "    }",
        "  });",
        "  el.on('plotly_unhover', function(data) {",
        "    Shiny.setInputValue('hovering', false);",
        "  });",
        "}")
      
      # Shiny app ----
      ui <- fluidPage(
        tags$head(
          # style for the tooltip with an arrow (http://www.cssarrowplease.com/)
          tags$style("
                     .arrow_box {
                          position: absolute;
                        pointer-events: none;
                        z-index: 100;
                        white-space: nowrap;
                        background: rgb(54,57,64);
                        color: white;
                        font-size: 14px;
                        border: 1px solid;
                        border-color: rgb(54,57,64);
                        border-radius: 1px;
                     }
                     .arrow_box:after, .arrow_box:before {
                        right: 100%;
                        top: 50%;
                        border: solid transparent;
                        content: ' ';
                        height: 0;
                        width: 0;
                        position: absolute;
                        pointer-events: none;
                     }
                     .arrow_box:after {
                        border-color: rgba(136, 183, 213, 0);
                        border-right-color: rgb(54,57,64);
                        border-width: 4px;
                        margin-top: -4px;
                     }
                     .arrow_box:before {
                        border-color: rgba(194, 225, 245, 0);
                        border-right-color: rgb(54,57,64);
                        border-width: 10px;
                        margin-top: -10px;
                     }")
        ),
        div(
          style = "position:relative",
          plotlyOutput("myplot"),
          uiOutput("hover_info")
        )
      )
      
      server <- function(input, output){
        output$myplot <- renderPlotly({
          airquality[[".id"]] <- seq_len(nrow(airquality))
          gg <- ggplot(airquality, aes(x=Month, y=Ozone, ids=.id)) + geom_boxplot()
          ggly <- ggplotly(gg, tooltip = "y")
          ids <- ggly$x$data[[1]]$ids
          ggly$x$data[[1]]$text <- 
            with(airquality, paste0("<b> sample: </b>", Sample, "<br/>",
                                    "<b> month: </b>", Month, "<br/>",
                                    "<b> ozone: </b>", Ozone))[ids]
          ggly %>% onRender(addHoverBehavior)
        })
        output$hover_info <- renderUI({
          if(isTRUE(input[["hovering"]])){
            style <- paste0("left: ", input[["left_px"]] + 4 + 5, "px;", # 4 = border-width after
                            "top: ", input[["top_px"]] - 24 - 2 - 1, "px;") # 24 = line-height/2 * number of lines; 2 = padding; 1 = border thickness
            div(
              class = "arrow_box", style = style,
              p(HTML(input$dtext), 
                style="margin: 0; padding: 2px; line-height: 16px;")
            )
          }
        })
      }
      
      shinyApp(ui = ui, server = server)
      

      【讨论】:

        【解决方案4】:

        我在https://github.com/ropensci/plotly/issues/887找到了解决方案

        尝试制作这种代码!

         library(plotly)
        
         vals <- boxplot(airquality$Ozone,plot = FALSE)
         y <- airquality[airquality$Ozone > vals$stats[5,1] | airquality$Ozone < vals$stats[1,1],]
        
        plot_ly(airquality,y = ~Ozone,x = ~Month,type = "box") %>% 
           add_markers(data = y, text = y$Day)
        

        【讨论】:

        • 抱歉,这是如何注释异常值的?对于最后一个框,即 9 月,您无法通过将鼠标悬停在 4 个异常点上来查看标签。
        • 实际上这个解决方案可能只有在你有一个盒子的情况下才有效
        • 对不起 Komal,这是我找到的最佳解决方案。确实,当您遇到叠加的异常值时,这是一个缺点,但它有效!
        猜你喜欢
        • 1970-01-01
        • 2018-07-02
        • 2012-12-10
        • 2016-10-27
        • 2021-11-06
        • 2020-08-27
        • 2021-01-14
        • 1970-01-01
        • 2023-03-12
        相关资源
        最近更新 更多