【问题标题】:Freehand drawing ggplot. How to improve and/or format for plotly?手绘ggplot。如何改进和/或格式化情节?
【发布时间】:2021-01-26 06:40:30
【问题描述】:

我有一个客户希望能够在 Rshiny 中“徒手”绘制绘图 (ggplot) 图。我说过要在绘图图上使用套索选择按钮,但如果您单击图上的其他位置会删除第一个套索,他们不高兴。

使用这个post,我能够处理一个我可以绘制的ggplot。但是,我无法让它与 plotly 一起工作,因为我不知道下面 ui 中悬停选项的等效项。我希望得到一些关于如何使用 plotly 执行此操作、如何改进代码以使其更快和/或如何不让它以 (1,1) 的任意值开始的输入。

可以理解,这仅在数据完全是数字时才有效。如果说数据中的第一列是 c("a","b","c") 而不是像下面这样的 c(1,2,3) ,是否有办法做到这一点。

注意:第一次单击时,该行从 (1,1) 开始,因为 ggplot 需要一个值来绘制图形,但响应式输入需要一个图形。为了绕过这个循环,我只是将列放在 c(1,vals$x)... 希望这是有道理的。

library(shiny)
library(tidyverse)
ui <- fluidPage(
  actionButton("reset", "reset"),
  plotOutput("plot",
             hover=hoverOpts(id = "hover", delay = 300, delayType = "throttle", clip = TRUE, nullOutside = TRUE),
             click="click"))


server <- function(input, output, session) {
  vals = reactiveValues(x=NULL, y=NULL)
  draw = reactiveVal(FALSE)
  observeEvent(input$click, handlerExpr = {
    temp <- draw(); draw(!temp)
    if(!draw()) {
      vals$x <- c(vals$x, NA)
      vals$y <- c(vals$y, NA)
    }})
  observeEvent(input$reset, handlerExpr = {
    vals$x <- NULL; vals$y <- NULL
  })
  observeEvent(input$hover, {
    if (draw()) {
      vals$x <- c(vals$x, input$hover$x)
      vals$y <- c(vals$y, input$hover$y)
    }})
  output$plot= renderPlot({
    Data<-cbind(c(1,2,3),c(2,3,4))%>%as.data.frame()
    d<-cbind(c(1,vals$x),c(1,vals$y))%>%as.data.frame()
    ggplot(data=Data)+geom_point(data=Data,aes(x=V1,y=V2))+
    geom_path(data=d,aes(x=V1,y=V2))+xlim(c(0,15))+ylim(c(0,15))
  })
  }

shinyApp(ui, server)

【问题讨论】:

  • 嗨,我看到了你对我帖子的评论。通过使用 plotly 快速搜索悬停选项,您似乎只能在鼠标悬停在现有点上时获取信息和触发事件,而不是在空白区域上。但我并不是说它不能完成...... :-)
  • @agenis 感谢另一篇文章的发帖,这对将我发送到正确的方向非常有帮助。我将不得不继续玩阴谋,看看我是否能解决一些问题。忽略情节,你能想到一个更好的方法来用 ggplot 做到这一点吗?这行得通,但对我来说缺乏技巧。
  • 是的,好吧,不是那么乐观。 GGplot 是一个相当繁重的界面,悬停需要您非常频繁地上传您的绘图以使绘图“平滑”,这反过来又会产生一些滞后......所以这是滞后和绘图平滑度之间的一个糟糕的折衷......我看到你了试图修改delay 参数... ;-) 也许试试ggvis?或者在你的问题上用大笔赏金去阴谋?我在这里读到了一些有趣的东西:stackoverflow.com/questions/41232780/…
  • 感谢您的意见!这真的让我放心,我不会发疯。我没有想过 ggvis 所以也许这将是我的下一个努力,再次感谢!

标签: r ggplot2 shiny plotly r-plotly


【解决方案1】:

首先,您可以通过按shift 在情节中进行多个套索选择。

以下是对我的答案 here 的修改 - 所以它基于 plot_ly / plotlyProxy,修改现有的 plotly 对象(无需重新渲染)而不使用 ggplotly。由于 GitHub issuePR 中正在进行一些相关工作,因此以下答案可能不是 100% 可靠的(例如,缩放似乎把事情搞砸了 - 你可能想停用它)并且可能会过时。

不过,请检查以下内容:

library(plotly)
library(shiny)
library(htmlwidgets)

ui <- fluidPage(
  plotlyOutput("myPlot"),
  verbatimTextOutput("click")
)

