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.

First steps

Using vegadown, you can compose specs using JSON or YAML code chunks in RMarkdown:

  • use vegajson or vegayaml (vegayml) as the language of your code chunk.
  • afterwards, you can access the vegaspec of a given chunk using 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().

Examples

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"

Interpolation from R

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:

adjective <- sample(
  c("wonderful", "amazing", "interesting", "penguiny"),
  size = 1
)

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:

  • substitution, e.g. "${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.
  • inline, e.g. "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"

Behind the scenes

There’s not a lot to this package; it offers:

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.