2017-02-25 166 views
4

我之前發佈了類似的問題(Retrieving R object attributes in JavaScript)。在那篇較早的文章中,我簡單地介紹了我的MWE,所以不幸的回答並不真正適用於我真正的問題。在這裏,我展示了爲什麼我需要在JavaScript中檢索R對象屬性(除非有另一個我不知道的選項)。在JavaScript中檢索R對象屬性 - 第2部分

我有一個包含100個觀測值的5變量數據集。我使用六邊形裝箱並創建了散點圖矩陣。 10個散點圖中的每一個都有12-18個六邊形之間的地方。

attr(hexdf, "cID") <- [email protected] 
:爲了節省100組的意見,即在每個六邊形倉所有10個散點圖的行,我使用的鹼::在R. ATTR功能在下面的代碼,這是在完成

我正在嘗試創建六邊形裝箱的交互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 = [email protected], counts = [email protected]) 
    attr(hexdf, "cID") <- [email protected] 
    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) 

這將創建一個圖像,如下圖所示:

Scatterplot matrix of hexagon binning

舉個例子,如果我點擊六邊形綠色框中突出,我可以確定哪些插曲它出現在(「myX」和「myY」)中,單擊的六邊形的標識(「hexID」)以及被分類到該六邊形中的觀察點的數量(「計數」)。對於這個特定的六邊形,myX = 5,myY = 2,hexID = 39,計數= 1。因此,用戶只需在第五行和第二列散點圖上點擊帶ID39的六角形,並且應該有一個數據點歸檔。

如果我離開的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」對象。有沒有解決這個問題的辦法,或者我應該考慮採取另一種可能的方法?感謝您的任何想法/建議!

回答

1

我不確定您是否可以從plotly訪問十六進制ID,或者它是否將此數據保存在某處,因此一種選擇是將您需要的所有數據傳遞給onRender函數。

首先,你可以每hexplot添加到您的bindata據幀一列,被稱爲mX-mY(其中替換MX和我通過他們對每個列的值),這將保持每個觀察它屬於hexbin對於情節:

for(i in 2:5) { 
    for(j in 1:4) { 
    bindata[[paste(i,j,sep="-")]] <- attr(pS[i,j]$data, "cID") 
    } 
} 

然後,您可以通過bindataonRender功能和whever您在情節的一個點擊一個六邊形,檢查相應的列在其中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) 
+0

謝謝你向我指出這一點。我一直在研究這個問題很長一段時間,而且我從來沒有想過我可以用你的演示方式來使用attr(),即使在你之前將它顯示爲一個子場景之後。令我驚訝的是,這種方法似乎不會在onRender()函數中造成延遲。我將數據框大小從100個例子改爲50,000個,當我點擊給定的六邊形時,它可以立即解析50,000個大數據框以獲得單獨的觀察結果。我認爲JavaScript允許這發生得比我預期的更快(我大多使用R)? – luckButtered