Javascript 在htmlWidgets的onRender()中修改选定点的有效方法

Javascript 在htmlWidgets的onRender()中修改选定点的有效方法,javascript,r,shiny,plotly,htmlwidgets,Javascript,R,Shiny,Plotly,Htmlwidgets,我正在制作一个交互式图形,为两个定量变量(A和B)绘制黑点。用户可以选择一个阈值。执行此操作后,只会绘制高于该阈值幅值的点,并绘制一个表示阈值未通过区域的灰色矩形。这一切似乎都运作良好 然而,我现在试图实现的是允许用户选择某些点。执行此操作时,选定点应从黑色变为红色。当用户选择新的点子集时,先前选择的点将从红色返回到黑色。换句话说,在绘图中显示为红色的点应该是最后选定的点 不幸的是,这部分似乎不起作用。目前的问题是: 1) 选定的点将永久保持红色。 2) 如果已选择红色点,有时未选择的黑色点会变

我正在制作一个交互式图形,为两个定量变量(A和B)绘制黑点。用户可以选择一个阈值。执行此操作后,只会绘制高于该阈值幅值的点,并绘制一个表示阈值未通过区域的灰色矩形。这一切似乎都运作良好

然而,我现在试图实现的是允许用户选择某些点。执行此操作时,选定点应从黑色变为红色。当用户选择新的点子集时,先前选择的点将从红色返回到黑色。换句话说,在绘图中显示为红色的点应该是最后选定的点

不幸的是,这部分似乎不起作用。目前的问题是:

1) 选定的点将永久保持红色。 2) 如果已选择红色点,有时未选择的黑色点会变为红色。您可以在文章底部看到此错误的示例(在图片中)

