/vegacookbook

A port of the BBC ggplot2 cookbook into vega and vegalite

Primary LanguageNixMIT LicenseMIT

Vegacookbook

./vcb-logo.png

About

This project is a port of the BBC ggplot2 cookbook into vega and vegalite. This makes use of the gapminder dataset which is included as a CSV in this repository. The goal is to have useful examples of vega-lite specifications recreating the main plots in the BBC cookbook. These should be are easy to reuse. If getting the last 20% of the visualisation to agree makes up 80% of the code, then I will opt for only getting it 80% correct. Pull requests are very welcome provided they agree with the goals of the project.

Additional examples

The =example= directory contains additional examples which replicate graphics produced by the BBC. Again, the goal is not to create a pixel-perfect replication of the original but to get close enough that the differences are the type of thing that one might leave for tidying up with a vector graphics editor.

  • =vaccines-given= shows a bar chart of the number of vaccine doses per hundred people.
  • =r-over-time= shows a ribbon plot to display the uncertainty in estimates of the effective reproduction number of SARS-CoV-2 in UK.
  • =ecdc-covid-cases= shows time series of cases of COVID-19 per capta for several European countries.
  • =uk-confirmed-cases= shows a time series of the total number of confirmed SARS-CoV-2 infections on each day in the UK. This example demonstrates how to work with temporal data.
  • =us-map-jh= shows a choropleth of the daily average number of COVID-19 cases per million in each state of the US with some annotations. This example demonstrates how to work with spatial data.

Figures

./.out/plot1.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "Living longer",
        "subtitle": "Life expectancy in Malawi 1952-2007",
        "align": "left",
        "anchor": "start"
    },
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [{"filter": "datum.country == 'Malawi'"}],
    "mark": "line",
    "encoding": {
        "x": {
            "field": "year",
            "type": "temporal",
            "timeUnit": "year",
            "title": null,
            "axis": {"grid": false}
        },
        "y": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": null
        }
    }
}

./.out/plot2.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "Living longer",
        "subtitle": "Life expectancy in China and the US",
        "align": "left",
        "anchor": "start"
    },
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {
                "field": "country",
                "oneOf": ["China", "United States"]
            }
        }
    ],
    "mark": "line",
    "encoding": {
        "x": {
            "field": "year",
            "type": "temporal",
            "timeUnit": "year",
            "title": null,
            "axis": {"grid": false}
        },
        "y": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": null
        },
        "color": {
            "field": "country",
            "type": "nominal",
            "title": null
        }
    },
    "config": {
        "legend": {"orient": "top"}
    }
}

./.out/plot3.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {"field": "year", "equal": 2007}
        },
        {
            "filter": {"field": "continent", "equal": "Africa"}
        },
        {
            "filter": {"field": "lifeExp", "gte": 72}
        }
    ],
    "mark": {
        "type": "bar",
        "color": "#1380A1"
    },
    "encoding": {
        "x": {
            "field": "country",
            "type": "ordinal",
            "sort": "ascending",
            "title": null,
            "axis": {
                "grid": false,
                "ticks": false,
                "labelAngle": 0
            }
        },
        "y": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": null,
            "axis": {
                "ticks": false,
                "domain": false
            }
        }
    },
    "title": {
        "text": "Reunion is highest",
        "subtitle": "Highest African life expectancy, 2007",
        "align": "left",
        "anchor": "start"
    },
    "view": {"stroke": null},
    "width": 250
}

