简体   繁体   English

交互式地将点添加到plotly R中的子图,而不重绘背景图

[英]Interactively adding points to subplots in plotly R without redrawing background plot

This is a continuation of a previous post ( Interactively adding points to plotly R without redrawing background plot ). 这是前一篇文章的延续( 在没有重绘背景图的情况下,交互式地将点添加到plotly R )。 I am making a scatterplot matrix (using the R package ggpairs) of 32 mtcars dataset values. 我正在制作一个32 mtcars数据集值的散点图矩阵(使用R包ggpairs)。 My goal is to allow users to click on a point in any subplot. 我的目标是允许用户点击任何子图中的一个点。 This will cause a random subset (size can vary, but in the example below is 2) of rows from the original data frame to be overlaid in all scatterplot subplots (number of scatterplots can vary, but in the example below is 3). 这将导致原始数据帧中的行的随机子集(大小可以变化,但在下面的示例中为2)将在所有散点图子图中重叠(散点图的数量可以变化,但是在下面的示例中是3)。

My semi-working MWE is as follows - 我的半工作MWE如下 -

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

dat <- mtcars[,c(3,4,7)]
dat[,3] = dat[,3]*8

p <- ggpairs(dat)

myMax = max(abs(dat))
myMin = min(abs(dat))
myRange = c(myMax, myMin)

p2 <- p
for(x in 2:p$nrow) {
  for(y in 1:(x-1)) {
    p2[x,y] <- p[x,y] +
      coord_cartesian(xlim = c(myRange), ylim = c(myRange))
  }
}

p3 <- ggplotly(p2)

p3 %>% onRender("function(el, x, data) {

    // Number of rows in data frame is myLength=3
    myLength = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);

    // AxisNames stores the names of the 3 rows ('disp','hp','qsec')
    AxisNames = [];
    for (i = 1; i < (myLength+1); i++) {
      AxisNames.push(document.getElementsByClassName('infolayer')[0].childNodes[i].textContent);
    }

    el.on('plotly_click', function(e) {
      // Grab two random rows of the 32 rows from mtcars dataset and store in myData. In my real code (not this MWE), myData represents an array of 1 or more objects, where each object contains values for each column in the dataset.
      data1 = data[Math.floor(Math.random() * 32) + 1];
      data2 = data[Math.floor(Math.random() * 32) + 1];
      var myData = [data1, data2];

      //May not be necessary, but this creates one array allData that contains all column values for all randomly selected rows. Since this example has 3 columns (disp, hp, and qsec) and two randomly selected rows, allData has a length of 6.
      var allData = [];
      for (i = 0; i < myData.length; i++){
        for (j = 0 ; j < myLength; j++){
          allData.push(myData[i][AxisNames[j]])
        }
      }
      console.log(allData)

      //This correctly plots the disp on the x-axis and qsec on the y-axis of both randomly selected data frame rows and plots it into the correct scatterplot (bottom left one that has x-axis of disp and y-axis of qsec). This needs to be automated, so that the corresponding x and y values for the 2 randomly selected data frame rows are also plotted on all other scatterplot matrices.
      var trace1 = {
      x: [allData[0], allData[3]],
      y: [allData[2], allData[5]],
      mode: 'markers',
      marker: {
      color: 'green',
      size: 20
      }
      };

      Plotly.addTraces(el.id, trace1);
      }
      )}", data = dat)

What currently happens is the randomly selected rows are only plotted (in green) in the one subplot in the bottom left (instead of in all three scatterplots). 目前发生的是随机选择的行仅在左下角的一个子图中(而不是在所有三个散点图中)绘制(绿色)。 I am having difficultly accessing and plotting on any other scatterplot beside the bottom left one. 我很难访问和绘制左下角旁边的任何其他散点图。

ScatMatrix

I may be working on method with a much longer data frame (on order of thousands of row observations) and wider data frame (more than three columns, causing more than three scatterplots to be drawn). 我可能正在使用更长的数据框(按数千行观察的顺序)和更宽的数据框(超过三列,导致绘制三个以上的散点图)的方法。 So, I am trying to find an efficient way to achieve this goal so that the points do not take too long to draw. 所以,我试图找到一种有效的方法来实现这个目标,这样点数不需要太长时间来绘制。 I believe (from reading) that each Plotly.addTraces() can slow down the plotting time. 我相信(从阅读中)每个Plotly.addTraces()都可以减慢绘图时间。 If the data frame had, say, 6 columns, then there would be 15 scatterplots, and if each scatterplot had its own addTraces(), then there would be 15 addTraces(). 如果数据框有6列,则会有15个散点图,如果每个散点图都有自己的addTraces(),则会有15个addTraces()。 I wonder if that would render the plotting of the points too slow? 我想知道这是否会使点的绘图太慢? If so, I would be very eager to hear advice on how to achieve this goal the most efficiently (allowing the green points to be plotted as fast as possible on all scatterplots). 如果是这样,我会非常渴望听到关于如何最有效地实现这一目标的建议(允许在所有散点图上尽可能快地绘制绿点)。

