--- title: "8. Linked plots with `detourr`" output: rmarkdown::html_vignette: keep_md: true vignette: > %\VignetteIndexEntry{8. Linked plots with `detourr`} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} options(rmarkdown.html_vignette.check_title = FALSE) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", warning = FALSE, message = FALSE ) ``` ```{r} library(quollr) library(plotly) library(detourr) ``` While `quollr` integrates directly with `langevitour` for interactive exploration, an alternative workflow is to use the `detourr` package. This approach gives users more flexibility to manually construct linked, browser-based visualizations using `crosstalk` and `htmltools`. In this setup, multiple views—such as the $2\text{-}D$ NLDR layout, model diagnostics, and a tour are displayed side by side and interactively linked through brushing and selection. ### Fitting the Model We begin by fitting a model using a high-dimensional dataset and its corresponding NLDR embedding. ```{r} model_obj <- fit_highd_model( highd_data = scurve, nldr_data = scurve_umap, b1 = 21, q = 0.1, hd_thresh = 0 ) ``` From the fitted object, we extract the $2\text{-}D$ model (`df_bin_centroids`), the lifted high-dimensional representation (`df_bin`), and the triangular mesh (`trimesh`) used to define neighborhood relationships. ```{r} df_bin_centroids <- model_obj$model_2d df_bin <- model_obj$model_highd trimesh <- model_obj$trimesh_data model_error <- augment( x = model_obj, highd_data = scurve ) ``` To support linked interaction across views, the model and data are combined into a single data structure. ```{r} df_exe <- comb_all_data_model( highd_data = scurve, nldr_data = scurve_umap, model_highd = df_bin, model_2d = df_bin_centroids ) ``` ## Two-Panel Linked View: NLDR Layout and Tour A simple linked view pairs the $2\text{-}D$ NLDR layout with a tour generated using `detourr`. Both panels are connected using `crosstalk`, allowing selections in one view to be reflected in the other. The NLDR plot is constructed with `plotly` to enable interactive brushing: ```{r} point_colours <- c("#66B2CC", "#FF7755") point_sizes <- c(0, 1) shared_df <- crosstalk::SharedData$new(df_exe) nldr_plt <- plot_ly( shared_df, x = ~emb1, y = ~emb2, type = "scatter", mode = "markers", marker = list( color = point_colours[1], size = 3, opacity = 0.5), hoverinfo = "none" ) |> layout( width = 300, height = 300, xaxis = list(title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), yaxis = list( title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), margin = list(l = 20, r = 20, t = 20, b = 20), dragmode = "select" ) |> style(selected = list(marker = list(opacity = 1)), unselected=list(marker=list(opacity=1))) |> highlight(on="plotly_selected", off="plotly_deselect") |> config(displayModeBar = FALSE) ``` The corresponding tour view is created using `detourr`, with the triangular mesh overlaid to show neighborhood structure: ```{r} detourr_output <- detour( shared_df, tour_aes(projection = starts_with("x"), colour = type) ) |> tour_path(grand_tour(2), max_bases=50, fps = 60) |> show_scatter(axes = TRUE, size = 0.5, alpha = 0.8, edges = as.matrix(trimesh[, c("from_reindexed", "to_reindexed")]), palette = c("#66B2CC", "#FF7755"), width = "300px", height = "300px") ``` These two views are arranged side by side using `bscols()`: ```{r, eval=FALSE} lndet_link <- crosstalk::bscols( htmltools::div( style = "display: grid; grid-template-columns: 1fr 1fr;", nldr_plt, htmltools::div(style = "margin-top: 20px;", detourr_output) ), device = "xs" ) class(lndet_link) <- c(class(lndet_link), "htmlwidget") lndet_link ``` This two-panel display allows users to explore how selections in the $2\text{-}D$ embedding correspond to structures observed in high-dimensional space. ## Three-Panel Linked View: Adding Model Error To support deeper diagnostic exploration, a third panel showing the **error distribution** can be added. This view highlights how well different regions of the $2\text{-}D$ layout represent the original high-dimensional data. First, we recombine the data to include per-point error information: ```{r} df_exe <- comb_all_data_model_error( highd_data = scurve, nldr_data = scurve_umap, model_highd = df_bin, model_2d = df_bin_centroids, error_data = model_error ) shared_df <- crosstalk::SharedData$new(df_exe) ``` The NLDR and tour views are constructed as before, but using a different `SharedData` object. ```{r} nldr_plt_n <- plot_ly( shared_df, x = ~emb1, y = ~emb2, type = "scatter", mode = "markers", marker = list(color = point_colours[1], size = 3, opacity = 0.5), hoverinfo = "none" ) |> layout( width = 250, height = 250, xaxis = list( title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), yaxis = list( title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), margin = list(l = 20, r = 20, t = 20, b = 20), dragmode = "select" ) |> style(selected = list(marker = list(opacity = 1)), unselected=list(marker=list(opacity=1))) |> highlight(on="plotly_selected", off="plotly_deselect") |> config(displayModeBar = FALSE) detourr_output_n <- detour( shared_df, tour_aes(projection = starts_with("x"), colour = type) ) |> tour_path(grand_tour(2), max_bases=50, fps = 60) |> show_scatter(axes = TRUE, size = 0.5, alpha = 0.8, edges = as.matrix(trimesh[, c("from_reindexed", "to_reindexed")]), palette = c("#66B2CC", "#FF7755"), width = "250px", height = "250px") ``` The error distribution is visualized as an interactive scatter plot: ```{r} error_plt <- plot_ly( shared_df, x = ~sqrt_row_wise_total_error, y = ~density, type = "scatter", mode = "markers", marker = list(color = point_colours[1], size = 3, opacity = 0.5), hoverinfo = "none" ) |> layout( width = 250, height = 250, xaxis = list( title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), yaxis = list( title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE, ticks = "", linecolor = "black", mirror = TRUE ), margin = list(l = 20, r = 20, t = 20, b = 20), dragmode = "select" ) |> style(selected = list(marker = list(opacity = 1)), unselected=list(marker=list(opacity=1))) |> highlight(on="plotly_selected", off="plotly_deselect") |> config(displayModeBar = FALSE) ``` All three panels are arranged in a single linked display: ```{r, eval=FALSE} erlndet_link <- crosstalk::bscols( htmltools::div( style = "display: grid; grid-template-columns: 1fr 1fr 1fr;", error_plt, nldr_plt_n, htmltools::div(style = "margin-top: 20px;", detourr_output_n) ), device = "xs" ) class(erlndet_link) <- c(class(erlndet_link), "htmlwidget") erlndet_link ``` This three-panel view allows users to explore between **embedding space**, **model error**, and **tour**, making it easier to identify regions where the NLDR layout may distort distances or cluster relationships.