javascriptrplotlyhtmlwidgetsonrender

Retrieving R object attributes in JavaScript


I have a bivariate dataset with 100 observations. I used hexagon binning and ended up with 26 hexagon bins. In order to save the rows of the 100 observations that are in each of the 26 hexagon bins, I used the base::attr function in R. In the code below, this is done at:

attr(hexdf, "cID") <- h@cID

I am trying to create an interactive R Plotly object of the hexagon binning so that if a user were to click on a given hexagon bin, they would obtain the rows of the 100 observations that were grouped into that bin. I have part of this goal completed. My MWE is below:

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))
bindata$ID <- as.character(bindata$ID)

x = bindata[,c("A")]
y = bindata[,c("B")]
h <- hexbin(x=x, y=y, xbins=5, shape=1, IDs=TRUE)
hexdf <- data.frame (hcell2xy (h),  hexID = h@cell, counts = h@count)
attr(hexdf, "cID") <- h@cID
pS <- ggplot(hexdf, aes(x=x, y=y, fill = counts, hexID=hexID)) + geom_hex(stat="identity")

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) {
            //console.log(el)
            //console.log(x)
            //console.log(data)

            myGraph = document.getElementById(el.id);
            el.on('plotly_click', function(e) {

            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(hexID)
            console.log(counts)

           })}
           ", data = pS$data)

When I run this code and open it in the Web Browser, I obtain an interactive plot like below (green box is not in plot; it's superimposed for explanatory purposes):

enter image description here

If I click on the hexagon inside the green box, the correct hexID of 40 and counts of 3 are printed to the console. At this point, I would like to obtain the 3 rows of the original data frame that were put into that hexagon bin.

I know how to do this in R outside of the onRender() function of the htmlwidgets package by using the base::attr function. For instance, I can do the following:

hexID=40
obsns <- which(attr(pS$data, "cID")==hexID)
dat <- bindata[obsns,]

And receive the following correct 3 data points that were put into that bin I clicked on:

     ID         A        B
47 ID47 0.3645820 2.087167
66 ID66 0.1887923 2.206102
71 ID71 0.4755095 2.307978

I am working with much larger datasets than in this MWE. For that reason, my intention of using the base:attr function was to prevent ever larger data frame floating around. However, I am unsure how to translate the functionality of the base::attr function so that I can access the appropriate data point rows that occur in a clicked hexagon bin in the onRender() JavaScript code. I did include the pS$data object into the onRender() JavaScript code, but am still stuck.

Any advice would be sincerely appreciated!


Solution

  • You could add a column which for each row has the ID of the hexbin it belongs to in your bindata:

    bindata$hex <- h@cID
    

    You can then pass it to the onRender function and filter rows when the users clicks on a hexagon:

    ggPS %>% onRender("
                      function(el, x, data) {
                      myGraph = document.getElementById(el.id);
                      el.on('plotly_click', function(e) {
    
                      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.hex==hexID) selected_rows.push(row);
                      });
                      console.log(selected_rows);
    
                      })}
                      ", data = bindata)