| country | continent | year | life | population | gdp |
|---|---|---|---|---|---|
| Afghanistan | Asia | 2007 | 43.828 | 31889923 | 974.5803 |
| Albania | Europe | 2007 | 76.423 | 3600523 | 5937.0295 |
| Algeria | Africa | 2007 | 72.301 | 33333216 | 6223.3675 |
| Angola | Africa | 2007 | 42.731 | 12420476 | 4797.2313 |
| Argentina | Americas | 2007 | 75.320 | 40301927 | 12779.3796 |
Appendix B — Functions around the grammar
Every word in the kernel belongs to a sentence. point is a mark, x is a channel, bin is a transform, and none of them means anything on its own. They are words, and a word needs a sentence.
The functions in this appendix are not words. They take a plot you have already written and act on it, or they hand you a table to write a sentence about. None of them joins a sentence with + or *, and none of them is part of the grammar.
The name alone will not tell you which is which. x_label is a word of the grammar and joins two words with an underscore, exactly as render_svg does. What separates them is where each one can stand. A word of the grammar stands inside a sentence, and these three never do. That is why none of them appears in The kernel card.
| What it gives you | |
|---|---|
render_svg(plot) |
The plot as text, in one string |
save_gif(plot, path) |
A played plot as a file that moves |
gog_table(name) |
One of this book’s example tables |
The list is short. Everything on it follows the same rule: it acts on a finished plot, or it hands you a table, and it has no place in a sentence.
B.1 render_svg()
A plot is a specification until something draws it. render_svg() draws it and gives you the result as ordinary text:
picture <- render_svg(data(gapminder_2007) + point + x(gdp) + y(life))
cat(substr(picture, 1, 51), "\n")<svg xmlns="http://www.w3.org/2000/svg" width="800"
cat(nchar(picture), "characters\n")14581 characters
The first line of output shows where that text begins. The second line gives its whole length. So the picture you have been looking at is also this: a run of characters that starts <svg and states a width. After that it names every point, every tick and every label.
That text is the whole plot. It is what every notebook, every web page and this book itself receives. Writing it to a file with a .svg name gives you a picture that any browser opens and a vector drawing program edits.
The function has the same name in all four languages, and takes the plot as its one argument.
B.1.1 What the file carries
An SVG file is text, so anyone who opens it can read it. If your table holds something private, open the file and read it yourself before you publish. The file carries the drawing and nothing else.
A plot draws the columns you map. Columns you never map are absent from the file, and so are their names:
private <- data.frame(
patient = c("A. Kim", "B. Lee", "C. Park", "D. Choi"),
email = c("ak@ex.com", "bl@ex.com", "cp@ex.com", "dc@ex.com"),
weight = c(61.2, 74.8, 55.4, 68.0)
)
picture <- render_svg(data(private) + bar * bin + x(weight))
sapply(c("A. Kim", "email", "ex.com", "61.2"), grepl, x = picture, fixed = TRUE)A. Kim email ex.com 61.2
FALSE FALSE FALSE FALSE
Every search returns FALSE. The sentence maps one column, so the drawing used one column. Even the weights are gone, because bin counts rows into intervals and the bars show the counts.
The missing weights come from the transform rather than from a general rule. A mark that summarizes draws no individual row. A mark that does not summarize draws one glyph per row, and each position is computed from one value:
picture <- render_svg(data(private) + point + x(weight) + y(patient))
lengths(gregexpr("<circle", picture))[1] 4
Four rows, four circles, and the four names printed along the axis. Every plotting tool works this way, and it is what you asked for. A scatter plot draws each value you gave it.
So the rule is not that the file is safe. It is narrower and more useful. What you map is what you publish. A column you never map cannot be recovered from the file, and a private column that stays out of the sentence stays private.
B.1.2 Plots that carry the table
One case works differently, and you should know it before you publish.
Four kinds of plot are drawn twice: one that binds brush(), one in the cube, one on the globe, and a network given an angle. Once when you write it, and once again in the reader’s browser, because the reader can move a selection or turn the view. The second drawing needs the numbers, so the page carries the table beside the picture.
It carries the whole table, including columns the sentence never maps. The table that show rows (a button under the plot, explained in Selection) opens lists only the mapped columns, and the page itself carries every column.
An SVG file never carries the table, even when its sentence names a brush. render_svg() gives you the picture and stops.
One line of code keeps a private column out of the page. Bind the columns you plot and drop the rest:
shown <- private[, c("patient", "weight")]
data(shown) + point + x(weight) + y(patient) + brush(weight)“Given the shown table: points, x is weight, y is patient, and let the reader select weight.”
That sentence names a brush, so this page carries a table. The table it carries is shown, which has two columns. Bind private instead and email would be in this page too.
B.1.3 What “byte for byte” means
This book says in several places that the four languages draw the same picture byte for byte. render_svg() is the function that claim is about, and the idea behind it is simpler than the phrase sounds.
A plot looks like a picture on your screen. Underneath, it is the text you just saw. Two plots are the same when that text is the same, byte for byte. Not similar. Not close enough to look the same. The same.
That is how the four languages are kept in step. Nearly every sentence in this book is written in R, in Python, in Julia and in JavaScript. Each one that draws is turned into its text, and the four texts are compared byte by byte. One wrong byte counts as a disagreement and has to be explained.
The reason for such a strict test is that a loose one would miss things. Nobody looks at four pictures and decides whether they match. A person cannot see a bar that is half a pixel too wide, or a color one step away from the right one. This check sees both, and it compares the four texts the same way every time.
B.2 save_gif()
A plot that binds play() moves in a browser, because the SVG carries its own timing. A message, a slide in a talk and a post on a social site cannot play that timing. save_gif() writes the same sequence as a file that they do play:
| country | continent | year | life | population | gdp |
|---|---|---|---|---|---|
| Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.4453 |
| Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.8530 |
| Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.1007 |
| Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.1971 |
| Afghanistan | Asia | 1972 | 36.088 | 13079460 | 739.9811 |
ring <- file.path(tempdir(), "ring.gif")
save_gif(data(gm_all) + bar * mean + x(continent) + y(life) + polar() +
play(year),
ring, scale = 2)
cat(basename(ring), round(file.size(ring) / 1024), "KB\n")ring.gif 687 KB
Nothing has to be installed. scale multiplies the canvas, which is 800 by 600 pixels unless the plot’s theme says otherwise.
Three of the four languages take scale as a named argument. JavaScript has no named arguments, so it takes the same setting in the options object it uses everywhere else:
| R | save_gif(p, "ring.gif", scale = 2) |
| Python | save_gif(p, "ring.gif", scale=2) |
| Julia | save_gif(p, "ring.gif", scale = 2) |
| JavaScript | save_gif(p, "ring.gif", { scale: 2 }) |
A plot with no play() is refused rather than written as a file of one frame. The Play chapter has that refusal, and the reason there is no video version.
B.3 gog_table()
Every shared table this book plots is available to you by name, so you can run any example here without writing a data reader first:
gapminder_2007 <- gog_table("gapminder_2007")
data(gapminder_2007) + point + x(gdp) + y(life)That block does not run on this page, and the reason is worth one line. The call reads the table over the network. A page that fetched on every build would leave the book unable to render without a working connection.
The book’s data is the chapter for this function. It lists every table in the book and shows the call in each of the four languages.
gog_table() exists for reading this book rather than for your own work. When the data is yours, bind it with data() and skip this entirely.