./.out/plot4.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": { "url": "gapminder.csv"},
    "transform": [
        {"filter": {"field": "year", "equal": 2007}},
        {
            "calculate": "datum.lifeExp < 50 ? 'Under 50' : (datum.lifeExp < 65 ? '50-65' : (datum.lifeExp < 80 ? '65-80' : '80+'))",
            "as": "binnedLifeExp"
        }
    ],
    "mark": {
        "type": "bar",
        "size": 50
    },
    "width": {
        "step": 60
    },
    "encoding": {
        "y": {
            "aggregate": "count",
            "field": "binnedLifeExp",
            "stack":  "normalize",
            "title": null,
            "axis": {
                "format": ".1~%",
                "domain": false
            }
        },
        "x": {
            "field": "continent",
            "title": null,
            "axis": {
                "labelAngle": 0
            }
        },
        "color": {
            "field": "binnedLifeExp",
            "title": null,
            "scale": {
                "range": [
                    "#3B1C8C",
                    "#21908D",
                    "#5AC865",
                    "#F9E721"
                ],
                "domain": [
                    "Under 50",
                    "50-65",
                    "65-80",
                    "80+"
                ]
            }
        }
    },
    "title": {
        "text": "How life expectancy varies",
        "subtitle": "% of population by life expectancy band, 2007",
        "align": "left",
        "anchor": "start"
    },
    "config": {
        "legend": {"orient": "top"}
    },
    "view": {"stroke": null}
}

./.out/plot5.png

 {
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {"filter": {"field": "year", "oneOf": [1967,2007]}},
        {"filter": {"field": "country",
                    "oneOf": [
                        "Indonesia",
                        "Libya",
                        "Oman",
                        "Vietnam",
                        "Yemen, Rep."
                    ]}}
    ],
    "mark": "bar",
    "encoding": {
        "column": {
            "field": "country",
            "type": "ordinal",
            "title": null,
            "header": {
                "labelOrient": "bottom"
            }
        },
        "x": {
            "field": "year",
            "type": "ordinal",
            "title": null,
            "axis": {
                "labels": false,
                "ticks": false
            }
        },
        "y": {
            "field": "lifeExp",
            "type": "quantitative",
            "axis": {
                "grid": true,
                "domain": false
            },
            "title": null
        },
        "color": {
            "field": "year",
            "type": "ordinal",
            "title": null,
            "scale": {
                "range": [
                    "#1380A1", "#FAAB18"
                ]
            }
        }
    },
    "config": {
        "view": {"stroke": "transparent"},
        "legend": {"orient": "top"}
    },
    "title": {
        "text": "We're living longer",
        "subtitle": "Biggest life expectancy rise, 1967--2007",
        "align": "left",
        "anchor": "start"
    }
}

./.out/plot6.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {"filter": {"field": "year", "oneOf": [1967,2007]}},
        {"filter": {"field": "country",
                    "oneOf": [
                        "Indonesia",
                        "Libya",
                        "Oman",
                        "Vietnam",
                        "Yemen, Rep."
                    ]}}
    ],
    "encoding": {
        "y": {
            "field": "country",
            "type": "ordinal",
            "title": null,
            "axis": {
                "ticks": false,
                "domain": false,
                "grid": true
            }
        },
        "x": {
            "field": "lifeExp",
            "type": "quantitative",
            "axis": {
                "grid": false,
                "domain": false,
                "ticks": false
            },
            "scale": {
                "zero": false
            },
            "title": null
        },
        "color": {
            "field": "year",
            "type": "ordinal",
            "title": null,
            "scale": {
                "range": [
                    "#1380A1", "#FAAB18"
                ]
            }
        }
    },
    "layer": [
        {
            "mark": {
                "type": "rule",
                "strokeWidth": 5
            },
            "encoding": {
                "x": {
                    "field": "lifeExp", "aggregate": "min"
                },
                "x2": {
                    "field": "lifeExp", "aggregate": "max"
                },
                "color": {"value": "#dddddd"}
            }
        },
        {
            "mark": {
                "type": "point",
                "filled": true,
                "size": 100
            }
        }
    ],
    "config": {
        "view": {"stroke": "transparent"},
        "legend": {"disable": true}
    },
    "title": {
        "text": "We're living longer",
        "subtitle": "Biggest life expectancy rise, 1967--2007",
        "align": "left",
        "anchor": "start"
    }
}