server <- function(input, output, session) {
  
  js <- "
    function(el, x){
      var id = el.getAttribute('id');
      var gd = document.getElementById(id);
      Plotly.plot(id).then(attach);
        function attach() {
          var xaxis = gd._fullLayout.xaxis;
          var yaxis = gd._fullLayout.yaxis;

          var coordinates = [null, null]

          gd.addEventListener('click', function(evt) {
            var bb = evt.target.getBoundingClientRect();
            var x = xaxis.p2d(evt.clientX - bb.left);
            var y = yaxis.p2d(evt.clientY - bb.top);
            var coordinates = [x, y];
            Shiny.setInputValue('clickposition', coordinates);
          });
          gd.addEventListener('mousemove', function(evt) {
            var bb = evt.target.getBoundingClientRect();
            var x = xaxis.p2d(evt.clientX - bb.left);
            var y = yaxis.p2d(evt.clientY - bb.top);
            var coordinates = [x, y];
            Shiny.setInputValue('mouseposition', coordinates);
          });
        };
  }
  "
  
  output$myPlot <- renderPlotly({
    plot_ly(type = "scatter", mode = "markers") %>% layout(
      xaxis = list(range = c(0, 100)),
      yaxis = list(range = c(0, 100))) %>%
      onRender(js)
  })
  
  myPlotProxy <- plotlyProxy("myPlot", session)

  followMouse <- reactiveVal(FALSE)
  traceCount <- reactiveVal(0L)
  
  observeEvent(input$clickposition, {
    followMouse(!followMouse())
    
    if(followMouse()){
      plotlyProxyInvoke(myPlotProxy, "addTraces", list(x = list(input$clickposition[1]), y = list(input$clickposition[2])))  
      traceCount(traceCount()+1)
    }
  })
  
  observe({
    if(followMouse()){
      plotlyProxyInvoke(myPlotProxy, "extendTraces", list(x = list(list(input$mouseposition[1])), y = list(list(input$mouseposition[2]))), list(traceCount())) 
    }
  })

}

shinyApp(ui, server)


如果您希望使用单个跟踪:

library(plotly)
library(shiny)
library(htmlwidgets)

ui <- fluidPage(
  plotlyOutput("myPlot"),
  verbatimTextOutput("click")
)

server <- function(input, output, session) {
  
  js <- "
    function(el, x){
      var id = el.getAttribute('id');
      var gd = document.getElementById(id);
      Plotly.plot(id).then(attach);
        function attach() {
          var xaxis = gd._fullLayout.xaxis;
          var yaxis = gd._fullLayout.yaxis;

          var coordinates = [null, null]

          gd.addEventListener('click', function(evt) {
            var bb = evt.target.getBoundingClientRect();
            var x = xaxis.p2d(evt.clientX - bb.left);
            var y = yaxis.p2d(evt.clientY - bb.top);
            var coordinates = [x, y];
            Shiny.setInputValue('clickposition', coordinates);
          });
          gd.addEventListener('mousemove', function(evt) {
            var bb = evt.target.getBoundingClientRect();
            var x = xaxis.p2d(evt.clientX - bb.left);
            var y = yaxis.p2d(evt.clientY - bb.top);
            var coordinates = [x, y];
            Shiny.setInputValue('mouseposition', coordinates);
          });
        };
  }
  "
  
  output$myPlot <- renderPlotly({
    plot_ly(type = "scatter", mode = "markers") %>% layout(
      xaxis = list(range = c(0, 100)),
      yaxis = list(range = c(0, 100))) %>%
      onRender(js)
  })
  
  myPlotProxy <- plotlyProxy("myPlot", session)

  followMouse <- reactiveVal(FALSE)
  clickCount <- reactiveVal(0L)
  
  observeEvent(input$clickposition, {
    followMouse(!followMouse())
    clickCount(clickCount()+1)
    
    if(clickCount() == 1){
      plotlyProxyInvoke(myPlotProxy, "addTraces", list(x = list(input$clickposition[1]), y = list(input$clickposition[2])))
    }
    
  })
  
  observe({
    if(followMouse()){
      plotlyProxyInvoke(myPlotProxy, "extendTraces", list(x = list(list(input$mouseposition[1])), y = list(list(input$mouseposition[2]))), list(1)) 
    } else {
      plotlyProxyInvoke(myPlotProxy, "extendTraces", list(x = list(list(NA)), y = list(list(NA))), list(1))
    }
  })

}

shinyApp(ui, server)

【讨论】:

  • 哇,这里有一些很酷的东西!我会在接下来的一两天内尝试一下,当我有机会自己测试后接受您的答案时!还要感谢您指出 plotly 的多个套索,感觉我应该知道已经使用 plotly 几个月了.....
  • 您发布的代码多有趣。感谢您的分享,因为我一直在玩弄它并为各种迭代修改它时获得了很多乐趣。也许这是一个很好的迹象,我应该开始学习更多的 JS,所以谢谢你的回答!
  • 徒手画的坐标能找回来吗?
  • 可以通过input$mouseposition 访问它们。您可以将数据块保存到包含列表的reactiveVal 中,然后将其拆分为input$clickposition(就像我分隔跟踪一样)。
猜你喜欢
  • 1970-01-01
  • 2022-01-01
  • 2015-11-03
  • 1970-01-01
  • 2011-12-29
  • 2021-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多