---
title: "Concatenation and Repetition"
description: |
  Adapting the small multiples principle to fields that are not exactly
  parallel.
author:
  - name: Kris Sankaran
    url: {}
date: 02-03-2021
output:
  distill::distill_article:
    self_contained: false
---

```{r setup, include=FALSE}
library("robservable")
knitr::opts_chunk$set(cache = FALSE, message = FALSE, warning = FALSE, echo = TRUE)
```

_[Reading](https://observablehq.com/@uwdata/multi-view-composition?collection=@uwdata/visualization-curriculum), [Recording](https://mediaspace.wisc.edu/media/1_zadih83r), [Notebook](https://observablehq.com/@krisrs1128/examples-of-repetition), [Rmarkdown](https://raw.githubusercontent.com/krisrs1128/stat479/master/_posts/2021-01-20-week2-3/week2-3.Rmd)_

Faceting is useful whenever you want different rows of your dataset to appear in
different panels. What if you want to compare different columns, or work with
several datasets? A more general alternative is to use concatenation or
repetition.

We’re going to illustrate this using vega-lite, but the principles also apply to
ggplot2^[The analogous function is called `grid.arrange` from the `gridExtra`
package]. Suppose we want to plot several weather variables next to one another
-- we can use `hconcat`. The idea here is to construct each plot separately and
then combine them only at the very end.

````js
{

  const width = 180,
        height = 130;
  
  // maximum temperature plot
  const temp_max = vl.markLine()
    .data(weather)
    .encode(
      vl.x().month("date"),
      vl.y().average("temp_max"),
      vl.color().fieldN("location")
    )    
    .width(width)
    .height(height);

  // precipitation plot
  const precip = vl.markLine()
    .data(weather)
    .encode(
      vl.x().month("date"),
      vl.y().average("precipitation"),
      vl.color().fieldN("location")
    )    
    .width(width)
    .height(height);

  // precipitation plot
  const wind = vl.markLine()
    .data(weather)
    .encode(
      vl.x().month("date"),
      vl.y().average("wind"),
      vl.color().fieldN("location")
    )
    .width(width)
    .height(height);
  
  return vl.hconcat(temp_max, precip, wind)
    .data(weather)
    .render()
}
````

```{r layout="l-page"}
robservable("@krisrs1128/examples-of-repetition", include = 4, height = 220)
```

This implementation is straightforward, but very clumsy. Whenever you find
yourself copying and pasting code you should ask yourself whether there is a
more elegant way to implement the same idea. In this case, there is, by reusing
the same template for everything except the $y$-axis encoding.

````js
{
  const width = 180,
        height = 130;
  
  const base = vl.markLine()
    .data(weather)
    .encode(
      vl.x().month("date"),
      vl.color().fieldN("location")
    )
    .width(width)
    .height(height);
  
  const temp_max = base.encode(vl.y().average("temp_max")),
        precip = base.encode(vl.y().average("precipitation")),
        wind = base.encode(vl.y().average("wind"));
  
  return vl.hconcat(temp_max, precip, wind).render()
}
````

```{r layout="l-page"}
robservable("@krisrs1128/examples-of-repetition", include = 5, height = 220)
```

That's better, but we can be even more concise, by using repetition. This lets
us reuse the same template by referring to an abstract `vl.repeat()` object in
the encoding.

````js
{
  const width = 180,
        height = 130;
  
  return vl.markLine()
    .data(weather)
    .encode(
      vl.x().month("date"),
      vl.color().fieldN("location"),
      vl.y().average(vl.repeat("column"))
    )
    .width(width)
    .height(height)
    .repeat({"column": ["temp_max", "precipitation", "wind"]})
    .render()
}
````

```{r layout="l-page"}
robservable("@krisrs1128/examples-of-repetition", include = 6, height = 220)
```

Let’s use this idea to generate a scatterplot matrix, a type of plot that shows
all pairs of scatterplots between columns in a dataset. This type of plot is
often useful in revealing correlations between fields.

````js
{
  const width = 140,
        height = 140;
  
  return vl.markPoint({filled: true, size: 3, opacity: 0.8})
    .data(weather)
    .encode(
      vl.x().fieldQ(vl.repeat("column")),
      vl.y().fieldQ(vl.repeat("row")),
      vl.color().fieldN("location")
    )
    .width(width)
    .height(height)
    .repeat({
      column: ["temp_max", "precipitation", "wind"],
      row: ["temp_max", "precipitation", "wind"],
    })    
    .render()
}
````

```{r}
robservable("@krisrs1128/examples-of-repetition", include = 7)
```
