Overview
perspectiveR provides an R interface to the FINOS Perspective library, a high-performance WebAssembly-powered data visualization engine. It offers interactive pivot tables, cross-tabulations, and multiple chart types that run entirely in the browser.
Quick Start
The simplest usage is to pass a data frame directly:
perspective(mtcars)This opens a fully interactive viewer with a settings panel where you can:
- Drag columns in/out of the view
- Switch between chart types (datagrid, bar, line, scatter, heatmap, etc.)
- Add group-by (row pivot) and split-by (column pivot) dimensions
- Create filters, sort, and change aggregations
- Write computed expressions
Configuration
You can set an initial configuration programmatically:
perspective(mtcars,
group_by = "cyl",
columns = c("mpg", "hp", "wt"),
plugin = "Y Bar",
theme = "Pro Dark"
)Users can still modify the view interactively even after initial configuration.
Available Chart Types
-
"Datagrid"— interactive data grid (default) -
"Y Bar"/"X Bar"— vertical / horizontal bar charts -
"Y Line"— line chart -
"X/Y Line"— line chart with explicit X axis -
"Y Area"— area chart -
"Y Scatter"/"XY Scatter"— scatter plots -
"Treemap"— treemap -
"Sunburst"— sunburst chart -
"Heatmap"— heatmap
Filtering and Sorting
perspective(iris,
filter = list(c("Species", "==", "setosa")),
sort = list(c("Sepal.Length", "desc"))
)Computed Expressions
perspective(mtcars,
expressions = c('"hp" / "wt"'),
columns = c("mpg", "hp", "wt", '"hp" / "wt"')
)Arrow IPC for Large Datasets
For datasets with 100k+ rows, use Arrow IPC serialization for better performance:
# Requires the arrow package
big_data <- data.frame(
x = rnorm(100000),
y = rnorm(100000),
group = sample(letters, 100000, replace = TRUE)
)
perspective(big_data, use_arrow = TRUE)Shiny Integration
perspectiveR includes full Shiny support with a proxy interface for streaming updates:
library(shiny)
ui <- fluidPage(
perspectiveOutput("viewer", height = "600px"),
actionButton("add", "Add Data")
)
server <- function(input, output, session) {
output$viewer <- renderPerspective({
perspective(mtcars, plugin = "Y Bar", group_by = "cyl")
})
observeEvent(input$add, {
proxy <- perspectiveProxy(session, "viewer")
new_data <- mtcars[sample(nrow(mtcars), 5), ]
psp_update(proxy, new_data)
})
# Capture user's interactive config changes
observeEvent(input$viewer_config, {
message("User changed config: ", str(input$viewer_config))
})
}
shinyApp(ui, server)Proxy Functions
-
psp_update(proxy, data)— append new rows (upserts when table has an index) -
psp_replace(proxy, data)— replace all data -
psp_clear(proxy)— clear all rows -
psp_restore(proxy, config)— apply a config -
psp_reset(proxy)— reset to defaults -
psp_remove(proxy, keys)— remove rows by primary key (indexed tables only) -
psp_export(proxy, format)— export data as JSON, CSV, columns, or Arrow (supports windowed export) -
psp_save(proxy)— retrieve current viewer state -
psp_on_update(proxy, enable)— subscribe/unsubscribe to data change events -
psp_schema(proxy)— get table schema (column names and types) -
psp_size(proxy)— get table row count -
psp_columns(proxy)— get table column names -
psp_validate_expressions(proxy, expressions)— validate expression strings
Filter Operator
When using multiple filters, you can control how they are combined
using filter_op:
# Match rows where Species is "setosa" OR Sepal.Length > 6
perspective(iris,
filter = list(
c("Species", "==", "setosa"),
c("Sepal.Length", ">", "6")
),
filter_op = "or"
)The default is "and" (all filters must match). Set
filter_op = "or" to match rows that satisfy any filter.
Rolling Window Tables
Use the limit parameter to create a rolling-window table
that automatically drops the oldest rows when new rows are added beyond
the limit:
# Keep only the last 100 rows
perspective(streaming_data, limit = 100)Note that limit and index are mutually
exclusive.
Indexed Tables
Use the index parameter to create a keyed table. When an
index is set, psp_update() performs upserts—rows with
matching keys are updated instead of appended—and
psp_remove() can delete rows by key.
# Create an indexed table keyed on "cyl"
perspective(mtcars, index = "cyl", plugin = "Datagrid")
# In a Shiny server:
proxy <- perspectiveProxy(session, "viewer")
psp_update(proxy, updated_rows) # upserts by "cyl"
psp_remove(proxy, keys = c(4, 8)) # remove rows where cyl == 4 or 8Exporting Data
Request the current view’s data from the browser:
proxy <- perspectiveProxy(session, "viewer")
psp_export(proxy, format = "csv")
# Result arrives asynchronously:
observeEvent(input$viewer_export, {
cat("Format:", input$viewer_export$format, "\n")
cat("Data:", input$viewer_export$data, "\n")
})Supported formats: "json", "csv",
"columns", "arrow" (base64-encoded).
Saving and Restoring State
Retrieve the current viewer configuration and restore it later:
proxy <- perspectiveProxy(session, "viewer")
# Save current state
psp_save(proxy)
observeEvent(input$viewer_state, {
saved <- input$viewer_state
# Later, restore it:
psp_restore(proxy, saved)
})Update Notifications
Subscribe to table data changes to react when data is updated:
proxy <- perspectiveProxy(session, "viewer")
psp_on_update(proxy, enable = TRUE)
observeEvent(input$viewer_update, {
info <- input$viewer_update
message("Update at ", info$timestamp, " from ", info$source)
})
# To unsubscribe:
psp_on_update(proxy, enable = FALSE)Table Metadata
Query the table for its schema, size, or column names:
proxy <- perspectiveProxy(session, "viewer")
# Get column types
psp_schema(proxy)
observeEvent(input$viewer_schema, {
str(input$viewer_schema) # list(col1 = "float", col2 = "string", ...)
})
# Get row count
psp_size(proxy)
observeEvent(input$viewer_size, {
message("Table has ", input$viewer_size, " rows")
})
# Get column names
psp_columns(proxy)
observeEvent(input$viewer_columns, {
message("Columns: ", paste(input$viewer_columns, collapse = ", "))
})Windowed Export
Export a subset of rows/columns by specifying a window:
proxy <- perspectiveProxy(session, "viewer")
# Export only the first 50 rows
psp_export(proxy, format = "json", start_row = 0, end_row = 50)
# Export rows 100-200, columns 0-3
psp_export(proxy, format = "csv",
start_row = 100, end_row = 200,
start_col = 0, end_col = 3
)Validating Expressions
Check whether expression strings are valid before applying them:
proxy <- perspectiveProxy(session, "viewer")
psp_validate_expressions(proxy, c('"hp" / "wt"', '"invalid_col" + 1'))
observeEvent(input$viewer_validate_expressions, {
result <- input$viewer_validate_expressions
# Contains validation info for each expression
str(result)
})Themes
Set the visual theme with the theme parameter:
perspective(mtcars, theme = "Pro Dark")
perspective(mtcars, theme = "Dracula")
perspective(mtcars, theme = "Gruvbox Dark")Available themes: "Pro Light" (default),
"Pro Dark", "Monokai",
"Solarized Light", "Solarized Dark",
"Vaporwave", "Dracula",
"Gruvbox", "Gruvbox Dark".