Home > Mobile >  Is it possible to apply two filters to the same variable using the crosstalk package in R?
Is it possible to apply two filters to the same variable using the crosstalk package in R?

Time:01-21

Using crosstalk and plotly (and ggplot2), I am trying to apply two filters in a plot based on the same column in the underlying dataframe, but I cannot get it to work. I have tried a couple of approaches, but the result is always the same: when I make selections from both filters, all data disappears from the plot. My goal is to make selections from both filters and have the results showing up in the plot.

Below is a reproducible example of the problem, using toy data from the mtcars dataset. With this approach, the attempted solution is to split the dataframe in two before plotting and to add cases from each part to the plot by using separate ggplot geoms.

# Libraries
library(plotly)
library(crosstalk)

# Prepare data and create shared data objects
mtcars_lowcarb_shared <-
  SharedData$new(mtcars[mtcars$carb < 4, ], group = "my_group")

mtcars_highcarb_shared <-
  SharedData$new(mtcars[mtcars$carb >= 4, ], group = "my_group")

# Make checkboxes
cb1 <- filter_checkbox(id = "lowcarb_selector",
                label = "Select among low carb values",
                sharedData = mtcars_lowcarb_shared,
                group = ~carb
                )

cb2 <- filter_checkbox(id = "highcarb_selector",
                label = "Select among high carb values",
                sharedData = mtcars_highcarb_shared,
                group = ~carb
                )

# Make plot
p <-
  ggplot(mapping = aes(x = hp, y = mpg, color = as.character(carb)))  
  geom_point(data = mtcars_lowcarb_shared)  
  geom_point(data = mtcars_highcarb_shared)

p <- ggplotly(p)

# Put it all together
bscols(list(cb1, cb2), p, widths = c(2, 10))

If I try to make selections from both checkboxes in the above example, say carb values 1 and 4, no points are shown in the plot. I would expect points showing for all cars with a carb value of either 1 or 4. Note that if I select cases from only one checkbox, say only 4, or 4 and 6, the filtering behaves as I would expect it to do.

Below is another attempt. Here, the data used for plotting is kept in a single dataframe and the plot is made using one rather than two geoms. The checkboxes for the filtering, however, are made using separate dataframes.

# Libraries
library(crosstalk)
library(plotly)

# Prepare data and create shared data objects
mtcars_lowcarb_shared <-
  SharedData$new(mtcars[mtcars$carb < 4, ], group = "my_group")

mtcars_highcarb_shared <-
  SharedData$new(mtcars[mtcars$carb >= 4, ], group = "my_group")

mtcars_shared <- 
  SharedData$new(mtcars, group = "my_group")

# Make checkboxes
cb1 <- filter_checkbox(id = "lowcarb_selector",
                label = "Select among low carb values",
                sharedData = mtcars_lowcarb_shared,
                group = ~carb
                )

cb2 <- filter_checkbox(id = "highcarb_selector",
                label = "Select among high carb values",
                sharedData = mtcars_highcarb_shared,
                group = ~carb
                )

# Make plot
p <-
  ggplot(mtcars_shared, aes(x = hp, y = mpg, color = as.character(carb)))  
  geom_point()

p <- ggplotly(p)

# Put it all together
bscols(list(cb1, cb2), p, widths = c(2, 10))

The results are the same with this approach. Selections from both checkboxes result in an empty plot.

I don't understand why I get an empty plot when selecting from both checkboxes. I would like to find a way around this issue. I want, that is, to use two checkboxes and be able to select from both.

Of course, with the data in the examples, using two checkboxes does not make much sense. But with my real use case, I have a very good reason for wanting to separate values into two boxes.

CodePudding user response:

It is a bit chimeric but you can tweak the filter's children to suit your format needs:

# Libraries
library(crosstalk)
library(plotly)

# Prepare data and create shared data objects
mtcars_lowcarb_shared <-
  SharedData$new(mtcars[mtcars$carb < 4, ], group = "my_group")

mtcars_highcarb_shared <-
  SharedData$new(mtcars[mtcars$carb >= 4, ], group = "my_group")

mtcars_shared <-
  SharedData$new(mtcars, group = "my_group")

cb <- filter_checkbox(
  id = "general_selector",
  label = "Select among low carb values",
  sharedData = mtcars_shared,
  group = ~ carb
)

# Make checkboxes
cb1 <- filter_checkbox(
  id = "lowcarb_selector",
  label = "Select among low carb values",
  sharedData = mtcars_lowcarb_shared,
  group = ~ carb
)

cb2 <- filter_checkbox(
  id = "highcarb_selector",
  label = "Select among high carb values",
  sharedData = mtcars_highcarb_shared,
  group = ~ carb
)


cb3 <- cb
cb3$children <-
  list(cb1$children[[1]],
       cb1$children[[2]],
       cb2$children[[1]],
       cb2$children[[2]],
       cb$children[[3]])
# Make plot
p <-
  ggplot(mtcars_shared, aes(x = hp, y = mpg, color = as.character(carb)))  
  geom_point()

p <- ggplotly(p)

# Put it all together
bscols(list(cb3), p, widths = c(2, 10))

The last element of children is the one containing the mapping javascript code.

  • Related