./.out/plot7.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {"filter": {"field": "year", "equal": 2007}},
        {"filter": {"field": "lifeExp", "range": [40,90]}}
    ],
    "mark": "bar",
    "encoding": {
        "x": {
            "bin": true,
            "field": "lifeExp",
            "title": "Years"
        },
        "y": {
            "aggregate": "count",
            "axis": {
                "domain": false
            },
            "title": null
        },
        "color": {"value": "#1380A1"}
    },
    "config": {
        "view": {"stroke": "transparent"}
    },
    "title": {
        "text": "How life expectancy varies",
        "subtitle": "Distribution of life expectancy in 2007",
        "align": "left",
        "anchor": "start"
    }
}

./.out/plot8.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {"field": "year", "equal": 2007}
        },
        {
            "filter": {"field": "continent", "equal": "Africa"}
        },
        {
            "filter": {"field": "lifeExp", "gte": 72}
        }
    ],
    "mark": {
        "type": "bar",
        "color": "#1380A1"
    },
    "encoding": {
        "y": {
            "field": "country",
            "type": "ordinal",
            "sort": "ascending",
            "title": null,
            "axis": {
                "grid": false,
                "ticks": false,
                "labelAngle": 0
            }
        },
        "x": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": null,
            "axis": {
                "ticks": false,
                "domain": false
            },
            "scale": {
                "domain": [0,200]
            }
        }
    },
    "title": {
        "text": "Reunion is highest",
        "subtitle": "Highest African life expectancy, 2007",
        "align": "left",
        "anchor": "start",
        "dx": 40
    },
    "view": {"stroke": null},
    "width": 250
}

./.out/plot9.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "Living longer",
        "subtitle": "Life expectancy in China and the US",
        "align": "left",
        "anchor": "start"
    },
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {
                "field": "country",
                "oneOf": ["China", "United States"]
            }
        }
    ],
    "encoding": {
        "x": {
            "field": "year",
            "type": "temporal",
            "timeUnit": "year",
            "title": null,
            "axis": {"grid": false},
            "scale": {
                "domain": [1940,2050]
            }
        },
        "y": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": null,
            "scale": {
                "domain": [0,85]
            }
        },
        "color": {
            "field": "country",
            "type": "nominal",
            "title": null
        }
    },
    "layer": [
        {
            "mark": "line"
        },
        {
            "transform": [
                {"filter": {"field": "year", "equal": {"year": 2007}}}
            ],
            "mark": {
                "type": "text",
                "align": "left",
                "dx": 10
            },
            "encoding": {
                "text": {
                    "field": "country"
                }
            }
        },
        {
            "data": {
                "values": [
                    {
                        "year": 1980,
                        "lifeExp": 50
                    }
                ]
            },
            "mark": {
                "type": "text",
                "align": "left"
            },
            "encoding": {
                "text": {
                    "value": [
                        "I'm quite a long",
                        "annotation over",
                        "three rows"
                    ]
                },
                "color": {
                    "value": "black"
                }
            }
        }
    ],
    "config": {
        "legend": {"disable": true}
    }
}

./.out/plot10a.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {"field": "year", "equal": 2007}
        },
        {
            "filter": {"field": "continent", "equal": "Africa"}
        },
        {
            "filter": {"field": "lifeExp", "gte": 72}
        },
        {
            "calculate": "format(datum.lifeExp, 'd')",
            "as": "roundedLifeExp"
        }
    ],
    "encoding": {
        "y": {
            "field": "country",
            "type": "ordinal",
            "sort": "ascending",
            "title": null,
            "axis": {
                "grid": false,
                "ticks": false,
                "labelAngle": 0
            }
        },
        "x": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": "Years",
            "axis": {
                "ticks": false,
                "domain": false
            }
        }
    },
    "layer": [
        {
            "mark": {
                "type": "bar",
                "color": "#1380A1"
            }
        },
        {
            "mark": {
                "type": "text",
                "color": "white",
                "dx": -15
            },
            "encoding": {
                "text": {
                    "field": "roundedLifeExp"
                }
            }
        }
    ],
    "title": {
        "text": "Reunion is highest",
        "subtitle": "Highest African life expectancy, 2007",
        "align": "left",
        "anchor": "start",
        "dx": 40
    },
    "view": {"stroke": null},
    "width": 250
}

