r - 如何使用响应式(Reactive)数据框在 R Shiny 中创建可拖动的绘图?

标签 r shiny plotly purrr shiny-reactivity

在下面的代码1中,我尝试使用plotly包创建一个可拖动的绘图。用户应该能够拖动绘图的点并捕获呈现在左侧的称为“Data1”的数据框中的新点。运行代码时,我收到错误“警告:<-:赋值左侧无效(NULL)错误”。我做错了什么?

仅供引用,下面的 Code2 就是这样做的,但使用了不同的数据集,尽管两者的结构相同。在运行 Code2 时,我将在 Code2 中工作的数据帧(称为“Data”)与在 Code1 中不起作用的数据帧(“Data1”)进行比较,以显示这两个数据帧在结构上的相似程度。拖动 Code2 中绘制的数据点,看看左侧的“数据”表更新得如何。这就是我试图在 Code1 中实现的目标,但通过使用 Data1 数据来实现。

解决方案剧透:请参阅下面的 ismirsehregal 答案。 Code1 和 Code2 之间的差异(Code1 失败而 Code2 没有)是由于在 Code1 中定义 data1() 数据帧时不恰本地使用了 reactive() 造成的。由于 data1() 是从不同地方修改的(sliderInput(),plotly 中的拖动功能),reactiveVal()reactiveValues在定义数据帧时必须使用 () 而不是 reactive() 。另请注意在拖动绘图点后渲染修改后的数据帧时使用 reactiveValuesToList()

代码1:

library(plotly)
library(purrr)
library(shiny)

ui <- fluidPage(
  fluidRow(column(5,sliderInput('periods','Nbr of periods:',min=0,max=24,value=12))),
  fluidRow(column(2,h5("Data1:"),tableOutput('data1')),
           column(6, plotlyOutput("p")))
)

server <- function(input, output, session) {
  data1 <- reactive({
    data.frame(
      x = c(1:input$periods),
      y = c((0.15-0.70)*(exp(-50/100*(1:input$periods))-exp(-50/100*input$periods)*
            (1:input$periods)/input$periods)) + 0.70
    )
  })

  output$p <- renderPlotly({
    circles <- map2(data1()$x, data1()$y, 
                    ~list(type = "circle",
                          xanchor = .x,
                          yanchor = .y,
                          x0 = -4, x1 = 4,
                          y0 = -4, y1 = 4,
                          xsizemode = "pixel", 
                          ysizemode = "pixel",
                          fillcolor = "blue",
                          line = list(color = "transparent"))
                    )
    plot_ly() %>%
      add_lines(x = data1()$x, y = data1()$y, color = I("red")) %>%
      layout(shapes = circles) %>%
      config(edits = list(shapePosition = TRUE))
  })
 
  output$data1 <- renderTable(data1())
  
  # update x/y reactive values in response to changes in shape anchors
  observe({
    ed <- event_data("plotly_relayout")
    shape_anchors <- ed[grepl("^shapes.*anchor$", names(ed))]
    if (length(shape_anchors) != 2) return()
    row_index <- unique(readr::parse_number(names(shape_anchors)) + 1)
    pts <- as.numeric(shape_anchors)
    data1()$x[row_index] <- pts[1]
    data1()$y[row_index] <- pts[2]
  })
  
}

shinyApp(ui, server)

代码2:

library(plotly)
library(purrr)
library(shiny)

ui <- fluidPage(
  fluidRow(column(5,sliderInput('periods','Nbr of periods:',min=0,max=24,value=12))),
  fluidRow(
    column(2,h5(strong(("Data:"))),tableOutput('data')),
    column(2,h5(strong(("Data1:"))),tableOutput('data1')),
    column(6,h5(strong(("Move the points and see how `Data` table to left updates:"))), plotlyOutput("p")),
  ),
  fluidRow(h5(strong(("Data1 above shown for comparison purposes, would like to substitute Data with Data1 in the plot"))))
)

