---
title: "Using Plotly with linkeR"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Using Plotly with linkeR}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
eval = FALSE # Set to FALSE since these are Shiny examples
)
```
```{r setup}
library(linkeR)
library(shiny)
library(plotly)
library(DT)
library(leaflet)
```
# Introduction
The `linkeR` package provides seamless integration with `plotly` charts, enabling interactive linking between plotly visualizations and other Shiny components. This vignette demonstrates how to create linked plotly charts with automatic selection highlighting.
## Key Features
- **Automatic linking** with any plotly chart type (scatter, bar, histogram, etc.)
- **Native selection highlighting** using plotly's built-in selectedpoints mechanism
- **Zero-configuration setup** for most common use cases
- **Custom behaviors** for advanced interactions
- **Multi-trace support** for complex visualizations
## Basic Plotly Linking
### Simple Example
The most basic way to link a plotly chart is using the `link_plots()` function:
```{r basic-plotly}
library(shiny)
library(plotly)
library(linkeR)
# Sample data
sample_data <- data.frame(
id = 1:20,
name = paste("Item", 1:20),
x_value = runif(20, 1, 10),
y_value = runif(20, 1, 10),
category = sample(c("A", "B", "C"), 20, replace = TRUE),
value = runif(20, 100, 1000)
)
ui <- fluidPage(
titlePanel("Basic Plotly Linking"),
fluidRow(
column(6,
h4("Interactive Scatter Plot"),
plotlyOutput("scatter_plot")
),
column(6,
h4("Data Table"),
DTOutput("data_table")
)
),
verbatimTextOutput("selection_info")
)
server <- function(input, output, session) {
# Create reactive data
data_reactive <- reactive({ sample_data })
# Simple one-line linking setup
registry <- link_plots(
session,
scatter_plot = data_reactive,
data_table = data_reactive,
shared_id_column = "id"
)
# Create plotly chart with key parameter for reliable linking
output$scatter_plot <- renderPlotly({
plot_ly(
data = sample_data,
x = ~x_value,
y = ~y_value,
color = ~category,
key = ~id, # Essential for reliable linking
source = "scatter_plot",
type = "scatter",
mode = "markers"
) %>%
layout(title = "Click any point to see linking")
})
# Create data table
output$data_table <- renderDT({
datatable(
sample_data,
selection = "single",
options = list(pageLength = 5)
)
})
# Show selection information
output$selection_info <- renderText({
selection <- registry$get_selection()
if (!is.null(selection$selected_id)) {
paste("Selected ID:", selection$selected_id,
"| Source:", selection$source)
} else {
"No selection"
}
})
}
# Run the app
if (interactive()) {
shinyApp(ui = ui, server = server)
}
```
## Important Note: Enabling Visual Highlighting
For DEFAULT plotly selection highlighting to work with linkeR, you must add a custom JavaScript message handler to your Shiny UI. This enables linkeR to send selection updates to the plotly chart for native visual feedback.
Add the following to your `fluidPage()` or `dashboardPage()` UI definition:
```{r, eval=FALSE}
tags$script(HTML("
Shiny.addCustomMessageHandler('eval', function(code) {
try {
eval(code);
} catch(e) {
console.error('JavaScript execution error:', e);
}
});
"))
```
This is required for linkeR's native selection highlighting to work in all linked plotly charts.
## Essential Plotly Parameters
For reliable linking, always include these parameters in your `plot_ly()` calls:
### The `key` Parameter
The `key` parameter is crucial for linking. It should reference your shared ID column:
```{r key-parameter}
plot_ly(
data = my_data,
x = ~x_column,
y = ~y_column,
key = ~id_column, # This enables reliable point identification
source = "my_plot"
)
```
### The `source` Parameter
The `source` parameter identifies your plot for event handling:
```{r source-parameter}
plot_ly(
data = my_data,
x = ~x_column,
y = ~y_column,
key = ~id_column,
source = "unique_plot_name" # Must match your output ID
)
```
## Chart Types Support
`linkeR` works with all plotly chart types through native selectedpoints highlighting:
### Scatter Plots
```{r scatter-example}
output$scatter <- renderPlotly({
plot_ly(
data = data,
x = ~x_value,
y = ~y_value,
key = ~id,
source = "scatter",
type = "scatter",
mode = "markers"
)
})
```
### Bar Charts
```{r bar-example}
output$bar_chart <- renderPlotly({
plot_ly(
data = aggregated_data,
x = ~category,
y = ~total_value,
key = ~category_id, # Use appropriate ID for linking
source = "bar_chart",
type = "bar"
)
})
```
### Line Charts
```{r line-example}
output$line_chart <- renderPlotly({
plot_ly(
data = time_series_data,
x = ~date,
y = ~value,
key = ~observation_id,
source = "line_chart",
type = "scatter",
mode = "lines+markers"
)
})
```
### Multi-trace Charts
For charts with multiple traces (e.g., using `color = ~category`), linkeR handles the complexity automatically:
```{r multi-trace}
output$multi_trace <- renderPlotly({
plot_ly(
data = data,
x = ~x_value,
y = ~y_value,
color = ~category, # Creates multiple traces
key = ~id, # Still works perfectly
source = "multi_trace",
type = "scatter",
mode = "markers"
)
})
```
## Advanced Integration
### Multiple Plotly Charts
Link multiple plotly charts together for coordinated views:
```{r multiple-charts}
ui <- fluidPage(
titlePanel("Multiple Linked Plotly Charts"),
fluidRow(
column(4,
h4("Scatter Plot"),
plotlyOutput("scatter", height = "300px")
),
column(4,
h4("Bar Chart"),
plotlyOutput("bar", height = "300px")
),
column(4,
h4("Box Plot"),
plotlyOutput("box", height = "300px")
)
),
verbatimTextOutput("multi_selection")
)
server <- function(input, output, session) {
data_reactive <- reactive({ sample_data })
# Link all three charts
registry <- link_plots(
session,
scatter = data_reactive,
bar = data_reactive,
box = data_reactive,
shared_id_column = "id"
)
# Scatter plot
output$scatter <- renderPlotly({
plot_ly(
data = sample_data,
x = ~x_value,
y = ~y_value,
key = ~id,
source = "scatter"
)
})
# Aggregated bar chart
bar_data <- sample_data %>%
group_by(category) %>%
summarise(
mean_value = mean(value),
id = first(id), # Use first ID for linking
.groups = 'drop'
)
output$bar <- renderPlotly({
plot_ly(
data = bar_data,
x = ~category,
y = ~mean_value,
key = ~id,
source = "bar",
type = "bar"
)
})
# Box plot
output$box <- renderPlotly({
plot_ly(
data = sample_data,
y = ~value,
color = ~category,
key = ~id,
source = "box",
type = "box"
)
})
output$multi_selection <- renderText({
selection <- registry$get_selection()
paste("Selected:", selection$selected_id %||% "None")
})
}
```
### Mixed Component Types
Combine plotly charts with other interactive components:
```{r mixed-components}
ui <- fluidPage(
titlePanel("Mixed Component Dashboard"),
fluidRow(
column(3,
h4("Map View"),
leafletOutput("map", height = "400px")
),
column(4,
h4("Performance Chart"),
plotlyOutput("performance", height = "400px")
),
column(5,
h4("Data Details"),
DTOutput("details")
)
)
)
server <- function(input, output, session) {
business_data <- reactive({
data.frame(
business_id = 1:50,
name = paste("Business", 1:50),
latitude = runif(50, 40.7, 40.8),
longitude = runif(50, -111.95, -111.85),
revenue = runif(50, 100000, 1000000),
employees = sample(10:500, 50),
category = sample(c("Tech", "Retail", "Food"), 50, replace = TRUE)
)
})
# Link map, chart, and table
registry <- link_plots(
session,
map = business_data,
performance = business_data,
details = business_data,
shared_id_column = "business_id"
)
# Map
output$map <- renderLeaflet({
data <- business_data()
leaflet(data) %>%
addTiles() %>%
addMarkers(
lng = ~longitude,
lat = ~latitude,
layerId = ~business_id,
popup = ~name
)
})
# Performance chart
output$performance <- renderPlotly({
data <- business_data()
plot_ly(
data = data,
x = ~employees,
y = ~revenue,
color = ~category,
key = ~business_id,
source = "performance",
text = ~paste("Name:", name),
hovertemplate = "%{text}
Employees: %{x}
Revenue: $%{y:,.0f}"
) %>%
layout(
title = "Revenue vs Employees",
xaxis = list(title = "Employees"),
yaxis = list(title = "Revenue ($)")
)
})
# Data table
output$details <- renderDT({
datatable(
business_data(),
selection = "single",
options = list(pageLength = 8, scrollX = TRUE)
) %>%
formatCurrency("revenue", currency = "$", digits = 0)
})
}
```
## Selection Highlighting
`linkeR` uses plotly's native `selectedpoints` mechanism for highlighting, which provides:
- **Consistent behavior** across all chart types
- **Automatic dimming** of unselected points
- **Enhanced styling** for selected points
- **Multi-trace support** without additional configuration
### How It Works
When a point is selected in any linked component:
1. **Selected points** are highlighted with full opacity and increased size
2. **Unselected points** are dimmed to 30% opacity
3. **All traces** are processed automatically
4. **Visual feedback** is immediate and consistent
### Visual Customization
The default highlighting can be customized through plotly's selected/unselected parameters:
```{r custom-highlighting}
plot_ly(
data = data,
x = ~x_value,
y = ~y_value,
key = ~id,
source = "custom_plot"
) %>%
layout(
# Custom selection styling
selectdirection = "diagonal",
dragmode = "select"
)
```
## Troubleshooting
### Common Issues
**Problem**: Linking doesn't work or selection highlighting is missing
**Solutions**:
1. Always include `key = ~id_column` in your `plot_ly()` call
2. Ensure the `source` parameter matches your output ID
3. Verify your shared ID column exists in the data
4. Check that `register_plotly()` or `link_plots()` is called correctly
**Problem**: Multiple traces don't highlight correctly
**Solution**: linkeR handles multi-trace plots automatically. Ensure you're using the same ID column across all traces.
**Problem**: There is no plotly visual update on selection
**Solution**: Make sure the custom JavaScript handler is included in your UI for selection highlighting to work.
### Best Practices
1. **Always use the `key` parameter** for reliable point identification
2. **Set consistent `source` names** that match your output IDs
3. **Use meaningful ID columns** that uniquely identify your data points
4. **Test with different chart types** to ensure consistent behavior
5. **Keep data reactive** to ensure updates propagate correctly
## Complete Example
Here's a complete, runnable example demonstrating plotly integration:
```{r complete-example}
library(shiny)
library(plotly)
library(linkeR)
library(DT)
library(dplyr)
# Generate sample data
set.seed(123)
categories <- c("Electronics", "Clothing", "Books")
n <- 30
sample_data <- data.frame(
business_id = paste0("PROD_", sprintf("%03d", 1:n)),
name = paste("Product", LETTERS[1:n]),
price = round(runif(n, 10, 100), 2),
sales = round(runif(n, 100, 1000), 0),
category = sample(categories, n, replace = TRUE),
rating = round(runif(n, 1, 5), 1),
stringsAsFactors = FALSE
)
# Defensive: Remove any rows with NA in key columns
sample_data <- subset(sample_data, !is.na(business_id) & !is.na(name) & !is.na(category))
ui <- fluidPage(
titlePanel("Complete Plotly + linkeR Example"),
tags$script(HTML("
Shiny.addCustomMessageHandler('eval', function(code) {
try {
eval(code);
} catch(e) {
console.error('JavaScript execution error:', e);
}
});
")),
fluidRow(
column(7,
h4("Scatter Plot"),
plotlyOutput("scatter_plot", height = "400px"),
br(),
verbatimTextOutput("current_selection")
),
column(5,
h4("Data Table"),
DTOutput("data_table")
)
)
)
server <- function(input, output, session) {
data_reactive <- reactive({ sample_data })
# Use a fresh registry name to avoid conflicts
scatter_registry <- link_plots(
session,
scatter_plot = data_reactive,
data_table = data_reactive,
shared_id_column = "business_id"
)
# Scatter plot
output$scatter_plot <- renderPlotly({
plot_ly(
data = sample_data,
x = ~price,
y = ~sales,
color = ~category,
key = ~business_id,
source = "scatter_plot",
text = ~paste("Product:", name, "
Category:", category, "
Rating:", rating),
hovertemplate = "%{text}
Price: $%{x:.2f}
Sales: %{y:.0f}",
type = "scatter",
mode = "markers"
) %>%
layout(
title = "Price vs Sales by Category",
xaxis = list(title = "Price ($)"),
yaxis = list(title = "Sales")
)
})
# Data table
output$data_table <- renderDT({
datatable(
sample_data,
selection = "single",
rownames = FALSE,
options = list(
pageLength = 10,
scrollX = TRUE,
searchHighlight = TRUE
)
) %>%
formatCurrency("price", currency = "$") %>%
formatRound(c("sales", "rating"), digits = c(0, 1))
})
# Show current selection
output$current_selection <- renderText({
selection <- scatter_registry$get_selection()
if (!is.null(selection$selected_id)) {
selected_item <- sample_data[sample_data$business_id == selection$selected_id, ]
if (nrow(selected_item) > 0) {
paste0(
"Selected: ", selected_item$name, "\n",
"Category: ", selected_item$category, "\n",
"Price: $", selected_item$price, "\n",
"Sales: ", selected_item$sales, "\n",
"Rating: ", selected_item$rating, "\n",
"Source: ", selection$source
)
} else {
"No item selected"
}
} else {
"No item selected"
}
})
}
# Run the application
if (interactive()) {
shinyApp(ui = ui, server = server)
}
```
## Summary
The `linkeR` package makes it easy to create interactive plotly charts that work seamlessly with other Shiny components. Key takeaways:
- Use `key = ~id_column` for reliable linking
- Set `source` parameter to match output IDs
- linkeR handles all chart types automatically
- Native plotly highlighting provides consistent visual feedback
- Mix plotly charts with maps, tables, and other components effortlessly
For more examples, see the other vignettes and the package's example applications.