To adjust the position of the annotation you can put in a fixed value for x and then tweak the dx of the text mark.

./.out/plot10b.png

{
    "mark": {
        "type": "text",
        "color": "white",
        "dx": 10
    },
    "encoding": {
        "x": {
            "value": 0
        },
        "text": {
            "field": "roundedLifeExp"
        }
    }
}

./.out/plot11.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "Living longer",
        "subtitle": "Life expectancy in China and the US",
        "align": "left",
        "anchor": "start"
    },
    "layer": [
        {
            "data": {
                "url": "gapminder.csv"
            },
            "transform": [
                {
                    "filter": {
                        "field": "country",
                        "oneOf": ["China", "United States"]
                    }
                }
            ],
            "mark": "line",
            "encoding": {
                "x": {
                    "field": "year",
                    "type": "temporal",
                    "timeUnit": "year",
                    "title": null,
                    "axis": {
                        "grid": false,
                        "ticks": false,
                        "labelPadding": 10,
                        "values": [1960,1980,2000]
                    },
                    "scale": {
                        "domain": [1950,2020]
                    }
                },
                "y": {
                    "field": "lifeExp",
                    "type": "quantitative",
                    "title": null,
                    "scale": {
                        "domain": [0,85]
                    },
                    "axis": {
                        "domain": false
                    }
                },
                "color": {
                    "field": "country",
                    "type": "nominal",
                    "title": null
                }
            }
        },
        {
            "data": {
                "url": "gapminder.csv"
            },
            "transform": [
                {
                    "filter": {
                        "field": "country",
                        "oneOf": ["China", "United States"]
                    }
                },
                {
                    "filter": {
                        "field": "year",
                        "equal": {"year": 2007}
                    }
                }
            ],
            "mark": {
                "type": "text",
                "align": "left",
                "dx": 10
            },
            "encoding": {
                "text": {
                    "field": "country"
                },
                "x": {
                    "field": "year",
                    "type": "temporal",
                    "timeUnit": "year"
                },
                "y": {
                    "field": "lifeExp",
                    "type": "quantitative",
                    "title": null,
                    "scale": {
                        "domain": [0,85]
                    }
                },
                "color": {
                    "field": "country",
                    "type": "nominal",
                    "title": null
                }
            }
        },
        {
            "data": {
                "values": [
                    {
                        "year": 1980,
                        "lifeExp": 60
                    }
                ]
            },
            "mark": {
                "type": "text",
                "align": "left",
                "dy": -20
            },
            "encoding": {
                "text": {
                    "value": [
                        "I'm quite a long",
                        "annotation over",
                        "three rows"
                    ]
                },
                "color": {
                    "value": "black"
                }
            }
        },
        {
            "data": {
                "values": [{}]
            },
            "encoding": {
                "y": {"datum": 10}
            },
            "mark": {
                "type": "rule",
                "color": "red",
                "strokeDash": [8,8]
            }
        }
    ],
    "config": {
        "legend": {"disable": true}
    },
    "view": {
        "stroke": null
    },
    "width": 500
}

