You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shiny中用sliderInput筛选数据时保留Plotly已添加轨迹

R Shiny + Plotly:调整数据范围后保留点击高亮轨迹的问题

我在R语言的Shiny框架中制作交互式Plotly图表,需求如下:

  • 点击图表可高亮对应点
  • 支持用sliderInput调整显示的数据范围
  • 希望调整范围后,之前点击添加的高亮轨迹能够保留,但当前每次调整范围都会重绘图表,导致已添加的轨迹全部丢失

技术方案选择原因

  • 使用addTraces而非restyle处理点击标记:因为需要实现删除轨迹的功能,这种方式比单独修改标记更简便
  • 选择子集数据而非仅修改X轴范围:处理的是包含数万条数据的长时序,子集化能大幅提升性能,避免卡顿

尝试过的无效方案

  • 参考相关问题解决方案,但因场景和数据结构不同未能成功
  • 将点击数据保存到reactiveValues的响应式数据框中,在范围变化时重新添加轨迹,但未生效

示例代码

示例数据

# 示例数据
df <- data.frame(
  t = seq(as.POSIXct("2024-01-01 00:00:00", tz='UTC'),
          as.POSIXct("2024-01-02 00:00:00", tz='UTC'), by="1 hour"),
  V1 = sample(1:20, 25, replace=T)
)

UI 代码

library(shiny)
library(plotly)

# UI 部分
ui <- fluidPage(
  fluidRow(style = "padding: 15px;",
           actionButton("remove", "删除最后一次点击标记", width='150px') 
  ),
  fluidRow(style = "padding: 0px;",
           plotlyOutput("plot"),
           div(style = "margin: auto; width: 90%",
               sliderInput("range", label = NULL, width="100%",
                           min = as.POSIXct(min(df$t), tz='UTC'), 
                           max = as.POSIXct(max(df$t), tz='UTC'), 
                           value = c(as.POSIXct(min(df$t), tz='UTC'), 
                                     as.POSIXct(max(df$t), tz='UTC')),
                           timeFormat="%F %T", timezone="+0000")
           ))
)

初始服务器代码(调整范围后丢失高亮轨迹)

# 初始服务器代码(调整范围后丢失高亮轨迹)
server <- function(input, output, session) {
  
  output$plot <- renderPlotly({
    df[df$t >= input$range[1] & df$t <= input$range[2],] %>%
      plot_ly(x= ~t, y = ~V1, type='scatter', mode='line') %>%
      layout(showlegend=F) 
  })
  
  # 高亮点击的点
  observeEvent(event_data("plotly_click"),{
    d <- req(event_data("plotly_click"))
    
    plotlyProxy("plot", session) %>%
      plotlyProxyInvoke("addTraces", list(
        x = c(d$x, d$x), 
        y = c(d$y, d$y), 
        type = 'scatter',
        marker = list(symbol='x', size=10, color='red')
      ))
  })
  
  # 删除最后一次点击标记
  observeEvent(input$remove, {
    plotlyProxy("plot", session) %>%
      plotlyProxyInvoke("deleteTraces", list(-1))
  })
}

启动应用

shinyApp(ui, server)

效果演示

交互式Plotly图表演示


尝试的无效版本(使用observeEvent(input$range,{}))

尝试使用observeEvent(input$range,{})的版本(未生效,无法重新添加轨迹):

server <- function(input, output, session) {
  
  vals <- reactiveValues(
    d_click = data.frame()
  )
  
  output$plot <- renderPlotly({
    df[df$t >= input$range[1] & df$t <= input$range[2],] %>%
      plot_ly(x= ~t, y = ~V1, type='scatter', mode='line') %>%
      layout(showlegend=F)
  })
  
  # 范围变化且已有高亮点时重新添加轨迹
  observeEvent(input$range,{
    if(dim(vals$d_click)[1] > 0){
      plotlyProxy("plot", session) %>%
        plotlyProxyInvoke("addTraces", list(
          list(
            x = c(vals$d_click$x, vals$d_click$x), 
            y = c(vals$d_click$y, vals$d_click$y), 
            type = 'scatter',
            marker = list(symbol='x', size=10, color='red')
          )
        ))
    }
  })
  
  # 高亮点击的点
  observeEvent(event_data("plotly_click"),{
    d <- req(event_data("plotly_click"))
    
    plotlyProxy("plot", session) %>%
      plotlyProxyInvoke("addTraces", list(
        x = c(d$x, d$x), 
        y = c(d$y, d$y), 
        type = 'scatter',
        marker = list(symbol='x', size=10, color='red')
      ))
    
    vals$d_click <- rbind(vals$d_click, d)
  })
  
  # 删除最后一次点击标记
  observeEvent(input$remove, {
    plotlyProxy("plot", session) %>%
      plotlyProxyInvoke("deleteTraces", list(-1))
    
    vals$d_click <- vals$d_click[-nrow(vals$d_click),]
  })
}

内容的提问来源于stack exchange,提问作者Anke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 14:00:26