Интерактивное добавление точек к участкам в графике R без перерисовки фонового графика
Это продолжение предыдущего поста ( Интерактивное добавление точек к графику R без перерисовки фонового графика). Я делаю матрицу диаграммы рассеяния (используя ggpairs пакета R) из значений набора данных 32 mtcars. Моя цель - дать пользователям возможность щелкнуть точку в любом из участков. Это приведет к тому, что случайное подмножество (размер может варьироваться, но в приведенном ниже примере равно 2) строк из исходного фрейма данных, которые будут наложены на все вспомогательные участки диаграммы рассеяния (число диаграмм рассеяния может варьироваться, но в приведенном ниже примере это 3).
Мой полу-рабочий 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)
В настоящее время происходит случайное выделение строк (отображаются зеленым цветом) только в одном подплощадке слева внизу (а не во всех трех точках рассеяния). У меня трудный доступ и построение графиков на любом другом графике рассеяния, кроме нижнего левого.
Возможно, я работаю над методом с гораздо более длинным фреймом данных (порядка тысяч наблюдений за строками) и более широким фреймом данных (более трех столбцов, в результате чего рисуется более трех диаграмм рассеяния). Поэтому я пытаюсь найти эффективный способ достижения этой цели, чтобы очки не заняли слишком много времени. Я полагаю (из чтения), что каждый Plotly.addTraces() может замедлить время прорисовки. Если бы фрейм данных имел, скажем, 6 столбцов, то было бы 15 диаграмм рассеяния, и если бы каждая диаграмма рассеяния имела свои собственные addTraces(), то было бы 15 addTraces(). Интересно, если бы это сделало рисование точек слишком медленным? Если это так, я бы очень хотел услышать совет о том, как достичь этой цели наиболее эффективно (позволяя наносить зеленые точки как можно быстрее на всех точках рассеяния).
Буду очень признателен за любую помощь или идеи!
РЕДАКТИРОВАТЬ:
Благодаря вкладу NicE я смог обновить этот скрипт, чтобы ему не нужно было жестко кодировать метки осей и переменные, которые будут использоваться в каждом подзадаче. Обновленный 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)
1 ответ
Вы можете добавить xaxis
а также yaxis
к вашим трассам, чтобы указать, к какому графику трасса должна быть добавлена.
В вашем случае xaxis
для первого столбца x
второе x2
и третий x3
, yaxis
нижнего левого участка y
и это увеличивает идти вверх, y2
для вышеупомянутого, y3
для верхней части первого столбца, затем y4
для нижней колонки среднего столбца и т.д...
Например, вы могли бы сделать в своем 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]);