Code
library(vegabrite)
library(altair)
library(dplyr)
library(tidyr)
library(readr)
library(jsonlite)
library(palmerpenguins)
vega_data <- import_vega_data()Data 304: Visualzing Data and Models
The basic building block in Vega-Lite’s grammar of interaction.
2 types:
Can be used throughout the remainder of the chart specification
Can also optionally be bound to input widgets (e.g., sliders or drop down menus).
| property | type | description |
|---|---|---|
| name | string | required; unique name for the parameter |
| value | any | initial value; default is undefined |
| bind | binding | binds the paramter to an input element (slider, radio buttons, etc.) |
| expr | expression | This expression may include other parameters, in which case the parameter value will automatically update in reponse to changes in the other parameters. |
height, width, padding, autosize, background (can be redefined as user parameters)datum, item, event, parent (may not be redefined, but may be used)cursor (for geting information about cursor location)[1] "vl_bind_checkbox_input" "vl_bind_direct_input" "vl_bind_input"
[4] "vl_bind_radio_input" "vl_bind_range_input" "vl_bind_select_input"
We’ve seen range inputs before, but here is a different (perhaps silly) use case.
Steps:
vl_add_aparameter()vl_bind_range_input()list(expr = "...")penguins_scatter |>
vl_add_parameter("width", value = 200) |>
vl_add_parameter("height", value = 200) |>
vl_bind_range_input("width", min = 100, max = 400, step = 1) |>
vl_bind_range_input("height", min = 100, max = 400, step = 1) |>
vl_add_properties(
width = list(expr = "width"),
height = list(expr = "height")
)Here we use a dropdown to filter the data set to one species.
Here we use the legend to highlight one species. We’ll talk about “condition” shortly.
2 types:
[1] "vl_add_interval_selection" "vl_add_point_selection"
[3] "vl_config_selection"
vl_config_selection() can be used to set defaults for point and interal selections.
Note: The default behavior of empty = TRUE means that an empty selection selects everything.
Note: Here we set empty = FALSE so an empty selection selects nothing.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"height": 200,
"width": 400,
"mark": {
"type": "rect"
},
"encoding": {
"x": {
"field": "Origin",
"type": "nominal"
},
"y": {
"field": "Cylinders",
"type": "ordinal"
},
"color": {
"condition": {
"param": "selection",
"aggregate": "count",
"empty": false
},
"value": "gray"
}
},
"data": {
"url": "https://cdn.jsdelivr.net/npm/vega-datasets@v1.29.0/data/cars.json"
},
"params": [
{
"name": "selection",
"select": {
"type": "interval"
}
}
]
}
Vega-Lite conditions are like if-statements.
Two ways to use them:
condition argument in an encoding (see above)
condition versions of encoding channels (see below)
[1] "vl_condition_angle" "vl_condition_color"
[3] "vl_condition_description" "vl_condition_fill"
[5] "vl_condition_fillOpacity" "vl_condition_href"
[7] "vl_condition_opacity" "vl_condition_order"
[9] "vl_condition_shape" "vl_condition_size"
[11] "vl_condition_stroke" "vl_condition_strokeDash"
[13] "vl_condition_strokeOpacity" "vl_condition_strokeWidth"
[15] "vl_condition_text" "vl_condition_tooltip"
[17] "vl_condition_url"
What elements do we need to create this interaction?
# create invisible points b/c nearest selecting
# only works with points, not with lines
points <- base |>
vl_mark_point(opacity = 0) |>
vl_encode_y("price:Q") |>
# create selection parameter
vl_add_point_selection(
"hilite", on = "pointermove",
nearest = TRUE, encodings = list("x"))
# draw a rule only for selected date
# empty = FALSE gives empty selection when nothing has been selected
rule <- base |>
vl_mark_rule(color = "black") |>
vl_filter(list(param = "hilite", empty = FALSE))
# less efficient: draw all the text, but most of it is invisible
# but it demonstrates how to use an encoding condition
text <- base |>
vl_mark_text(align = "right", dx = -6, dy = 8) |>
vl_encode_text("date:T") |>
vl_encode_y(value = 1) |>
vl_encode_color(value = "black") |>
vl_encode_opacity(value = 0.0) |>
vl_condition_opacity(param = "hilite", value = 1, empty = FALSE)
lines + points + rule + text{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"layer": [
{
"layer": [
{
"data": {
"url": "https://cdn.jsdelivr.net/npm/vega-datasets@v1.29.0/data/stocks.csv"
},
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative"
},
"color": {
"field": "symbol",
"type": "nominal"
}
},
"height": 200,
"width": 400,
"mark": {
"type": "line"
}
},
{
"data": {
"url": "https://cdn.jsdelivr.net/npm/vega-datasets@v1.29.0/data/stocks.csv"
},
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative"
}
},
"height": 200,
"width": 400,
"mark": {
"opacity": 0,
"type": "point"
},
"params": [
{
"name": "hilite",
"select": {
"encodings": [
"x"
],
"nearest": true,
"on": "pointermove",
"type": "point"
}
}
]
}
]
},
{
"data": {
"url": "https://cdn.jsdelivr.net/npm/vega-datasets@v1.29.0/data/stocks.csv"
},
"encoding": {
"x": {
"field": "date",
"type": "temporal"
}
},
"height": 200,
"width": 400,
"mark": {
"color": "black",
"type": "rule"
},
"transform": [
{
"filter": {
"param": "hilite",
"empty": false
}
}
]
}
]
}
penguins_scatter |>
vl_add_interval_selection(
name = "panzoom",
# bind the interval selection to x and y the scales
bind = "scales", encodings = c("x", "y"),
# pan/zoom affects all panels (the default in this situation)
# other options are "intersect" and "union"
resolve = "global", # each panel affects all panels (default)
translate = TRUE, # enable panning (default)
zoom = TRUE) # enable zooming (default)detail <- vl_chart(height = 200, width = 480) |>
vl_mark_area() |>
vl_encode_x('date:T') |>
vl_axis_x(title = '') |>
vl_encode_y('price:Q')
overview <- vl_chart(width = 480, height = 40) |>
vl_mark_area() |>
vl_encode_x('date:T') |>
vl_encode_y('price:Q') |>
vl_axis_y(tickCount = 3, grid = FALSE)
vl_vconcat(detail, overview) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/sp500.csv") vl_vconcat(
detail |>
# use selected x range to reset the domain
vl_scale_x(domain = list(param = 'brush')),
overview |>
# select a arange of x values (date) by brushing
vl_add_interval_selection(name = 'brush', encodings = list('x'))
) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/sp500.csv") Splom = scatter plot matrix
splom <-
vl_chart(width = 120, height = 120) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/cars.json") |>
vl_mark_point() |>
vl_encode_x(field = list("repeat" = "column"), type = "quantitative") |>
vl_encode_y(field = list("repeat" = "row"), type = "quantitative") |>
vl_repeat_row("Horsepower", "Acceleration", "Miles_per_Gallon") |>
vl_repeat_col("Miles_per_Gallon", "Acceleration", "Horsepower")
splomsplom |>
# select with a brush -- selection affects all panels
vl_add_interval_selection("brush", resolve = "global") |>
# default (non-selected) color of dots is gray
vl_encode_color(value = "grey") |>
# if selected, color them according to the origin field
vl_condition_color(param = "brush", field = "Origin", type = "nominal")---
title: |
| Interactive Graphics
subtitle: "Data 304: Visualzing Data and Models"
embed-resources: true
format:
html:
theme: [yeti, ./slides-304.scss]
output-file: interactive-graphics-noslides.html
code-tools: true
revealjs:
theme: [default, ./slides-304.scss]
scrollable: true
auto-stretch: false
execute:
echo: true
---
## Setup
```{r}
#| label: setup
#| message: false
#| warning: false
#| code-fold: true
library(vegabrite)
library(altair)
library(dplyr)
library(tidyr)
library(readr)
library(jsonlite)
library(palmerpenguins)
vega_data <- import_vega_data()
```
# Parameters {.smaller}
* The basic building block in Vega-Lite’s grammar of interaction.
* 2 types:
* **variable parameters** store a value
* **selection parameters** store (instructions for creating) data subsets
* used to map user input (e.g., mouse clicks and drags)
to data queries .
* Can be used throughout the remainder of the chart specification
* to determine encoding rules,
* to filter data points,
* to determine data extents,
* in expression strings,
* etc.
* Can also optionally be bound to input widgets (e.g., sliders or drop down menus).
* changing the widget changes the parameter, and the graphic updates accordingly.
## Defining parameters
property | type | description
---------|------------|--------------------------------------------------
name | string | required; unique name for the parameter
value | any | initial value; default is `undefined`
bind | binding | binds the paramter to an input element (slider, radio buttons, etc.)
expr | expression | This expression may include other parameters, in which case the parameter value will automatically update in reponse to changes in the other parameters.
```{r}
vl_chart() |>
# create a parameter and initialize to five.
vl_add_parameter(name = "my_param", value = 5) |>
# create a calculated parameter
vl_add_parameter(name = "my_squared_param", expr = "my_param * my_param")
```
# Value Parameters
## Some built-in parameters
* `height`, `width`, `padding`, `autosize`, `background` (can be redefined as user parameters)
* `datum`, `item`, `event`, `parent` (may not be redefined, but may be used)
* `cursor` (for geting information about cursor location)
## Widgets we can bind value parametes to
```{r}
apropos("vl_bind")
```
* Consult documentation for each to get details.
## Range input
We've seen range inputs before, but here is a different (perhaps silly)
use case.
:::{.panel-tabset}
### Penguins scatterplot
```{r}
#| code-fold: true
penguins_scatter <-
vl_chart( ) |>
vl_mark_point() |>
vl_encode_x("bill_length_mm:Q") |>
vl_encode_y("bill_depth_mm:Q") |>
vl_add_data(penguins)
penguins_scatter
```
### Penguins scatter -- adjustable size
Steps:
* create parameter(s): `vl_add_aparameter()`
* bind parameter(s): `vl_bind_range_input()`
* use parameter values in expressions: `list(expr = "...")`
```{r}
#| code-fold: true
penguins_scatter |>
vl_add_parameter("width", value = 200) |>
vl_add_parameter("height", value = 200) |>
vl_bind_range_input("width", min = 100, max = 400, step = 1) |>
vl_bind_range_input("height", min = 100, max = 400, step = 1) |>
vl_add_properties(
width = list(expr = "width"),
height = list(expr = "height")
)
```
:::
## Select input
:::{.panel-tabset}
### Fewer penguins
Here we use a dropdown to filter the data set to one species.
```{r}
#| code-fold: true
penguins_scatter |>
vl_add_parameter("species_param", value = "Adelie") |>
vl_bind_select_input(
parameter_name = "species_param",
name = "Species: ", # for title on plot
options = list("Adelie","Gentoo","Chinstrap")) |>
vl_filter("datum.species == species_param")
```
### Interactive legend (penguins)
Here we use the legend to highlight one species.
We'll talk about "condition" shortly.
```{r}
#| code-fold: true
penguins_scatter |>
vl_encode_color("species:N") |>
vl_add_point_selection(
"species_param", bind = "legend", encodings = list("color")) |>
vl_encode_opacity(value = 0.2) |>
vl_condition_opacity(param = "species_param", value = 1)
```
### Cars scatter plot
```{r}
#| code-fold: true
cars_scatter <-
vl_chart(width = 400, height = 200) |>
vl_add_data(url = vega_data$cars$url) |>
vl_mark_point() |>
vl_encode_x(field = "Horsepower", type = "quantitative") |>
vl_encode_y(field = "Miles_per_Gallon", type = "quantitative") |>
vl_encode_color("Cylinders:O")
cars_scatter
```
### Cars scatter plot -- with point selection
```{r}
#| code-fold: true
cars_scatter |>
vl_add_point_selection("org", fields = list("Origin")) |>
vl_bind_select_input("org", options = list(NA,"Europe","Japan","USA")) |>
vl_encode_color(value = "grey") |>
vl_condition_color(param = "org", field = "Cylinders", type = "ordinal")
```
:::
# Selection parameters
2 types:
* **point** -- multiple discrete data values
* **interval** -- a continuous range of data values
```{r}
apropos("_selection")
```
`vl_config_selection()` can be used to set defaults for
point and interal selections.
## Point vs interval selections
:::{.panel-tabset}
### Basic heatmap
```{r}
#| code-fold: true
#| label: heatmap
heatmap <-
vl_chart(width = 400, height = 200) |>
vl_mark_rect() |>
vl_encode(x = "Origin:N", y = "Cylinders:O") |>
vl_encode_color(aggregate = "count") |>
vl_add_data(url = vega_data$cars$url)
heatmap
```
### Point selection
Note: The default behavior of `empty = TRUE`
means that an empty selection selects everything.
```{r}
#| code-fold: true
#| label: point-selection
heatmap |>
vl_add_point_selection(name = "selection") |>
vl_encode_color(
value = "gray", # default color
condition = list(param = "selection", aggregate = "count")
)
```
### Interval selection
Note: Here we set `empty = FALSE` so an
empty selection selects nothing.
```{r}
#| code-fold: true
#| label: interval-selection
heatmap_interval <-
heatmap |>
vl_add_interval_selection(name = "selection") |>
vl_encode_color(
value = "gray", # default color
condition =
list(param = "selection",
aggregate = "count", empty = FALSE)
)
heatmap_interval
```
### Vega-Lite
```{r}
heatmap_interval |> format()
```
:::
## Conditions
Vega-Lite conditions are like if-statements.
* Selection parameters can be used as predicates (TRUE/FALSE)
in conditions.
Two ways to use them:
1. `condition` argument in an encoding (see above)
2. condition versions of encoding channels (see below)
```{r}
apropos('vl_condition')
```
## Interactive guideline
:::{.panel-tabset}
### Basic time series (grouped)
```{r}
#| code-fold: true
base <-
vl_chart() |>
vl_add_data_url(vega_data$stocks$url) |>
vl_encode_x("date:T") |>
vl_add_properties(width = 400, height = 200)
lines <- base |>
vl_mark_line() |>
vl_encode_y("price:Q") |>
vl_encode_color("symbol:N")
lines
```
### Nearest date selection
What elements do we need to create this interaction?
```{r}
#| code-fold: true
# create invisible points b/c nearest selecting
# only works with points, not with lines
points <- base |>
vl_mark_point(opacity = 0) |>
vl_encode_y("price:Q") |>
# create selection parameter
vl_add_point_selection(
"hilite", on = "pointermove",
nearest = TRUE, encodings = list("x"))
# draw a rule only for selected date
# empty = FALSE gives empty selection when nothing has been selected
rule <- base |>
vl_mark_rule(color = "black") |>
vl_filter(list(param = "hilite", empty = FALSE))
# less efficient: draw all the text, but most of it is invisible
# but it demonstrates how to use an encoding condition
text <- base |>
vl_mark_text(align = "right", dx = -6, dy = 8) |>
vl_encode_text("date:T") |>
vl_encode_y(value = 1) |>
vl_encode_color(value = "black") |>
vl_encode_opacity(value = 0.0) |>
vl_condition_opacity(param = "hilite", value = 1, empty = FALSE)
lines + points + rule + text
```
### Vega-Lite
```{r}
(lines + points + rule) |> format()
```
:::
## Pan and zoom (penguins)
:::{.tabset}
### Static version
```{r}
#| label: penguins-scatter
#| code-fold: true
penguins_scatter <-
vl_chart(width = 125, height = 125) |>
vl_mark_circle() |>
vl_encode_x("body_mass_g:Q") |>
vl_encode_y("bill_length_mm:Q") |>
vl_encode_color("species:N") |>
vl_encode_row("sex:N") |>
vl_encode_column("island:N") |>
vl_add_data(penguins)
penguins_scatter
```
### Interactive version
* What sort of selection?
* How is it used?
```{r}
#| label: penguins-pan-zoom
#| code-fold: true
penguins_scatter |>
vl_add_interval_selection(
name = "panzoom",
# bind the interval selection to x and y the scales
bind = "scales", encodings = c("x", "y"),
# pan/zoom affects all panels (the default in this situation)
# other options are "intersect" and "union"
resolve = "global", # each panel affects all panels (default)
translate = TRUE, # enable panning (default)
zoom = TRUE) # enable zooming (default)
```
:::
## Using one view to control another
:::{.panel-tabset}
### S&P 500 over a decade
```{r}
#| code-fold: true
detail <- vl_chart(height = 200, width = 480) |>
vl_mark_area() |>
vl_encode_x('date:T') |>
vl_axis_x(title = '') |>
vl_encode_y('price:Q')
overview <- vl_chart(width = 480, height = 40) |>
vl_mark_area() |>
vl_encode_x('date:T') |>
vl_encode_y('price:Q') |>
vl_axis_y(tickCount = 3, grid = FALSE)
vl_vconcat(detail, overview) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/sp500.csv")
```
### Interactive version
* What sort of selection?
* How is it used?
```{r}
#| code-fold: true
vl_vconcat(
detail |>
# use selected x range to reset the domain
vl_scale_x(domain = list(param = 'brush')),
overview |>
# select a arange of x values (date) by brushing
vl_add_interval_selection(name = 'brush', encodings = list('x'))
) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/sp500.csv")
```
:::
## Panning and zooming across views
:::{.panel.tabset}
### Basic splom
Splom = scatter plot matrix
```{r}
#| label: splom
#| code-fold: true
splom <-
vl_chart(width = 120, height = 120) |>
vl_add_data(url = "https://vega.github.io/vega-editor/app/data/cars.json") |>
vl_mark_point() |>
vl_encode_x(field = list("repeat" = "column"), type = "quantitative") |>
vl_encode_y(field = list("repeat" = "row"), type = "quantitative") |>
vl_repeat_row("Horsepower", "Acceleration", "Miles_per_Gallon") |>
vl_repeat_col("Miles_per_Gallon", "Acceleration", "Horsepower")
splom
```
### Interactive splom
* What sort of selection?
* How is it used?
```{r}
#| label: splom-interactive
#| code-fold: true
splom |>
# select with a brush -- selection affects all panels
vl_add_interval_selection("brush", resolve = "global") |>
# default (non-selected) color of dots is gray
vl_encode_color(value = "grey") |>
# if selected, color them according to the origin field
vl_condition_color(param = "brush", field = "Origin", type = "nominal")
```
# More examples