library("vegadown")
# makes it easier to set data URL in specs
vw_set_base_url("https://cdn.jsdelivr.net/npm/vega-datasets@2")
Using only vegawidget, you have to compose a Vega or Vega-Lite spec (vegaspec) using lists:
as_vegaspec(
list(
`$schema` = vega_schema(),
data = list("url" = "data/cars.json"),
mark = "point",
encoding = list(
x = list(field = "Horsepower", type = "quantitative"),
y = list(field = "Miles_per_Gallon", type = "quantitative")
)
)
)
It can feel a bit awkward to code using so many lists.
Using vegadown, you can compose specs using JSON or YAML code chunks in RMarkdown:
vegajson
or vegayaml
(vegayml
) as the language of your code chunk.vegadown()
.If you label a code chunk as json-cars
, the vegaspec will be available using vegadown("json-cars")
. You can get the names of available specs using vegadown_names()
.
Here’s what your code chunk would look like:
```{vegajson json-cars}
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/cars.json"},
"mark": "point",
"encoding": {
"x": {"field": "Horsepower", "type": "quantitative"},
"y": {"field": "Miles_per_Gallon", "type": "quantitative"}
}
}
```
Here’s how it would appear:
As noted above, you can also use YAML (vegayaml
or vegayml
); we can also specify sizing options, just as with vegawidget. Here’s what the code-chunk front matter would look like:
```{vegayaml yaml-cars, vega.height=300, vega.width=300}
```
Here’s how it would look in your rendered document:
$schema: "https://vega.github.io/schema/vega-lite/v4.json"
data:
url: "data/cars.json"
mark: point
encoding:
x:
field: Weight_in_lbs
type: quantitative
"y":
field: Miles_per_Gallon
type: quantitative
Note that we quoted the "y"
for the y-encoding; this is because of the (many) reserved Boolean keywords in YAML.
You can access a particular spec using vegadown()
:
vegadown("json-cars")
To get the labels of chunks that use vegadown (thus far in the RMarkdown document):
vegadown_labels()
#> [1] "json-cars" "yaml-cars"
Writing a spec by hand is feasible if your data is available through a URL. However, it would be tedious to specify the data values manually. This is where interpolation is useful.
Let’s introduce this topic using palmerpenguins, by Allison Horst, Alison Hill, and Kristen Gorman:
library("palmerpenguins")
penguins
#> # A tibble: 344 x 8
#> species island bill_length_mm bill_depth_mm flipper_length_… body_mass_g
#> <fct> <fct> <dbl> <dbl> <int> <int>
#> 1 Adelie Torge… 39.1 18.7 181 3750
#> 2 Adelie Torge… 39.5 17.4 186 3800
#> 3 Adelie Torge… 40.3 18 195 3250
#> 4 Adelie Torge… NA NA NA NA
#> 5 Adelie Torge… 36.7 19.3 193 3450
#> 6 Adelie Torge… 39.3 20.6 190 3650
#> 7 Adelie Torge… 38.9 17.8 181 3625
#> 8 Adelie Torge… 39.2 19.6 195 4675
#> 9 Adelie Torge… 34.1 18.1 193 3475
#> 10 Adelie Torge… 42 20.2 190 4250
#> # … with 334 more rows, and 2 more variables: sex <fct>, year <int>
Let’s also choose an adjective for the title of our chart:
We use glue-like notation to interpolate objects from the R environment into the spec; in fact, behind the scenes, we use glue::glue()
. Instead of using {}
as interpolation delimiters, we use the JavaScript interpolation delimiters, ${}
:
$schema: "${vega_schema()}"
data:
values: ${penguins}
title: "Such ${adjective} penguins!"
mark: point
encoding:
x:
field: bill_length_mm
type: quantitative
scale:
zero: false
"y":
field: bill_depth_mm
type: quantitative
scale:
zero: false
color:
field: species
type: nominal
For YAML, these delimiters work either quoted or unquoted.
There are two interpolation contexts:
"${vega_schema()}"
, "${penguins}"
: if the entire string is interpolated, it substitutes the object itself; the object is not coerced to a string. This lets us interpolate data frames into a spec."Such ${adjective} penguins!"
: this is traditional string-interpolation; the interpolated object is coerced to a string.The structure of the spec shows the interpolation:
str(vegadown("interp"))
#> List of 5
#> $ $schema : chr "https://vega.github.io/schema/vega-lite/v4.json"
#> $ data :List of 1
#> ..$ values: tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
#> .. ..$ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
#> .. ..$ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
#> .. ..$ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
#> .. ..$ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
#> .. ..$ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
#> .. ..$ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
#> .. ..$ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
#> .. ..$ year : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
#> $ title : chr "Such interesting penguins!"
#> $ mark : chr "point"
#> $ encoding:List of 3
#> ..$ x :List of 3
#> .. ..$ field: chr "bill_length_mm"
#> .. ..$ type : chr "quantitative"
#> .. ..$ scale:List of 1
#> .. .. ..$ zero: logi FALSE
#> ..$ y :List of 3
#> .. ..$ field: chr "bill_depth_mm"
#> .. ..$ type : chr "quantitative"
#> .. ..$ scale:List of 1
#> .. .. ..$ zero: logi FALSE
#> ..$ color:List of 2
#> .. ..$ field: chr "species"
#> .. ..$ type : chr "nominal"
#> - attr(*, "class")= chr [1:4] "vegaspec_unit" "vegaspec_vega_lite" "vegaspec" "list"
There’s not a lot to this package; it offers:
vegadown()
, vegadown_labels()
.vegajson
and vegayaml
(vegayml
).Putting this together, I learned a lot by studying the knitr-engine code for r2d3. As well, this blog post by Jeff Allen had exactly what I needed to learn to get the vegadown()
function to work.