1

I created the following Rmarkdown file to make a selection based on clicking an interactive ggplot.

In the javascript chunk I would like to use instead of "A" the letter (A or B) obtained from
the onclick event in the interactive ggplot. If the user clicks on polygon B then the "A" should become a "B".

---
output:
  html_document
---

```{r, echo = FALSE, message = FALSE}
library(ggplot2)
library(ggiraph)

# Rectangle A
group_A <- data.frame(x1 = 0, 
                  x2 = 3, 
                  y1 = 0, 
                  y2 = 1, 
                  r = "A")

# Polygon B
group_B <- data.frame(x = c(3,4,4,0,0,3), 
                      y = c(0,0,2,2,1,1), 
                      r = "B")

p <- ggplot() + 
  geom_rect_interactive(data = group_A, 
                        aes(xmin = x1, xmax = x2, ymin = y1, 
                            ymax = y2, data_id = r, onclick = r), 
                        alpha = .1, color = "black") + 
  geom_polygon_interactive(data = group_B, 
                           aes(x = x, y = y, data_id = r, onclick = r), 
                           alpha = .1, color = "black") + 
  annotate("text", x = 0.1, y = .82, 
           label = "A",
           fontface = 2, hjust = 0) +
  annotate("text", x = 0.1, y = 1.82, 
           label = "B", 
           fontface = 2, hjust = 0) +
  theme_void()

girafe(ggobj = p)

```

Javascript chunk:

```{js}
$(document).ready(function() {
    document.getElementById("filter").getElementsByClassName("selectized"[0].selectize.setValue("A", false);
 });
```

How can I achieve this?

See Selecting a default value in an R plotly plot using a selectize box via crosstalk in R, using static html not shiny for a similar question.

Edit

More explicitly, I would like to filter the following table based on the chosen rectangle: 

```{r}

# example data 
dat <- tibble::tribble(~value, ~x, ~y, 
                          "A", 1, 1, 
                          "B", 2, 1,   
                          "A", 1, 2,    
                          "B", 2, 2,       
                          "A", 1, 3,    
                          "B", 2, 3,   
                          "A", 1, 2,       
                          "B", 2, 3)
```

Then the rectangle in question_filter should be equal to the chosen rectangle in the ggplot figure. I obtained the following chunk from the linked question, and would like to adjust this chunk to show the table based on the selected rectangle.

```{r}
library(crosstalk)
library(reactable)

# Initializing a crosstalk shared data object  
plotdat <- highlight_key(dat)

# Filter dropdown
question_filter <- crosstalk::filter_select(        
 "filter", "Select a group to examine",   
 plotdat, ~value, multiple = F
)

plot <- reactable(plotdat)

# Just putting things together for easy 
displayshiny::tags$div(class = 'flexbox', 
                       question_filter,
                       shiny::tags$br(),
                       plot)
```
mharinga
  • 1,200
  • 8
  • 20

2 Answers2

3

Here's a slightly more useful take on the problem:

---
output:
  html_document
---

```{r setup, include=FALSE}
library(ggplot2)
library(ggiraph)
knitr::opts_chunk$set(echo = TRUE)
library(knitr)
library(crosstalk)
library(reactable)
library(tibble)
```

```{r, echo = FALSE, message = FALSE}

dat <- tibble::tribble(~value, ~x, ~y,
                          "A", 1, 1,
                          "B", 2, 1,
                          "A", 1, 2,
                          "B", 2, 2,
                          "A", 1, 3,
                          "B", 2, 3,
                          "A", 1, 2,
                          "B", 2, 3)

shared_dat <- SharedData$new( dat, group="abSelector" )

# Rectangle A
group_A <- data.frame(x1 = 0,
                  x2 = 3,
                  y1 = 0,
                  y2 = 1,
                  r = "A")

# Polygon B
group_B <- data.frame(x = c(3,4,4,0,0,3),
                      y = c(0,0,2,2,1,1),
                      r = "B")

p <- ggplot() +
  geom_rect_interactive(data = group_A,
                        aes(xmin = x1, xmax = x2, ymin = y1,
                            ymax = y2, data_id = r,
                            onclick = paste0("filterOn(\"",r,"\")")
                            ),
                        alpha = .1, color = "black") +
  geom_polygon_interactive(data = group_B,
                           aes(x = x, y = y, data_id = r,
                            onclick = paste0("filterOn(\"",r,"\")")
                               ),
                           alpha = .1, color = "black") +
  annotate("text", x = 0.1, y = .82,
           label = "A",
           fontface = 2, hjust = 0) +
  annotate("text", x = 0.1, y = 1.82,
           label = "B",
           fontface = 2, hjust = 0) +
  theme_void()

g <- girafe(ggobj = p)

rt <- reactable(
    shared_dat,
    elementId = "ABtable"
)

fs <- filter_select("letterFilter", "Filter", shared_dat, group=~value, multiple=FALSE )

bscols(
    list( fs, rt ),
    g
)

```

<script>

$(function() {
    // Necessary to trigger selectize initialization
    $("#letterFilter input").focus();
    setTimeout( function(){ $("#letterFilter input").blur(); }, 0);
});

filterOn = function(letter) {
    var obj = $("#letterFilter div[data-value='" + letter + "']");
    obj.click();
}

</script>

As you will see, there are three components to it:

  • a filter_select
  • a reactable
  • your plot

Behind the scenes there's the SharedData object encapsulating your data and that know's how its being filtered.

Now ideally I'd use a crosstalk.FilterHandle to control the filtering, but it doesn't seem to play well with filter_select. I'd rather updat the selectize value and have the filtering happen based onthat, where as the FilterHandle filters the data directly, bypassing the actual filter string and instead dictating which elements to show. Which would have made for a more clunky solution in which I do the filtering myself, update shown elements, and then update the actual search key shown.

As it is now, I just fire a .click() on the filter option corresponding to the letter in the plot (using jQuery). I also have to focus and blur upon loading the document to trigger building of the filter options, which you will see in the code above.

Sirius
  • 4,339
  • 1
  • 10
  • 17
  • Thanks Sirius, very much appreciated. I was indeed looking to combine the solution with `selectize` and reactable. I added a new chunk to my question with a data.frame I would like to filter based on the selected rectangle. How can I adjust your solution to work with filtering the reactable? – mharinga Apr 06 '21 at 13:06
  • 2
    ill update the answer later tonight – Sirius Apr 06 '21 at 16:45
  • there is no clear'er at the moment (to empty the search field), perhaps that would have been a nice addition too? – Sirius Apr 06 '21 at 22:31
  • Perfect solution! This is exactly what I am looking for. Thank you very much! – mharinga Apr 07 '21 at 07:56
3

What about something like this? This is from Interactive web-based data visualization with R, plotly, and shiny by Carson Sievert Published by CRC press

---
title: "Untitled"
author: "Daniel"
date: "4/7/2021"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

```{r}


library(ggplot2)
library(plotly)
library(DT)

m<-highlight_key(mpg)
p<-ggplot(m,aes(displ,hwy))+geom_point(aes(color = cyl)) + geom_smooth(se = TRUE)
gg<-highlight(ggplotly(p),"plotly_selected")
m<-highlight_key(mpg)
p<-ggplot(m,aes(displ,hwy))+geom_point(aes(color = cyl)) + geom_smooth(se = TRUE)
gg<-highlight(ggplotly(p),"plotly_selected")
crosstalk::bscols(gg,DT::datatable(m))
```

Where you get a crosstalk DT in plotly

enter image description here

Daniel Jachetta
  • 1,614
  • 2
  • 5
  • 18