The vegawidget package is designed to be a low-level interface to the Vega and Vega-Lite visualization frameworks. As such, we hope that the functions provided here can serve as a foundation for other, presumably higher-level, packages. To make it easier for authors of such packages, we offer a function use_vegawidget() to help incorporate vegawidget functions.

You can use use_vegawidget() to import and re-export from vegawidget:

If you implement an S3 class in your package, you can call use_vegawidget() with the name of your class. This will add more boilerplate code; you will have to provide the code to convert from your class to a vegaspec.

If you want to re-export vegawidget’s interactivity functions, you can then use use_vegawidget_interactive() to import and re-export:

For example, the altair package provides an R interface to the Altair Python package. The altair package itself provides the connection to Python, using reticulate; altair’s focus is to provide a means to build a Vega-Lite specification - using higher-level abstractions than lists.

The rendering part of altair is handled by the vegawidget package. A number of vegawidget functions are imported into altair, then re-exported. From altair’s perspective, this import-export operation is handled by a single vegawidget function: use_vegawidget().

Inspiration

A lot of packages use the pipe (%>%) function, so much so that the usethis package offers the function usethis::use_pipe() for package authors to make the pipe part of their package’s offer:

  • adds the magrittr package to the “Imports” section of the package’s DESCRIPTION file
  • creates a new file in the package’s R directory, utils-pipe.R

This new file contains roxygen directives that will handle the importing and exporting the next time you build the package’s documentation. Now, people who install this package will have access to the pipe operator without having to refer explicitly to the magrittr package. It “just works”.

Implementation

We wish to bring a similar robustness and ease-of-use to package authors who want to incorporate vegawidget. To demonstrate how to do this, we will show the process we use for altair.

The use_vegawidget() function makes a couple of related assumptions about your package. It assumes that your package builds Vega and Vega-Lite specifications, and that the object that you use to contain a specification may be an object with an S3 class. In the case of altair, the name of this S3 class is "altair.vegalite.v4.api.TopLevelMixin".

The first step is generic. In your package-project, your R working directory is the package’s root directory. When you run use_vegawidget(), you can provide the name of the S3 class. In the case of altair, we run:

vegawidget::use_vegawidget("altair.vegalite.v4.api.TopLevelMixin")

Like the usethis::use_pipe() example, this call does two things:

  • adds the vegawidget package to the “Imports” section of the package’s DESCRIPTION file
  • creates new files in the package’s R directory, utils-vegawidget.R and utils-vegawidget-altair.vegalite.v4.api.TopLevelMixin.R.

However, you have a little bit more work to do before you’re done. You need to edit your new copy of utils-vegawidget-altair.vegalite.v4.api.TopLevelMixin to tell it how to convert your S3 class to a vegaspec. The part of the file that you need to edit is at the very top.

In the case of altair, the name of the function is as_vegaspec.altair.vegalite.v4.api.TopLevelMixin(); the use_vegawidget() function has already named it using the class-name provided.

as_vegaspec.altair.vegalite.v4.api.TopLevelMixin <- function(spec, ...) {

  # TODO: if needed, insert code to convert your object to
  # something that can be coerced to a vegaspec.
  #
  # e.g.:
  # spec <- spec$to_json()

  vegawidget::as_vegaspec(spec, ...)
}

Here, the commented example code is exactly what is needed. The altair “chart” object, altair.vegalite.v4.api.TopLevelMixin, has a method, to_json() to write out the specification to JSON. The vegawidget as_vegaspec() generic has a method for JSON-formatted strings (it also has a method for lists). So our “finished” product looks like:

as_vegaspec.altair.vegalite.v4.api.TopLevelMixin <- function(spec, ...) {

  spec <- spec$to_json()

  vegawidget::as_vegaspec(spec, ...)
}

One last thing, to enable knit-printing of your class, you should have some code like this in an .onLoad() function, usually kept in a file called zzz.R:

.onLoad <- function(libname, pkgname) {

  # you might have other code here, too

  vegawidget::s3_register(
    "knitr::knit_print",
    "altair.vegalite.v4.api.TopLevelMixin"
  )
}

The function vegawidget::s3_register() is copied from vctrs::s3_register(); for more details please see its documentation.

At this point, once you document and re-build the package, vegawidget is fully integrated into it.