server <- function(input, output, session) {
  rv <- reactiveValues( x = mtcars$mpg,y = mtcars$wt)
  
  data <- reactive(data.frame(x=(rv$x_sub),y=(rv$y_sub)))
  
  data1 <- reactive({
    data.frame(
      x = c(1:input$periods),
      y = c((0.15-0.70) * (exp(-50/100*(1:input$periods))-
          exp(-50/100*input$periods)*(1:input$periods)/input$periods)) + 0.70
    )
  })
  
  observe({
    rv$x_sub <- rv$x[1:input$periods]
    rv$y_sub <- rv$y[1:input$periods]
  })
 
  output$p <- renderPlotly({
    circles <- map2(rv$x_sub, rv$y_sub, 
                    ~list(
                      type = "circle",
                      xanchor = .x,
                      yanchor = .y,
                      x0 = -4, x1 = 4,
                      y0 = -4, y1 = 4,
                      xsizemode = "pixel", 
                      ysizemode = "pixel",
                      fillcolor = "blue",
                      line = list(color = "transparent")
                    )
    )
    plot_ly() %>%
      add_lines(x = rv$x_sub, y = rv$y_sub, color = I("red")) %>%
      layout(shapes = circles) %>%
      config(edits = list(shapePosition = TRUE))
  })
 
  output$data <- renderTable(data())
  output$data1 <- renderTable(data1())
  
  # update x/y reactive values in response to changes in shape anchors
  observe({
    ed <- event_data("plotly_relayout")
    shape_anchors <- ed[grepl("^shapes.*anchor$", names(ed))]
    if (length(shape_anchors) != 2) return()
    row_index <- unique(readr::parse_number(names(shape_anchors)) + 1)
    pts <- as.numeric(shape_anchors)
    rv$x[row_index] <- pts[1]
    rv$y[row_index] <- pts[2]
  })
  
}

shinyApp(ui, server)

最佳答案

不幸的是,您无法在多个位置修改reactive。对于此用例,旨在使用 reactiveValreactiveValues

请检查以下内容:

library(plotly)
library(purrr)
library(shiny)

ui <- fluidPage(
  fluidRow(column(5,sliderInput('periods','Nbr of periods:',min=0,max=24,value=12))),
  fluidRow(column(2,h5("Data1:"),tableOutput('data1table')),
           column(6, plotlyOutput("p")))
)

server <- function(input, output, session) {
  data1 <- reactiveValues(x = NULL, y = NULL)
  
  observe({
    data1$x <- c(1:input$periods)
    data1$y <- c((0.15-0.70)*(exp(-50/100*(1:input$periods))-exp(-50/100*input$periods)*
                           (1:input$periods)/input$periods)) + 0.70
  })
  
  output$p <- renderPlotly({
    circles <- map2(data1$x, data1$y, 
                    ~list(type = "circle",
                          xanchor = .x,
                          yanchor = .y,
                          x0 = -4, x1 = 4,
                          y0 = -4, y1 = 4,
                          xsizemode = "pixel", 
                          ysizemode = "pixel",
                          fillcolor = "blue",
                          line = list(color = "transparent"))
    )
    plot_ly() %>%
      add_lines(x = data1$x, y = data1$y, color = I("red")) %>%
      layout(shapes = circles) %>%
      config(edits = list(shapePosition = TRUE))
  })
  
  output$data1table <- renderTable({
    as.data.frame(reactiveValuesToList(data1))
  })
  
  # update x/y reactive values in response to changes in shape anchors
  observe({
    ed <- event_data("plotly_relayout")
    shape_anchors <- ed[grepl("^shapes.*anchor$", names(ed))]
    if (length(shape_anchors) != 2) return()
    row_index <- unique(readr::parse_number(names(shape_anchors)) + 1)
    pts <- as.numeric(shape_anchors)
    data1$x[row_index] <- pts[1]
    data1$y[row_index] <- pts[2]
  })
  
}

shinyApp(ui, server)

result

关于r - 如何使用响应式(Reactive)数据框在 R Shiny 中创建可拖动的绘图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74148981/

相关文章:

python - 在 Flask 应用程序中绘图以测量实时数据

python - 使用 Plotly 在面积图上添加一条具有适当比例的线

使用等宽系列字体在 R 中渲染图不再显示字符

r - Shiny - 使用基于输入的过滤数据填充静态 HTML 表

r - 如何在 Shiny 的应用程序中使用响应式设计?

python - 如何在 Plotly 中为瀑布图添加总值列

r - 如何将 2 列 (X, Y)、管道分隔的表格转换为 X by Y 数据框或长格式?

r - 估算中间缺失值

r - 如何使用 "OR"组合多个条件来对数据帧进行子集化?

r - 在 Shiny 中显示 dbplyr 收集进度