我相信这是因为我只是简单地绘制了一个与所选x和y值相对应的全新的红点轨迹(下面的代码注释为“//Add user selected points in red”)。因此,红点成为它们自己的实体

我的问题是:解决这个问题的有效方法是什么(使选定的点变为红色,直到选择另一个子集?)我使用高效这个术语,因为我使用的是大型数据集(100-1000s点),因此我正在寻找节省计算速度的方法。简单地将选定的黑点修改为红色可能比完全重新绘制叠加的红色点更有效?但是,我不确定如何使用Plotly trace语法实现这一点

任何帮助都将不胜感激

下面是我脚本的简化版本

library(plotly)
library(GGally)
library(htmlwidgets)

ui <- shinyUI(fluidPage(
  sliderInput("thresh", "Threshold:", min = 0, max = 3, value=1, step=1),
  plotlyOutput("myPlot"),
  textOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  thresh <- reactive(input$thresh)

  set.seed(1)
  dat <- data.frame(Row = paste0("Row",sample(c(1:20),20)), A=4*rnorm(20), B=4*rnorm(20))
  dat$Row <- as.character(dat$Row)

  minVal = min(dat[,-1])
  maxVal = max(dat[,-1])

  gg <- ggplot(data = dat, aes(x=A, y=B)) + coord_cartesian(xlim = c(minVal, maxVal), ylim = c(minVal, maxVal))

  ggY <- ggplotly(gg)

  output$myPlot <- renderPlotly(ggY %>% onRender("
    function(el, x, data) {
    var Points = [];
    var Traces = [];
    var selRows = [];

    data.dat.forEach(function(row){
    if(Math.abs(row['B']) > data.thresh) selRows.push(row);});

    var xArr = [];
    var yArr = [];
    var keepIndex = [];
    for (a=0; a<selRows.length; a++){
      xArr.push(selRows[a]['A'])
      yArr.push(selRows[a]['B'])
      keepIndex.push(selRows[a]['Row'])
    }
    Points.push(keepIndex);

    // Add points above the threshold in black
    var tracePoints = {
      x: xArr,
      y: yArr,
      hoverinfo: 'none',
      mode: 'markers',
      marker: {
        color: 'black',
        size: 4
      }
    };

    // Add upper horizontal line of gray box
    var hiLine = {
      x: [-15,15],
      y: [data.thresh,data.thresh],
      mode: 'lines',
      line: {
        color: 'gray',
        width: 1
      },
      opacity: 0.25,
      hoverinfo: 'none'
    };

    // Add lower horizontal line of gray box
    var lowLine = {
      x: [-15,15],
      y: [-1*data.thresh,-1*data.thresh],
      mode: 'lines',
      fill: 'tonexty',
      line: {
        color: 'gray',
        width: 1
      },
      opacity: 0.25,
      hoverinfo: 'none'
    };

    Traces.push(tracePoints);
    Traces.push(hiLine);
    Traces.push(lowLine);
    Plotly.addTraces(el.id, Traces);

    var idRows = []
    for (a=0; a<data.dat.length; a++){
      idRows.push(data.dat[a]['Row'])
    }

    el.on('plotly_selected', function(e) {
      numSel = e.points.length
      cN = e.points[0].curveNumber;

      var pointNumbers = [];
      var selData = [];
      for (a=0; a<numSel; a++){
        pointNumbers.push(e.points[a].pointNumber)
        selData.push(data.dat[idRows.indexOf(Points[0][pointNumbers[a]])])
      }
      Shiny.onInputChange('selData', selData);
      var Traces = [];
      var xArr = [];
      var yArr = [];
      for (a=0; a<selData.length; a++){
        xArr.push(selData[a]['A'])
        yArr.push(selData[a]['B'])
      }

      // Add user-selected points in red
      var traceRed = {
        x: xArr,
        y: yArr,
        mode: 'markers',
        marker: {
          color: 'red',
          size: 4
        },
        hoverinfo: 'none'
      };
      Traces.push(traceRed);

      Plotly.addTraces(el.id, Traces);
    })

    }", data = list(dat=dat, thresh=thresh())))

  selData <- reactive(input$selData)
  output$selectedValues <- renderPrint({selData()})

})

shinyApp(ui, server)
library(plotly)
图书馆(GGALY)
库(htmlwidgets)

ui处理这个问题的最简单的方法是简单地跟踪您是否选择了某个对象,如果之前有选择,则删除包含所选点的先前跟踪。为此,进行了以下更改:

  • 添加了一个计数器
    跟踪
    ,以跟踪我们选择事物的频率
  • 添加了一个
    Plotly.deleteTraces
    语句来删除最后一个跟踪(可以使用索引-1指定)
  • 删除操作由
    if(nseltrace>0)
    语句保护
  • 重新格式化选择点以将其放入数据帧中(它们以字符向量的形式叠加在一起)
  • 使用
    verbatumTextOutput
    将它们打印出来,因为它是一种更干净的格式
更新:
  • 添加了一个保护,使其仅选择属于
    curveNumber
    1的点,即原始数据,而不是随后的选定轨迹。这导致已选定点在新选定列表中输入两次
至于这是否是最有效的方法,显然要视情况而定。你在那里绘制的是选定的点。以前的非选定表示法,最坏的情况是两倍,但点绘制速度非常快,一小部分点的典型减速甚至可能无法测量。我不会对此进行优化除非后来证明这是个问题,然后我会使用最新的plotly,因为plotly代码库中目前正在进行大量优化(和bug消除)

代码如下:

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

ui <- shinyUI(fluidPage(
  sliderInput("thresh", "Threshold:", min = 0, max = 3, value=1, step=1),
  plotlyOutput("myPlot"),
  verbatimTextOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  thresh <- reactive(input$thresh)

  set.seed(1)
  dat <- data.frame(Row = paste0("Row",sample(c(1:20),20)), A=4*rnorm(20), B=4*rnorm(20))
  dat$Row <- as.character(dat$Row)

  minVal = min(dat[,-1])
  maxVal = max(dat[,-1])

  gg <- ggplot(data = dat, aes(x=A, y=B)) + coord_cartesian(xlim = c(minVal, maxVal), ylim = c(minVal, maxVal))

  ggY <- ggplotly(gg)

  output$myPlot <- renderPlotly(ggY %>% onRender("
                                                 function(el, x, data) {
                                                 var Points = [];
                                                 var Traces = [];
                                                 var selRows = [];

                                                 data.dat.forEach(function(row){
                                                 if(Math.abs(row['B']) > data.thresh) selRows.push(row);});

                                                 var xArr = [];
                                                 var yArr = [];
                                                 var keepIndex = [];
                                                 for (a=0; a<selRows.length; a++){
                                                 xArr.push(selRows[a]['A'])
                                                 yArr.push(selRows[a]['B'])
                                                 keepIndex.push(selRows[a]['Row'])
                                                 }
                                                 Points.push(keepIndex);

                                                 // Add points above the threshold in black
                                                 var tracePoints = {
                                                 x: xArr,
                                                 y: yArr,
                                                 hoverinfo: 'none',
                                                 mode: 'markers',
                                                 marker: {
                                                 color: 'black',
                                                 size: 4
                                                 }
                                                 };

                                                 // Add upper horizontal line of gray box
                                                 var hiLine = {
                                                 x: [-15,15],
                                                 y: [data.thresh,data.thresh],
                                                 mode: 'lines',
                                                 line: {
                                                 color: 'gray',
                                                 width: 1
                                                 },
                                                 opacity: 0.25,
                                                 hoverinfo: 'none'
                                                 };

                                                 // Add lower horizontal line of gray box
                                                 var lowLine = {
                                                 x: [-15,15],
                                                 y: [-1*data.thresh,-1*data.thresh],
                                                 mode: 'lines',
                                                 fill: 'tonexty',
                                                 line: {
                                                 color: 'gray',
                                                 width: 1
                                                 },
                                                 opacity: 0.25,
                                                 hoverinfo: 'none'
                                                 };

                                                 Traces.push(tracePoints);
                                                 Traces.push(hiLine);
                                                 Traces.push(lowLine);
                                                 Plotly.addTraces(el.id, Traces);

                                                 var idRows = []
                                                 for (a=0; a<data.dat.length; a++){
                                                 idRows.push(data.dat[a]['Row'])
                                                 }

                                                 var nseltrace = 0;
                                                 el.on('plotly_selected', function(e) {
                                                 console.log(e.points)
                                                 numSel = e.points.length

                                                 var pointNumbers = [];
                                                 var selData = [];
                                                 for (a=0; a<numSel; a++){
                                                   if (e.points[a].curveNumber==1){  // restrict to points in the original curve
                                                     pointNumbers.push(e.points[a].pointNumber)
                                                     selData.push(data.dat[idRows.indexOf(Points[0][pointNumbers[a]])])
                                                   }
                                                 }
                                                 Shiny.onInputChange('selData', selData);
                                                 var Traces = [];
                                                 var xArr = [];
                                                 var yArr = [];
                                                 for (a=0; a<selData.length; a++){
                                                 xArr.push(selData[a]['A'])
                                                 yArr.push(selData[a]['B'])
                                                 }

                                                 // Add user-selected points in red
                                                 var traceRed = {
                                                 x: xArr,
                                                 y: yArr,
                                                 mode: 'markers',
                                                 marker: {
                                                 color: 'red',
                                                 size: 4
                                                 },
                                                 hoverinfo: 'none'
                                                 };

                                                 if (nseltrace>0){
                                                 Plotly.deleteTraces(el.id,-1)
                                                 }
                                                 Traces.push(traceRed);
                                                 nseltrace = nseltrace+1

                                                 Plotly.addTraces(el.id, Traces);
                                                 })

                                                 }", data = list(dat=dat, thresh=thresh())))

  selData <- reactive({
    req(input$selData)
    rawc <- input$selData
    df <- data.frame(t(matrix(rawc,nrow=3)))
    names(df) <- names(rawc)[1:3]
    df
  })
  output$selectedValues <- renderPrint({selData()})

})

shinyApp(ui, server)
library(plotly)
图书馆(GGALY)
库(htmlwidgets)
图书馆(闪亮)

ui处理此问题的最简单的方法是简单地跟踪您是否选择了某个内容,如果以前有选择,则删除包含所选点的先前跟踪。为此,进行了以下更改:

  • 添加了一个计数器
    跟踪
    ,以跟踪我们选择事物的频率
  • 添加了一个
    Plotly.deleteTraces
    语句来删除最后一个跟踪(可以使用索引-1指定)
  • 删除操作由
    if(nseltrace>0)
    语句保护
  • 重新格式化选择点以将其放入数据帧中(它们以字符向量的形式叠加在一起)
  • 使用
    verbatumTextOutput
    将它们打印出来,因为它是一种更干净的格式
更新:
  • 添加了一个保护,使其仅选择属于
    curveNumber
    1的点,即原始数据,而不是随后的选定轨迹。这导致已选定点在新选定列表中输入两次
至于这是否是最有效的方法,显然要视情况而定。你在那里绘制的是选定的点。以前的非选定表示法,最坏的情况是两倍,但点绘制速度非常快,一小部分点的典型减速甚至可能无法测量。我不会对此进行优化除非后来证明这是个问题,然后我会使用最新的plotly,因为plotly代码库中目前正在进行大量优化(和bug消除)

代码如下:

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

ui <- shinyUI(fluidPage(
  sliderInput("thresh", "Threshold:", min = 0, max = 3, value=1, step=1),
  plotlyOutput("myPlot"),
  verbatimTextOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  thresh <- reactive(input$thresh)

  set.seed(1)
  dat <- data.frame(Row = paste0("Row",sample(c(1:20),20)), A=4*rnorm(20), B=4*rnorm(20))
  dat$Row <- as.character(dat$Row)

  minVal = min(dat[,-1])
  maxVal = max(dat[,-1])

  gg <- ggplot(data = dat, aes(x=A, y=B)) + coord_cartesian(xlim = c(minVal, maxVal), ylim = c(minVal, maxVal))

  ggY <- ggplotly(gg)

  output$myPlot <- renderPlotly(ggY %>% onRender("
                                                 function(el, x, data) {
                                                 var Points = [];
                                                 var Traces = [];
                                                 var selRows = [];

                                                 data.dat.forEach(function(row){
                                                 if(Math.abs(row['B']) > data.thresh) selRows.push(row);});

                                                 var xArr = [];
                                                 var yArr = [];
                                                 var keepIndex = [];
                                                 for (a=0; a<selRows.length; a++){
                                                 xArr.push(selRows[a]['A'])
                                                 yArr.push(selRows[a]['B'])
                                                 keepIndex.push(selRows[a]['Row'])
                                                 }
                                                 Points.push(keepIndex);

                                                 // Add points above the threshold in black
                                                 var tracePoints = {
                                                 x: xArr,
                                                 y: yArr,
                                                 hoverinfo: 'none',
                                                 mode: 'markers',
                                                 marker: {
                                                 color: 'black',
                                                 size: 4
                                                 }
                                                 };

                                                 // Add upper horizontal line of gray box
                                                 var hiLine = {
                                                 x: [-15,15],
                                                 y: [data.thresh,data.thresh],
                                                 mode: 'lines',
                                                 line: {
                                                 color: 'gray',
                                                 width: 1
                                                 },
                                                 opacity: 0.25,
                                                 hoverinfo: 'none'
                                                 };

                                                 // Add lower horizontal line of gray box
                                                 var lowLine = {
                                                 x: [-15,15],
                                                 y: [-1*data.thresh,-1*data.thresh],
                                                 mode: 'lines',
                                                 fill: 'tonexty',
                                                 line: {
                                                 color: 'gray',
                                                 width: 1
                                                 },
                                                 opacity: 0.25,
                                                 hoverinfo: 'none'
                                                 };

                                                 Traces.push(tracePoints);
                                                 Traces.push(hiLine);
                                                 Traces.push(lowLine);
                                                 Plotly.addTraces(el.id, Traces);

                                                 var idRows = []
                                                 for (a=0; a<data.dat.length; a++){
                                                 idRows.push(data.dat[a]['Row'])
                                                 }

                                                 var nseltrace = 0;
                                                 el.on('plotly_selected', function(e) {
                                                 console.log(e.points)
                                                 numSel = e.points.length

                                                 var pointNumbers = [];
                                                 var selData = [];
                                                 for (a=0; a<numSel; a++){
                                                   if (e.points[a].curveNumber==1){  // restrict to points in the original curve
                                                     pointNumbers.push(e.points[a].pointNumber)
                                                     selData.push(data.dat[idRows.indexOf(Points[0][pointNumbers[a]])])
                                                   }
                                                 }
                                                 Shiny.onInputChange('selData', selData);
                                                 var Traces = [];
                                                 var xArr = [];
                                                 var yArr = [];
                                                 for (a=0; a<selData.length; a++){
                                                 xArr.push(selData[a]['A'])
                                                 yArr.push(selData[a]['B'])
                                                 }

                                                 // Add user-selected points in red
                                                 var traceRed = {
                                                 x: xArr,
                                                 y: yArr,
                                                 mode: 'markers',
                                                 marker: {
                                                 color: 'red',
                                                 size: 4
                                                 },
                                                 hoverinfo: 'none'
                                                 };

                                                 if (nseltrace>0){
                                                 Plotly.deleteTraces(el.id,-1)
                                                 }
                                                 Traces.push(traceRed);
                                                 nseltrace = nseltrace+1

                                                 Plotly.addTraces(el.id, Traces);
                                                 })

                                                 }", data = list(dat=dat, thresh=thresh())))

  selData <- reactive({
    req(input$selData)
    rawc <- input$selData
    df <- data.frame(t(matrix(rawc,nrow=3)))
    names(df) <- names(rawc)[1:3]
    df
  })
  output$selectedValues <- renderPrint({selData()})

})

shinyApp(ui, server)
library(plotly)
图书馆(GGALY)
库(htmlwidgets)
图书馆(闪亮)

ui我真的很感谢你提供的有用的想法。不幸的是,我认为当用户的选择包含任何已经红点的子集时发生的错误仍然存在。我添加了一个示例(带有图像)在我的帖子底部。再次感谢。当我使用我粘贴在这里的代码时,我看到的不是我看到的。每次我选择之前的点都会消失。你使用我发布的代码了吗?哦,好吧,我现在看到了这个问题。它与以前不同,重新选择在某种程度上有所不同。让我看看。谢谢。错误非常狡猾,而且在两个方面都存在我们的方法和我的方法。是的,你发布的代码