1

我想改变下图中点击点的形状和大小。如何实现?对于这个玩具图,我将点数从原来的 100k 减少到了 2k。因此,预期的解决方案应该是高度可扩展的,并且不偏离原始图,即点击点更新前后的所有颜色应该相同。

library(shiny)
library(plotly)

df <- data.frame(X=runif(2000,0,2), Y=runif(2000,0,20), 
                 Type=c(rep(c('Type1','Type2'),600),
                        rep(c('Type3','Type4'),400)),
                 Val=sample(LETTERS,2000,replace=TRUE))

# table(df$Type, df$Val)

ui <- fluidPage(
  title = 'Select experiment',
  sidebarLayout(
    sidebarPanel(
      checkboxGroupInput("SelType", "Select Types to plot:",
                  choices = unique(df$Type),
                  selected = NA)
    ),
    mainPanel(
      plotlyOutput("plot", width = "400px"),
      verbatimTextOutput("click")
    )
  )
)

server <- function(input, output, session) {

  output$plot <- renderPlotly({
    if(length(input$SelType) != 0){
      df <- subset(df, Type %in% input$SelType)
      p <- ggplot(df, aes(X, Y, col = as.factor(Val))) + 
        geom_point()
    }else{
      p <- ggplot(df, aes(X, Y, col = as.factor(Val))) +
        geom_point()
    }
    ggplotly(p)  %>% layout(height = 800, width = 800)

  })

  output$click <- renderPrint({
    d <- event_data("plotly_click")
    if (is.null(d)) "Click events appear here (double-click to clear)" 
    else cat("Selected point associated with value: ", d$Val)
  })

}

shinyApp(ui, server)

在此处输入图像描述

一个相关的问题,但是用颜色突出显示点的方法不起作用(当变量的级别数很高时,很难对可能已经存在于图中的颜色进行硬编码) .

4

1 回答 1

4

Plotly 的restyle函数在这里对我们没有帮助,但我们仍然可以将onclick 事件与一点 JavaScript 一起使用。该代码在 10,000 分上具有可接受的性能。


我们可以使用以下方法获取在 JavaScript 中单击的点:

var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];

scatterlayer是所有散点图元素所在的层, scatter[n]是第n个散点图,point[p]是其中的第p个点)

现在我们只是把这一点放大很多(或者你想要的任何其他形状/变换):

point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');

为了获得恢复所有内容的可能性,我们将关于该点的未更改信息与其余的 Plotly 信息一起存储:

var plotly_div = document.getElementsByClassName('plotly')[0];
plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                     pointNumber: data.points[0].pointNumber,
                     d: point.attributes['d'].value
                    }

稍后我们可以恢复点:

var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
old_point.setAttribute('d', plotly_div.backup.d);

现在我们可以将所有代码添加到plotly小部件中。

javascript <- "
function(el, x){
  el.on('plotly_click', function(data) {
    var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
    var plotly_div = document.getElementsByClassName('plotly')[0];
    if (plotly_div.backup !== undefined) {
      var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
      if (old_point !== undefined) {
        old_point.setAttribute('d', plotly_div.backup.d);
      }
    }
    plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                         pointNumber: data.points[0].pointNumber,
                         d: point.attributes['d'].value,
                         style: point.attributes['style'].value
                        }

    point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
  });
}"

[...]

ggplotly(p) %>% onRender(javascript)

或者,您可以根据单击点的位置创建一个新的 SVG 元素,但颜色和形状是您想要的。


你可以在这里尝试没有 R/Shiny。

//create some random data
var data = [];
for (var i = 0; i < 10; i += 1) {
  data.push({x: [],
             y: [],
             mode: 'markers',
             type: 'scatter'});
  for (var p = 0; p < 200; p += 1) {
    data[i].x.push(Math.random());
    data[i].y.push(Math.random());
  }
}
//create the plot
var myDiv = document.getElementById('myDiv');
Plotly.newPlot(myDiv, data, layout = { hovermode:'closest'});

//add the same click event as the snippet above
myDiv.on('plotly_click', function(data) {
    //let's check if some traces are hidden

    var traces = document.getElementsByClassName('legend')[0].getElementsByClassName('traces');
    var realCurveNumber = data.points[0].curveNumber;
    for (var i = 0; i < data.points[0].curveNumber; i += 1) {
        if (traces[i].style['opacity'] < 1) {
            realCurveNumber -= 1
        }
    }

    data.points[0].curveNumber = realCurveNumber;
    var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
    var plotly_div = document.getElementsByClassName('plotly')[0];
    if (plotly_div.backup !== undefined) {
      var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
      if (old_point !== undefined) {
        old_point.setAttribute('d', plotly_div.backup.d);
      }
    }
    plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                         pointNumber: data.points[0].pointNumber,
                         d: point.attributes['d'].value,
                         style: point.attributes['style'].value
                        }

    point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
  });
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv">

于 2017-08-31T22:24:00.657 回答