Introducing Jitter for Scatter Plots in Plotly

Feedback

Question:

I’m developing an application that involves highlighting specific data points in a boxplot based on the selected ids from a map when clicked.

The process of highlighting was performed using

plotlyProxyInvoke

and

addTraces

methods, since I was unable to utilize the

relayout

method. However, a drawback of this approach is that when additional points are introduced by

plotlyProxyInvoke

, boxplots with numerous discrete classes experience a resizing of the x-axis.

I believe that the provided minimum reproducible example will aid in comprehending my point. In this instance, the

observe

is activated by an
action button
, resulting in the inclusion of

plotlyProxyInvoke

.

library(data.table)
library(shiny)
library(plotly)
set.seed(1234)
my_data <- data.table(class = rep(LETTERS[1:20], 10),
                      val = rnorm(200, 0, 1),
                      type = sample(c(0:10), 200, replace = TRUE))
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      width = 3,
      actionButton("button", "Find type = 1")
    ),
    mainPanel(
      plotlyOutput("boxplot")
    )
  )
)
# Plotly Boxplot
server <- function(input, output, session) {
  output$boxplot <- renderPlotly({
    plot_ly(source = "boxplot") %>%
      add_trace(
        data = my_data,
        x = ~class,
        y = ~val,
        color = ~I("gray"),
        marker = list(
          color = "black"
        ),
        line = list(color = "black"),
        type = "box",
        boxpoints = "all",
        pointpos = 0,
        jitter = 0.5
      ) %>%
      layout(
        xaxis = list(
          fixedrange = TRUE
        ),
        yaxis = list(
          fixedrange = TRUE
        )
      )
  })
  # Highlight points for type = 1 ----------------------------------------------
  observeEvent(input$button, {
    
    plotlyProxy("boxplot", session) %>%
      plotlyProxyInvoke(
        method = "addTraces",
        list(
          x = my_data[type == 1, class],
          y = my_data[type == 1, val],
          type = "scatter",
          mode = "markers",
          hoverinfo = "skip",
          marker = list(
            opacity = 1,
            color = "red",
            size = 10
          )
        )
      )
  })
}
# Run the application
shinyApp(ui = ui, server = server)

It would be preferable to refrain from resizing and instead focus on highlighting the existing points, rather than introducing additional ones. Thank you.


Solution:

This is a guide on how to choose points programmatically using Plotly.restyle without the need for adding a new trace.

Kindly refer to the code comments for additional details.

library(data.table)
library(shiny)
library(plotly)
set.seed(1234)
my_data <- data.table(
  class = rep(LETTERS[1:20], 10),
  val = rnorm(200, 0, 1),
  type = sample(c(0:10), 200, replace = TRUE)
)
setorder(my_data, type) # order matters: so that the selection corresponds to the internal pointNumber (see e.g. click events)
ui <- fluidPage(sidebarLayout(
  sidebarPanel(width = 3,
               actionButton("button", "Find type = 1")),
  mainPanel(plotlyOutput("boxplot"))
))
# Plotly Boxplot
server <- function(input, output, session) {
  output$boxplot <- renderPlotly({
    # set color for selected points
    # run schema()
    # and navigate: object -> traces -> box -> attributes -> selected -> marker
    # for more info
    plot_ly(source = "boxplot", selected = list(marker = list(color = "red"))) %>%
      add_trace(
        data = my_data,
        x = ~ class,
        y = ~ val,
        color = ~ I("gray"),
        marker = list(color = "black"),
        line = list(color = "black"),
        type = "box",
        boxpoints = "all",
        pointpos = 0,
        jitter = 0.5
      ) %>%
      layout(xaxis = list(fixedrange = TRUE),
             yaxis = list(fixedrange = TRUE))
  })
  
  boxProxy <- plotlyProxy("boxplot", session)
  
  
  # Highlight points for type == 1 ------------------------------------------
  observeEvent(input$button, {
    # traceNumber = 0
    plotlyProxyInvoke(boxProxy, "restyle", list(selectedpoints = list(which(
      my_data$type == 1
    ))), 0)
  })
  
  # doubleclick events ------------------------------------------------------
  # run schema()
  # and navigate: object -> traces -> bar -> attributes -> selectedpoints -> description
  observeEvent(event_data(event = "plotly_doubleclick", source = "boxplot"),
               {
                 # deselecting all points for all traces
                 plotlyProxyInvoke(
                   boxProxy,
                   "restyle",
                   list(
                     selectedpoints = NULL,
                     selected = list(marker = list(color = "black")),
                     unselected = list(marker = list(opacity = 0.4))
                   )
                 ) 
               })
  
}
shinyApp(ui = ui, server = server)

result


An alternative method involves using a second trace instead of the initial

selectedpoints

.

library(data.table)
library(shiny)
library(plotly)
set.seed(1234)
my_data <- data.table(class = rep(LETTERS[1:20], 10),
                      val = rnorm(200, 0, 1),
                      type = sample(c(0:10), 200, replace = TRUE))
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      width = 3,
      actionButton("button", "Find type = 1")
    ),
    mainPanel(
      plotlyOutput("boxplot")
    )
  )
)
# Plotly Boxplot
server <- function(input, output, session) {
  output$boxplot <- renderPlotly({
    plot_ly(source = "boxplot") %>%
      add_trace(
        data = my_data,
        x = ~class,
        y = ~val,
        color = ~I("gray"),
        marker = list(
          color = "black"
        ),
        line = list(color = "black"),
        type = "box",
        boxpoints = "all",
        pointpos = 0,
        jitter = 0.5
      ) %>% add_trace(
        data = my_data[type == 1],
        x = ~class,
        y = ~val,
        type = "scatter",
        mode = "markers",
        hoverinfo = "skip",
        marker = list(
          opacity = 1,
          color = "black"
        )
      ) %>% 
      layout(
        xaxis = list(
          fixedrange = TRUE
        ),
        yaxis = list(
          fixedrange = TRUE
        )
      )
  })
  
  boxProxy <- plotlyProxy("boxplot", session)
  
  # Highlight points for type == 1 ------------------------------------------
  observeEvent(input$button, {
    plotlyProxyInvoke(boxProxy, "restyle", list(marker = list(
      opacity = 1,
      color = "red"
    )), 1)
  })
  
  # doubleclick events ------------------------------------------------------
  # run schema()
  # and navigate: object -> traces -> bar -> attributes -> selectedpoints -> description
  observeEvent(event_data(event = "plotly_doubleclick", source = "boxplot"),
               {
                 plotlyProxyInvoke(boxProxy, "restyle", list(marker = list(
                   opacity = 1,
                   color = "black"
                 )), 1)
               })
}
# Run the application
shinyApp(ui = ui, server = server)

Frequently Asked Questions