在 JavaScript 中检索 R 对象属性 - 第 2 部分
Retrieving R object attributes in JavaScript - Part 2
我之前 post 回答过类似的问题 ()。在之前的 post 中,我过度简化了我的 MWE,因此不幸的是,我得到的答案并不真正适用于我的实际问题。在这里,我展示了为什么我可能需要在 JavaScript 中检索 R 对象属性(除非有另一个我不知道的选项)。
我有一个包含 100 个观测值的 5 变量数据集。我使用了六边形合并并创建了一个散点图矩阵。 10 个散点图中的每一个都有 12-18 个六边形。为了保存所有 10 个散点图的每个六边形箱中的 100 个观察值的行,我在 R 中使用了 base::attr 函数。在下面的代码中,这是在以下位置完成的:
attr(hexdf, "cID") <- h@cID
我正在尝试创建六边形分箱的交互式 R Plotly 对象,这样如果用户单击给定的六边形分箱(无论是哪个散点图),他们将获得 100 个观察值的行分组到那个箱子里。我已经完成了这个目标的一部分。我的 MWE 如下:
library(plotly)
library(data.table)
library(GGally)
library(hexbin)
library(htmlwidgets)
set.seed(1)
bindata <- data.frame(ID = paste0("ID",1:100), A=rnorm(100), B=rnorm(100), C=rnorm(100), D=rnorm(100), E=rnorm(100))
bindata$ID <- as.character(bindata$ID)
maxVal = max(abs(bindata[,2:6]))
maxRange = c(-1*maxVal, maxVal)
my_fn <- function(data, mapping, ...){
x = data[,c(as.character(mapping$x))]
y = data[,c(as.character(mapping$y))]
h <- hexbin(x=x, y=y, xbins=5, shape=1, IDs=TRUE, xbnds=maxRange, ybnds=maxRange)
hexdf <- data.frame (hcell2xy (h), hexID = h@cell, counts = h@count)
attr(hexdf, "cID") <- h@cID
p <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) + geom_hex(stat="identity")
p
}
p <- ggpairs(bindata[,2:6], lower = list(continuous = my_fn))
pS <- p
for(i in 2:p$nrow) {
for(j in 1:(i-1)) {
pS[i,j] <- p[i,j] +
coord_cartesian(xlim = c(maxRange[1], maxRange[2]), ylim = c(maxRange[1], maxRange[2]))
}
}
ggPS <- ggplotly(pS)
myLength <- length(ggPS[["x"]][["data"]])
for (i in 1:myLength){
item =ggPS[["x"]][["data"]][[i]]$text[1]
if (!is.null(item))
if (!startsWith(item, "co")){
ggPS[["x"]][["data"]][[i]]$hoverinfo <- "none"
}
}
ggPS %>% onRender("
function(el, x, data) {
el = el;
x=x;
var data = data[0];
console.log(el)
console.log(x)
console.log(data)
myLength = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);
console.log(myLength)
el.on('plotly_click', function(e) {
console.log(e.points[0])
xVar = (e.points[0].xaxis._id).replace(/[^0-9]/g,'')
if (xVar.length == 0) xVar = 1
yVar = (e.points[0].yaxis._id).replace(/[^0-9]/g,'')
if (yVar.length == 0) yVar = 1
myX = myLength + 1 - (yVar - myLength * (xVar - 1))
myY = xVar
cN = e.points[0].curveNumber
split1 = (x.data[cN].text).split(' ')
hexID = (x.data[cN].text).split(' ')[2]
counts = split1[1].split('<')[0]
console.log(myX)
console.log(myY)
console.log(hexID)
console.log(counts)
})}
", data = pS[5,2]$data)
这将创建如下所示的图像:
举个例子,如果我点击绿色框内高亮的六边形,我可以判断它发生在哪个子图中("myX"和"myY"),点击的六边形ID( "hexID"),以及分箱到该六边形中的观察点数 ("counts")。对于这个特定的六边形,myX=5、myY=2、hexID=39 和 counts=1。因此,用户只需在第五行第二列的散点图中单击 ID39 的六边形,应该有 1 个数据点被合并。
如果我离开 onRender() 函数,只需在 R 中输入以下代码:
myX <- 5
myY <- 2
hexID <- 39
obsns <- which(attr(pS[myX,myY]$data, "cID")==hexID)
dat <- bindata[obsns,]
然后,我可以获得数据框的行,其中包含被合并到该单击的六边形中的一个观察值:
> dat
ID A B C D E
95 ID95 1.586833 -1.208083 1.778429 -0.1101588 3.810277
我的问题就出在这最后一步。我无法弄清楚如何从 onRender() 函数中使用 base::attr() 函数来获取 "obsns" 对象。这个问题是否有任何解决方法,或者我应该考虑采取其他可能的方法?感谢任何 ideas/advice!
我不确定您是否可以从 plotly
访问十六进制 ID,或者它是否将这些数据保存在某个地方,因此一种选择是将您需要的所有数据传递给 onRender
函数.
首先,您可以向 bindata
数据帧中的每个 hexplot 添加一列,称为 mX-mY
(您将 mX 和 mY 替换为每列的值),这将适用于每次观察它属于该地块的 hexbin:
for(i in 2:5) {
for(j in 1:4) {
bindata[[paste(i,j,sep="-")]] <- attr(pS[i,j]$data, "cID")
}
}
然后您可以将 bindata
传递给 onRender
函数,无论何时单击其中一个图中的六边形,检查 bindata
中相应的列,哪些观测值属于那个 hexbin:
ggPS %>% onRender("
function(el, x, data) {
myLength = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);
el.on('plotly_click', function(e) {
xVar = (e.points[0].xaxis._id).replace(/[^0-9]/g,'')
if (xVar.length == 0) xVar = 1
yVar = (e.points[0].yaxis._id).replace(/[^0-9]/g,'')
if (yVar.length == 0) yVar = 1
myX = myLength + 1 - (yVar - myLength * (xVar - 1))
myY = xVar
cN = e.points[0].curveNumber
split1 = (x.data[cN].text).split(' ')
hexID = (x.data[cN].text).split(' ')[2]
counts = split1[1].split('<')[0]
var selected_rows = [];
data.forEach(function(row){
if(row[myX+'-'+myY]==hexID) selected_rows.push(row);
});
console.log(selected_rows);
})}
", data = bindata)
我之前 post 回答过类似的问题 (
我有一个包含 100 个观测值的 5 变量数据集。我使用了六边形合并并创建了一个散点图矩阵。 10 个散点图中的每一个都有 12-18 个六边形。为了保存所有 10 个散点图的每个六边形箱中的 100 个观察值的行,我在 R 中使用了 base::attr 函数。在下面的代码中,这是在以下位置完成的:
attr(hexdf, "cID") <- h@cID
我正在尝试创建六边形分箱的交互式 R Plotly 对象,这样如果用户单击给定的六边形分箱(无论是哪个散点图),他们将获得 100 个观察值的行分组到那个箱子里。我已经完成了这个目标的一部分。我的 MWE 如下:
library(plotly)
library(data.table)
library(GGally)
library(hexbin)
library(htmlwidgets)
set.seed(1)
bindata <- data.frame(ID = paste0("ID",1:100), A=rnorm(100), B=rnorm(100), C=rnorm(100), D=rnorm(100), E=rnorm(100))
bindata$ID <- as.character(bindata$ID)
maxVal = max(abs(bindata[,2:6]))
maxRange = c(-1*maxVal, maxVal)
my_fn <- function(data, mapping, ...){
x = data[,c(as.character(mapping$x))]
y = data[,c(as.character(mapping$y))]
h <- hexbin(x=x, y=y, xbins=5, shape=1, IDs=TRUE, xbnds=maxRange, ybnds=maxRange)
hexdf <- data.frame (hcell2xy (h), hexID = h@cell, counts = h@count)
attr(hexdf, "cID") <- h@cID
p <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) + geom_hex(stat="identity")
p
}
p <- ggpairs(bindata[,2:6], lower = list(continuous = my_fn))
pS <- p
for(i in 2:p$nrow) {
for(j in 1:(i-1)) {
pS[i,j] <- p[i,j] +
coord_cartesian(xlim = c(maxRange[1], maxRange[2]), ylim = c(maxRange[1], maxRange[2]))
}
}
ggPS <- ggplotly(pS)
myLength <- length(ggPS[["x"]][["data"]])
for (i in 1:myLength){
item =ggPS[["x"]][["data"]][[i]]$text[1]
if (!is.null(item))
if (!startsWith(item, "co")){
ggPS[["x"]][["data"]][[i]]$hoverinfo <- "none"
}
}
ggPS %>% onRender("
function(el, x, data) {
el = el;
x=x;
var data = data[0];
console.log(el)
console.log(x)
console.log(data)
myLength = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);
console.log(myLength)
el.on('plotly_click', function(e) {
console.log(e.points[0])
xVar = (e.points[0].xaxis._id).replace(/[^0-9]/g,'')
if (xVar.length == 0) xVar = 1
yVar = (e.points[0].yaxis._id).replace(/[^0-9]/g,'')
if (yVar.length == 0) yVar = 1
myX = myLength + 1 - (yVar - myLength * (xVar - 1))
myY = xVar
cN = e.points[0].curveNumber
split1 = (x.data[cN].text).split(' ')
hexID = (x.data[cN].text).split(' ')[2]
counts = split1[1].split('<')[0]
console.log(myX)
console.log(myY)
console.log(hexID)
console.log(counts)
})}
", data = pS[5,2]$data)
这将创建如下所示的图像:
举个例子,如果我点击绿色框内高亮的六边形,我可以判断它发生在哪个子图中("myX"和"myY"),点击的六边形ID( "hexID"),以及分箱到该六边形中的观察点数 ("counts")。对于这个特定的六边形,myX=5、myY=2、hexID=39 和 counts=1。因此,用户只需在第五行第二列的散点图中单击 ID39 的六边形,应该有 1 个数据点被合并。
如果我离开 onRender() 函数,只需在 R 中输入以下代码:
myX <- 5
myY <- 2
hexID <- 39
obsns <- which(attr(pS[myX,myY]$data, "cID")==hexID)
dat <- bindata[obsns,]
然后,我可以获得数据框的行,其中包含被合并到该单击的六边形中的一个观察值:
> dat
ID A B C D E
95 ID95 1.586833 -1.208083 1.778429 -0.1101588 3.810277
我的问题就出在这最后一步。我无法弄清楚如何从 onRender() 函数中使用 base::attr() 函数来获取 "obsns" 对象。这个问题是否有任何解决方法,或者我应该考虑采取其他可能的方法?感谢任何 ideas/advice!
我不确定您是否可以从 plotly
访问十六进制 ID,或者它是否将这些数据保存在某个地方,因此一种选择是将您需要的所有数据传递给 onRender
函数.
首先,您可以向 bindata
数据帧中的每个 hexplot 添加一列,称为 mX-mY
(您将 mX 和 mY 替换为每列的值),这将适用于每次观察它属于该地块的 hexbin:
for(i in 2:5) {
for(j in 1:4) {
bindata[[paste(i,j,sep="-")]] <- attr(pS[i,j]$data, "cID")
}
}
然后您可以将 bindata
传递给 onRender
函数,无论何时单击其中一个图中的六边形,检查 bindata
中相应的列,哪些观测值属于那个 hexbin:
ggPS %>% onRender("
function(el, x, data) {
myLength = Math.sqrt(document.getElementsByClassName('cartesianlayer')[0].childNodes.length);
el.on('plotly_click', function(e) {
xVar = (e.points[0].xaxis._id).replace(/[^0-9]/g,'')
if (xVar.length == 0) xVar = 1
yVar = (e.points[0].yaxis._id).replace(/[^0-9]/g,'')
if (yVar.length == 0) yVar = 1
myX = myLength + 1 - (yVar - myLength * (xVar - 1))
myY = xVar
cN = e.points[0].curveNumber
split1 = (x.data[cN].text).split(' ')
hexID = (x.data[cN].text).split(' ')[2]
counts = split1[1].split('<')[0]
var selected_rows = [];
data.forEach(function(row){
if(row[myX+'-'+myY]==hexID) selected_rows.push(row);
});
console.log(selected_rows);
})}
", data = bindata)