--- title: "Introduction to perspectiveR" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to perspectiveR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Overview perspectiveR provides an R interface to the [FINOS Perspective](https://perspective-dev.github.io/) 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 ```{r setup} library(perspectiveR) ``` The simplest usage is to pass a data frame directly: ```{r basic, eval=FALSE} 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: ```{r config, eval=FALSE} 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 ```{r filter-sort, eval=FALSE} perspective(iris, filter = list(c("Species", "==", "setosa")), sort = list(c("Sepal.Length", "desc")) ) ``` ### Computed Expressions ```{r expressions, eval=FALSE} 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: ```{r arrow, eval=FALSE} # 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: ```{r shiny, eval=FALSE} 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`: ```{r filter-op, eval=FALSE} # 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: ```{r limit, eval=FALSE} # 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. ```{r indexed, eval=FALSE} # 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 8 ``` ## Exporting Data Request the current view's data from the browser: ```{r export, eval=FALSE} 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: ```{r save-restore, eval=FALSE} 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: ```{r on-update, eval=FALSE} 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: ```{r metadata, eval=FALSE} 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: ```{r windowed-export, eval=FALSE} 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: ```{r validate-expr, eval=FALSE} 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: ```{r themes, eval=FALSE} 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"`.