I would be most grateful for any help or ideas! 我会非常感谢任何帮助或想法!

EDIT: 编辑:

Thanks to the input from NicE, I was able to update this script so that it does not need to hardcode the axis labels and variables to be used in each subplot. 感谢NicE的输入,我能够更新这个脚本,这样就不需要对每个子图中使用的轴标签和变量进行硬编码。 The updated MWE is below: 更新后的MWE如下:

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

dat <- mtcars[,c(3,4,7)]
dat[,3] = dat[,3]*8

p <- ggpairs(dat)

myMax = max(abs(dat))
myMin = min(abs(dat))
myRange = c(myMax, myMin)

p2 <- p
for(x in 2:p$nrow) {
  for(y in 1:(x-1)) {
    p2[x,y] <- p[x,y] +
      coord_cartesian(xlim = c(myRange), ylim = c(myRange))
  }
}

p3 <- ggplotly(p2)

p3 %>% onRender("function(el, x, data) {

                len = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);

                // AxisNames stores the names of the 3 rows ('disp','hp','qsec')
                AxisNames = [];
                for (i = 1; i < (len+1); i++) {
                AxisNames.push(document.getElementsByClassName('infolayer')[0].childNodes[i].textContent);
                }

                el.on('plotly_click', function(e) {
                  data1 = data[Math.floor(Math.random() * 32) + 1];
                  data2 = data[Math.floor(Math.random() * 32) + 1];
                  var myData = [data1, data2];
                  console.log(myData);

                  var Traces = [];
                  var i=0;
                  var k=1;
                  while ((i*len+k)<=Math.pow((len-1),2)) {
                        var xArr = [];
                        for (a=0; a<myData.length; a++){
                          xArr.push(myData[a][AxisNames[i]])
                        }
                    while ((i+k)<len){
                        var yArr = [];
                        for (a=0; a<myData.length; a++){
                          yArr.push(myData[a][AxisNames[(len-k)]])
                        }

                      var trace = {
                        x: xArr,
                        y: yArr,
                        mode: 'markers',
                        marker: {
                          color: 'green',
                          size: 20
                        },
                        xaxis: 'x' + (i+1),
                        yaxis: 'y' + (i*len+k)
                      };
                      Traces.push(trace);
                      k++;
                    }
                    i++;
                    k=1;
                  }
                  Plotly.addTraces(el.id, Traces);
                }
                )}", data = dat)

You can add an xaxis and yaxis to your traces to specify to which plot the trace needs to be added. 您可以在添加xaxisyaxis ,以你的痕迹,指定跟踪需要添加哪些阴谋。

In your case, the xaxis for the first column is x , the second is x2 , and third x3 . 在您的情况下,第一列的x xaxisx ,第二列是x2 ,第三列是x3 The yaxis of the bottom-left plot is y and it increases going up, y2 for the one above, y3 for the top one of the first column, then y4 for the middle column bottom plot etc... yaxis左下图的是y而且增加往上走, y2上面的一个, y3第一列中顶部的一个,然后y4为中塔底部的情节等等...

For example, you could do, in your onRender : 例如,您可以在onRender

var trace1 = {
  x: [myData[0]['disp'], myData[1]['disp']],
  y: [myData[0]['qsec'], myData[0]['qsec']],
  mode: 'markers',
  marker: {
    color: 'green',
    size: 20
  },
  xaxis: 'x',
  yaxis: 'y'
};

var trace2 = {
  x: [myData[0]['disp'], myData[1]['disp']],
  y: [myData[0]['hp'], myData[0]['hp']],
  mode: 'markers',
  marker: {
    color: 'green',
    size: 20
  },
  xaxis: 'x',
  yaxis: 'y2'
};

var trace3 = {
  x: [myData[0]['hp'], myData[0]['hp']],
  y: [myData[0]['qsec'], myData[0]['qsec']],
  mode: 'markers',
  marker: {
    color: 'green',
    size: 20
  },
  xaxis: 'x2',
  yaxis: 'y4'
};

Plotly.addTraces(el.id, [trace1,trace2,trace3]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM