Skip to contents

The langevitour development version now supports sharing selections and filters with other htmlwidgets that support the crosstalk package.

The typical way to use crosstalk is to create a special object from a data frame using crosstalk::SharedData$new(), and then use this where you would normally use the data frame. For example, this can be done with DT::datatable() for tables, or with ggplot2::ggplot() for plots which are then converted to an htmlwidget using plotly::ggplotly(). The critical part of the SharedData object is a key for each row. Keys are used to link the graphical representation of rows in different widgets. By default the keys are the row numbers. In advanced usage, multiple rows can be given the same key so that they are selected and filtered as a group.

To use crosstalk with langevitour, specify a SharedData object using the link= argument to langevitour(). I have not yet worked out a nice syntax for langevitour where the relevant columns can be taken directly from the SharedData object, so arguments like X= and group= still need to be given as conventional R objects.

Note: As at 2022-09-25 you must install the development version of plotly for plotly widgets in this page to work.

remotes::install_github("pfh/langevitour")
remotes::install_github("plotly/plotly.R")




Linked selections

library(langevitour)
library(crosstalk)
library(ggplot2)
library(GGally)
library(plotly)
library(DT)
library(htmltools)
library(palmerpenguins)

completePenguins <- na.omit(penguins)
scale <- apply(completePenguins[,3:6], 2, sd)*4
colors <- scales::hue_pal()(3)

# crosstalk object that allows widgets to be linked
shared <- SharedData$new(completePenguins)

# Create a plotly widget showing all pairs of variables
ggpairsWidget <- ggpairs(shared, aes(color=species, text=""), columns=3:6) |>
    ggplotly(tooltip="text", width=700, height=700) |> 
    style(unselected=list(marker=list(opacity=1))) |> # Don't double-fade unselected points
    highlight(on="plotly_selected", off="plotly_deselect")

# Create a langevitour widget that listens for selections and filters
langevitourWidget <- langevitour(
    completePenguins[,3:6], 
    completePenguins$species, 
    link=shared,                   #<--- the important bit
    levelColors=colors, 
    scale=scale, 
    pointSize=2,
    width=700, height=700)

# Create a table widget
datatableWidget <- datatable(
    shared,
    rownames=FALSE, width="100%", 
    class='compact cell-border hover', extensions='Buttons',
    options=list(dom='Bfrtip',buttons=c('copy','csv','excel')))
browsable(div(
    div(style="display: grid; grid-template-columns: 1fr 1fr;", 
        ggpairsWidget, langevitourWidget),
    datatableWidget))

To select points, drag on the langevitour widget, or drag on the pairs plot, or click rows in the table.
Hold shift to extend the selection.







Linked filters

Filters hide points completely. The view of the data chosen by a langevitour guide will adapt to this.

I want to demonstrate this with an independent set of widgets, so I make a new SharedData object.

shared2 <- SharedData$new(completePenguins)

# Create some filters
filter2a <- filter_checkbox(
    "filter2a", "Filter sex", shared2, ~sex)
filter2b <- filter_slider(
    "filter2b", "Filter body mass (g)", shared2, ~body_mass_g)

# Create a langevitour widget that listens for selections and filters
langevitourWidget2 <- langevitour(
    completePenguins[,3:6], 
    completePenguins$species, 
    link=shared2,
    levelColors=colors, 
    scale=scale, 
    pointSize=2,
    state='{"guideType":"pca"}')

# Create a plotly widget
plot2 <- ggplot(shared2, aes(x=sex, y=body_mass_g, color=species)) + 
    geom_jitter(height=0) + 
    guides(color="none")
plotWidget2 <- ggplotly(plot2, height=600) |> 
    style(unselected=list(marker=list(opacity=1))) |>  # Don't double-fade unselected points
    highlight(on="plotly_selected", off="plotly_deselect")

browsable(div(
    style="display: grid; grid-template-columns: 1fr 2fr; gap: 10px;",
    filter2a, filter2b, plotWidget2, langevitourWidget2))