./.out/plot12.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "Asia's rapid growth",
        "subtitle": "Population growth by continent 1952-2007",
        "align": "left",
        "anchor": "start"
    },
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {"filter": "datum.continent != 'Americas'"},
        {"groupby": ["continent", "year"],
         "aggregate": [{
             "op": "sum",
             "field": "pop",
             "as": "pop_total"
             }]
         }
    ],
    "mark": "area",
    "encoding": {
        "x": {
            "field": "year",
            "type": "quantitative",
            "scale": {
                "domain": [1950,2010]
            },
            "title": null,
            "axis": null
        },
        "y": {
            "field": "pop_total",
            "type": "quantitative",
            "title": null,
            "axis": {
                "format": ".1e"
            }
        },
        "color": {
            "field": "continent",
            "type": "nominal"
        },
        "column": {
            "field": "continent",
            "type": "nominal",
            "title": null
        }
    },
    "view": {
        "stroke": null
    },
    "config": {
        "legend": {
            "disable": true
            }
    }
}

./.out/plot13.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "title": {
        "text": "It's all relative",
        "subtitle": "Relative population growth by continent,1952-2007",
        "align": "left",
        "anchor": "start"
    },
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {"filter": "datum.continent != 'Americas'"},
        {"groupby": ["continent", "year"],
         "aggregate": [{
             "op": "sum",
             "field": "pop",
             "as": "pop_total"
             }]
         }
    ],
    "mark": "area",
    "encoding": {
        "x": {
            "field": "year",
            "type": "quantitative",
            "scale": {
                "domain": [1950,2010]
            },
            "title": null,
            "axis": null
        },
        "y": {
            "field": "pop_total",
            "type": "quantitative",
            "title": null,
            "axis": {
                "ticks": false,
                "labels": false,
                "domain": false
            }
        },
        "color": {
            "field": "continent",
            "type": "nominal"
        },
        "facet": {
            "field": "continent",
            "type": "nominal",
            "title": null,
            "columns": 2
        }
    },
    "view": {
        "stroke": null
    },
    "config": {
        "legend": {
            "disable": true
        }
    },
    "resolve": {"scale": {"x": "shared", "y": "independent"}}
}

./.out/plot14.png

{
    "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
    "data": {
        "url": "gapminder.csv"
    },
    "transform": [
        {
            "filter": {"field": "year", "equal": 2007}
        },
        {
            "filter": {"field": "continent", "equal": "Africa"}
        },
        {
            "filter": {"field": "lifeExp", "gte": 72}
        },
        {
            "calculate": "format(datum.lifeExp, 'd')",
            "as": "roundedLifeExp"
        },
        {
            "calculate": "datum.country == 'Mauritius'",
            "as": "isMauritius"
        }
    ],
    "encoding": {
        "y": {
            "field": "country",
            "type": "ordinal",
            "sort": "-x",
            "title": null,
            "axis": {
                "grid": false,
                "ticks": false,
                "labelAngle": 0
            }
        },
        "x": {
            "field": "lifeExp",
            "type": "quantitative",
            "title": "Years",
            "axis": {
                "ticks": false,
                "domain": false
            }
        },
        "color": {
            "field": "isMauritius",
            "scale": {
                "range": [
                    "#dddddd", "#1380A1"
                ]
            }

        }
    },
    "mark": {
        "type": "bar"
    },
    "title": {
        "text": "Reunion is highest",
        "subtitle": "Highest African life expectancy, 2007",
        "align": "left",
        "anchor": "start",
        "dx": 40
    },
    "config": {
        "legend": {
            "disable": true
        }
    },
    "view": {"stroke": null},
    "width": 250
}

References

@manual{stylianou2020bbplot,
  title =        {bbplot: Making ggplot Graphics in BBC News Style},
  author =       {Nassos Stylianou and Will Dahlgreen and Robert Cuffe and Tom
                  Calver and Ransome Mpini},
  year =         2020,
  note =         {R package version 0.2},
}

@manual{bryan2017gapminder,
  title =        {{gapminder: Data from Gapminder}},
  author =       {Jennifer Bryan},
  year =         2017,
  note =         {R package version 0.3.0},
  url =          {https://CRAN.R-project.org/package=gapminder},
}