1 Learning Objectives

suppressPackageStartupMessages(library(tidyverse))  # The tidyverse contains ggplot2!
suppressPackageStartupMessages(library(gapminder))
knitr::opts_chunk$set(fig.width=4, fig.height=3)

Why do I teach ggplot2? Shouldn’t beginners see “base” graphics first? I think not. David Robinson explains it well in Don’t teach built-in plotting to beginners (teach ggplot2).

Zev Ross has a lovely blog post: Beautiful plotting in R: A ggplot2 cheatsheet

1.1 Plotting in R

There are three main ways you can produce graphics in R. In order of inception, they are

  • base R
  • lattice (an R package)
  • ggplot2 (an R package)

Base R is tedious and unwieldly. Lattice is a nice option, but I find it requires setting up a plot to then just override everything.

ggplot2 is a very powerful plotting system that…

  • creates graphics by adding layers.
  • is based off of the grammar of graphics (book by Leland Wilkinson) – hence “gg”.
  • comes with the tidyverse meta-package.
  • has a steep learning curve, but pays dividends.

Stackoverflow was my main source of learning. Google what you’re trying to do, and persevere. You can do it.

1.2 ggplot2 framework

First, there are two ways you can make a plot wth ggplot2.

  1. The qplot function: limited functionality. “quick plot”
    • We won’t be focussing on this. No training wheels!
  2. The ggplot function: full functionality.

Let’s go through the basic syntax using the gapminder dataset.

1.2.0.1 Basic scatterplot

Let’s try to make a basic scatterplot of year vs. lifeExp.

Quick ways: - plot(gapminder$year, gapminder$lifeExp) – base R - qplot(year, lifeExp, data=gapminder)ggplot2’s “quick plot”.

Let’s just see the syntax right off the bat.

ggplot(gapminder, aes(x=year, y=lifeExp)) +
    geom_point()

The first line initiates the plot. The second one adds a layer of points.

Let’s see the components of each of these.

Two of the most important aspects of a ggplot are the geometric objects and the scales (part of the grammar of graphics).

  • geometric objects are things that you can draw to represent data.
    • Examples: points, lines, polygons, bars, boxplots, histograms
    • Indicated as new layers with geom_* where * is point, line, …
  • scales are aspects of a geometric object that correspond to a numeric scale.
    • Examples:
      • horizontal (x) position can indicate one variable.
      • vertical (y) position can indicate another variable.
      • size of a point
      • shape of a point
      • transparency
    • aesthetic mappings link variables to scales through the function aes.

Every geometric object has required and optional aesthetic mappings. Check the documentation of the geom_ to see what’s required (in bold). Examples: - points require a horizontal (x) and vertical (y) position. - line segments require starting and ending x and y

Let’s revisit the above plot. The first line outputs an empty plot because there are no geom’s (geometric objects):

p <- ggplot(gapminder, aes(x=year, y=lifeExp))
p

(Yes, we can assign ggplots to variables in R)

Contains: 1. the data frame, gapminder, and 2. an indication of which variables in the data frame go with which scale.

Next, we can add a layer with the + symbol. We add the “point” geometry to “execute” the setup and display points, to obtain the original plot.

This plot would benefit with some alpha transparency – another type of scale. Let’s put in 25% transparency:

p + geom_point(alpha=0.25)

Notes: - This scale is outside of an aesthetic mapping, meaning that ggplot will not associate it with a variable. - Scales can be indicated in the geom call. Scales within aes that appear in the ggplot function apply “globally” to the plot.

Exercises:

  1. Make a scatterplot of gdpPercap vs lifeExp. Store it the output of the ggplot function in a variable called p2.
p2 <- ggplot(gapminder, 
             aes(x=gdpPercap,
                 y=lifeExp))
p2 + geom_point()
  1. To p2, make the size of the points indicate the year, choose a level of alpha transparency that you’re happy with, and make the points your favourite colour.
p2 + geom_point(aes(size=year),
                colour="blue", 
                alpha=0.1)
## This *doesn't* work: (why not?)
p2 + geom_point(aes(size=year, colour="blue"), 
                alpha=0.1)
  1. To p2, colour the points by continent, but this time with year being represented by the size of the points, like we did in the previous exercise.
p2 + geom_point(aes(colour=continent, size=year))
  1. To p2, add another layer called scale_x_log10() in addition to the original geom_point() layer. Make a second plot by redoing the plot in (1), but replacing gdpPercap with log10(gdpPercap). What do you notice?
p2 + geom_point() + scale_x_log10()
ggplot(gapminder, 
             aes(x=log(gdpPercap),
                 y=lifeExp)) + geom_point()

—-Stuff that used to be here has been moved to cm007’s notes and exercises—-

LS0tCnRpdGxlOiAiU1RBVCA1NDUgQ2xhc3MgTWVldGluZyAwNiIKb3V0cHV0OgogICAgaHRtbF9ub3RlYm9vazoKICAgICAgICB0b2M6IHRydWUKICAgICAgICB0aGVtZTogY2VydWxlYW4KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIExlYXJuaW5nIE9iamVjdGl2ZXMKCgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpICAjIFRoZSB0aWR5dmVyc2UgY29udGFpbnMgZ2dwbG90MiEKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZ2FwbWluZGVyKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTMpCmBgYAoKLSBTZWUgW2NtMDA1XShodHRwOi8vc3RhdDU0NS5jb20vY20wMDVfdGlkeXZlcnNlLXRpYmJsZXMuaHRtbCkgZm9yIGBkcGx5cmAgcmVzb3VyY2VzCi0gSmVubnkncyBbYGdncGxvdDJgIHR1dG9yaWFsXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9nZ3Bsb3QyLXR1dG9yaWFsKSBhbmQgY29ycmVzcG9uZGluZyBbc2xpZGVzXShodHRwczovL3NwZWFrZXJkZWNrLmNvbS9qZW5ueWJjL2dncGxvdDItdHV0b3JpYWwpCi0gYGdncGxvdDJgIFtvZmZpY2lhbCB3ZWJzaXRlXShodHRwOi8vZ2dwbG90Mi5vcmcvKSAtLSBoYXMgb3RoZXIgcmVzb3VyY2VzIHRvby4KLSBbVGhlIFIgR3JhcGhpY3MgQ29va2Jvb2tdKGh0dHA6Ly9zaG9wLm9yZWlsbHkuY29tL3Byb2R1Y3QvMDYzNjkyMDAyMzEzNS5kbyksIGFuIGV4Y2VsbGVudCBib29rIGJ5IFdpbnN0b24gQ2hhbmcuIAogICAgLSBUaGUgW2dyYXBocyBzZWN0aW9uXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy8pIG9mIFdpbnN0b24gQ2hhbmcncyB3ZWJzaXRlIENvb2tib29rIGZvciBSLiBUaGUgYm9vayBsaXN0ZWQgYWJvdmUgY29udGFpbnMgbXVjaCBtb3JlIG1hdGVyaWFsLCBidXQgdGhlIHdlYnNpdGUgaXMgZ29vZCB0b28uCi0gQSBbYGdncGxvdDJgIHR1dG9yaWFsXShodHRwOi8vc3Rjb3JwLm5sL1JfY291cnNlL3R1dG9yaWFsX2dncGxvdDIuaHRtbCkgYnkgUGF1bCBIaWVtc3RyYSwgb25lIG9mIFttYW55IGV4Y2VsbGVudCBSIGNvdXJzZXNdKGh0dHA6Ly9zdGNvcnAubmwvUl9jb3Vyc2UvKSBoZSBvZmZlcnMuCi0gW0EgcXVpY2sgaW50cm9kdWN0aW9uIHRvIGdncGxvdDJdKGh0dHA6Ly9pbnVuZGF0YS5vcmcvMjAxMy8wNC8xMC9hLXF1aWNrLWludHJvZHVjdGlvbi10by1nZ3Bsb3QyLykgYnkgS2FydGhpayBSYW0uIFNsaWRlcyBhbmQgY29kZSBmcm9tIGEgMiBob3VyIHRhbGsvaGFuZHMgb24gcHJlc2VudGF0aW9uIGZvciBnZ3Bsb3QyIGJlZ2lubmVycy4KCldoeSBkbyBJIHRlYWNoIGBnZ3Bsb3QyYD8gU2hvdWxkbid0IGJlZ2lubmVycyBzZWUgImJhc2UiIGdyYXBoaWNzIGZpcnN0PyBJIHRoaW5rIG5vdC4gRGF2aWQgUm9iaW5zb24gZXhwbGFpbnMgaXQgd2VsbCBpbiBbRG9uJ3QgdGVhY2ggYnVpbHQtaW4gcGxvdHRpbmcgdG8gYmVnaW5uZXJzICh0ZWFjaCBnZ3Bsb3QyKV0oaHR0cDovL3ZhcmlhbmNlZXhwbGFpbmVkLm9yZy9yL3RlYWNoX2dncGxvdDJfdG9fYmVnaW5uZXJzLykuCgpaZXYgUm9zcyBoYXMgYSBsb3ZlbHkgYmxvZyBwb3N0OiBbQmVhdXRpZnVsIHBsb3R0aW5nIGluIFI6IEEgZ2dwbG90MiBjaGVhdHNoZWV0XShodHRwOi8vemV2cm9zcy5jb20vYmxvZy8yMDE0LzA4LzA0L2JlYXV0aWZ1bC1wbG90dGluZy1pbi1yLWEtZ2dwbG90Mi1jaGVhdHNoZWV0LTMvKQoKCiMjIFBsb3R0aW5nIGluIFIKClRoZXJlIGFyZSB0aHJlZSBtYWluIHdheXMgeW91IGNhbiBwcm9kdWNlIGdyYXBoaWNzIGluIFIuIEluIG9yZGVyIG9mIGluY2VwdGlvbiwgdGhleSBhcmUKCi0gYmFzZSBSCi0gYGxhdHRpY2VgIChhbiBSIHBhY2thZ2UpCi0gYGdncGxvdDJgIChhbiBSIHBhY2thZ2UpCgpCYXNlIFIgaXMgdGVkaW91cyBhbmQgdW53aWVsZGx5LiBMYXR0aWNlIGlzIGEgbmljZSBvcHRpb24sIGJ1dCBJIGZpbmQgaXQgcmVxdWlyZXMgc2V0dGluZyB1cCBhIHBsb3QgdG8gdGhlbiBqdXN0IG92ZXJyaWRlIGV2ZXJ5dGhpbmcuCgpgZ2dwbG90MmAgaXMgYSB2ZXJ5IHBvd2VyZnVsIHBsb3R0aW5nIHN5c3RlbSB0aGF0Li4uCgotIGNyZWF0ZXMgZ3JhcGhpY3MgYnkgYWRkaW5nIGxheWVycy4KLSBpcyBiYXNlZCBvZmYgb2YgdGhlIFtncmFtbWFyIG9mIGdyYXBoaWNzXShodHRwOi8vd3d3LnNwcmluZ2VyLmNvbS9ncC9ib29rLzk3ODAzODcyNDU0NDcpIChib29rIGJ5IExlbGFuZCBXaWxraW5zb24pIC0tIGhlbmNlICJnZyIuCi0gY29tZXMgd2l0aCB0aGUgYHRpZHl2ZXJzZWAgbWV0YS1wYWNrYWdlLgotIGhhcyBhIHN0ZWVwIGxlYXJuaW5nIGN1cnZlLCBidXQgcGF5cyBkaXZpZGVuZHMuCgpTdGFja292ZXJmbG93IHdhcyBteSBtYWluIHNvdXJjZSBvZiBsZWFybmluZy4gR29vZ2xlIHdoYXQgeW91J3JlIHRyeWluZyB0byBkbywgYW5kIHBlcnNldmVyZS4gWW91IGNhbiBkbyBpdC4KCiMjIGBnZ3Bsb3QyYCBmcmFtZXdvcmsKCkZpcnN0LCB0aGVyZSBhcmUgdHdvIHdheXMgeW91IGNhbiBtYWtlIGEgcGxvdCB3dGggYGdncGxvdDJgLgoKMS4gVGhlIGBxcGxvdGAgZnVuY3Rpb246IGxpbWl0ZWQgZnVuY3Rpb25hbGl0eS4gInF1aWNrIHBsb3QiCiAgICAtIFdlIHdvbid0IGJlIGZvY3Vzc2luZyBvbiB0aGlzLiBObyB0cmFpbmluZyB3aGVlbHMhCjIuIFRoZSBgZ2dwbG90YCBmdW5jdGlvbjogZnVsbCBmdW5jdGlvbmFsaXR5LgoKTGV0J3MgZ28gdGhyb3VnaCB0aGUgYmFzaWMgc3ludGF4IHVzaW5nIHRoZSBgZ2FwbWluZGVyYCBkYXRhc2V0LgoKIyMjIyBCYXNpYyBzY2F0dGVycGxvdAoKTGV0J3MgdHJ5IHRvIG1ha2UgYSBiYXNpYyBzY2F0dGVycGxvdCBvZiBgeWVhcmAgdnMuIGBsaWZlRXhwYC4gCgpRdWljayB3YXlzOgogICAgLSBgcGxvdChnYXBtaW5kZXIkeWVhciwgZ2FwbWluZGVyJGxpZmVFeHApYCAtLSBiYXNlIFIKICAgIC0gYHFwbG90KHllYXIsIGxpZmVFeHAsIGRhdGE9Z2FwbWluZGVyKWAgLS0gYGdncGxvdDJgJ3MgInF1aWNrIHBsb3QiLgoKTGV0J3MganVzdCBzZWUgdGhlIHN5bnRheCByaWdodCBvZmYgdGhlIGJhdC4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeD15ZWFyLCB5PWxpZmVFeHApKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgpUaGUgZmlyc3QgbGluZSBpbml0aWF0ZXMgdGhlIHBsb3QuIFRoZSBzZWNvbmQgb25lIF9hZGRzIGEgbGF5ZXJfIG9mIHBvaW50cy4gCgpMZXQncyBzZWUgdGhlIGNvbXBvbmVudHMgb2YgZWFjaCBvZiB0aGVzZS4gCgpUd28gb2YgdGhlIG1vc3QgaW1wb3J0YW50IGFzcGVjdHMgb2YgYSBnZ3Bsb3QgYXJlIHRoZSBfX2dlb21ldHJpYyBvYmplY3RzX18gYW5kIHRoZSBfX3NjYWxlc19fIChwYXJ0IG9mIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzKS4gCgotIF9fZ2VvbWV0cmljIG9iamVjdHNfXyBhcmUgdGhpbmdzIHRoYXQgeW91IGNhbiBkcmF3IHRvIHJlcHJlc2VudCBkYXRhLgogICAgLSBFeGFtcGxlczogcG9pbnRzLCBsaW5lcywgcG9seWdvbnMsIGJhcnMsIGJveHBsb3RzLCBoaXN0b2dyYW1zCiAgICAtIEluZGljYXRlZCBhcyBuZXcgbGF5ZXJzIHdpdGggYGdlb21fKmAgd2hlcmUgYCpgIGlzIGBwb2ludGAsIGBsaW5lYCwgLi4uCi0gX19zY2FsZXNfXyBhcmUgYXNwZWN0cyBvZiBhIGdlb21ldHJpYyBvYmplY3QgdGhhdCBjb3JyZXNwb25kIHRvIGEgbnVtZXJpYyBzY2FsZS4KICAgIC0gRXhhbXBsZXM6CiAgICAgICAgLSBob3Jpem9udGFsICh4KSBwb3NpdGlvbiBjYW4gaW5kaWNhdGUgb25lIHZhcmlhYmxlLgogICAgICAgIC0gdmVydGljYWwgKHkpIHBvc2l0aW9uIGNhbiBpbmRpY2F0ZSBhbm90aGVyIHZhcmlhYmxlLgogICAgICAgIC0gc2l6ZSBvZiBhIHBvaW50CiAgICAgICAgLSBzaGFwZSBvZiBhIHBvaW50CiAgICAgICAgLSB0cmFuc3BhcmVuY3kKICAgIC0gX19hZXN0aGV0aWMgbWFwcGluZ3NfXyBsaW5rIHZhcmlhYmxlcyB0byBzY2FsZXMgdGhyb3VnaCB0aGUgZnVuY3Rpb24gYGFlc2AuCgpFdmVyeSBnZW9tZXRyaWMgb2JqZWN0IGhhcyBfcmVxdWlyZWRfIGFuZCBfb3B0aW9uYWxfIGFlc3RoZXRpYyBtYXBwaW5ncy4gQ2hlY2sgdGhlIGRvY3VtZW50YXRpb24gb2YgdGhlIGBnZW9tX2AgdG8gc2VlIHdoYXQncyByZXF1aXJlZCAoaW4gYm9sZCkuIEV4YW1wbGVzOgogICAgLSBfcG9pbnRzXyByZXF1aXJlIGEgaG9yaXpvbnRhbCAoeCkgYW5kIHZlcnRpY2FsICh5KSBwb3NpdGlvbi4KICAgIC0gbGluZSBfc2VnbWVudHNfIHJlcXVpcmUgc3RhcnRpbmcgYW5kIGVuZGluZyB4IGFuZCB5CgpMZXQncyByZXZpc2l0IHRoZSBhYm92ZSBwbG90LiBUaGUgZmlyc3QgbGluZSBvdXRwdXRzIGFuIGVtcHR5IHBsb3QgYmVjYXVzZSB0aGVyZSBhcmUgbm8gYGdlb21gJ3MgKGdlb21ldHJpYyBvYmplY3RzKToKCmBgYHtyfQpwIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyh4PXllYXIsIHk9bGlmZUV4cCkpCnAKYGBgCgooWWVzLCB3ZSBjYW4gYXNzaWduIGdncGxvdHMgdG8gdmFyaWFibGVzIGluIFIpCgpDb250YWluczoKICAgIDEuIHRoZSBkYXRhIGZyYW1lLCBgZ2FwbWluZGVyYCwgYW5kCiAgICAyLiBhbiBpbmRpY2F0aW9uIG9mIHdoaWNoIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBmcmFtZSBnbyB3aXRoIHdoaWNoIHNjYWxlLgogICAgCk5leHQsIHdlIGNhbiBhZGQgYSBfbGF5ZXJfIHdpdGggdGhlIGArYCBzeW1ib2wuIFdlIGFkZCB0aGUgInBvaW50IiBnZW9tZXRyeSB0byAiZXhlY3V0ZSIgdGhlIHNldHVwIGFuZCBkaXNwbGF5IHBvaW50cywgdG8gb2J0YWluIHRoZSBvcmlnaW5hbCBwbG90LgoKVGhpcyBwbG90IHdvdWxkIGJlbmVmaXQgd2l0aCBzb21lIF9hbHBoYSB0cmFuc3BhcmVuY3lfIC0tIGFub3RoZXIgdHlwZSBvZiBzY2FsZS4gTGV0J3MgcHV0IGluIDI1JSB0cmFuc3BhcmVuY3k6CgpgYGB7cn0KcCArIGdlb21fcG9pbnQoYWxwaGE9MC4yNSkKYGBgCgpOb3RlczoKICAgIC0gVGhpcyBzY2FsZSBpcyBfb3V0c2lkZSBvZiBhbiBhZXN0aGV0aWMgbWFwcGluZ18sIG1lYW5pbmcgdGhhdCBnZ3Bsb3Qgd2lsbCBub3QgYXNzb2NpYXRlIGl0IHdpdGggYSB2YXJpYWJsZS4KICAgIC0gU2NhbGVzIGNhbiBiZSBpbmRpY2F0ZWQgaW4gdGhlIGBnZW9tYCBjYWxsLiBTY2FsZXMgd2l0aGluIGBhZXNgIHRoYXQgYXBwZWFyIGluIHRoZSBgZ2dwbG90YCBmdW5jdGlvbiBhcHBseSAiZ2xvYmFsbHkiIHRvIHRoZSBwbG90LiAKCl9fRXhlcmNpc2VzX186CgoxLiBNYWtlIGEgc2NhdHRlcnBsb3Qgb2YgYGdkcFBlcmNhcGAgdnMgYGxpZmVFeHBgLiBTdG9yZSB+aXR+IHRoZSBvdXRwdXQgb2YgdGhlIGBnZ3Bsb3RgIGZ1bmN0aW9uIGluIGEgdmFyaWFibGUgY2FsbGVkIGBwMmAuCgpgYGB7cn0KcDIgPC0gZ2dwbG90KGdhcG1pbmRlciwgCiAgICAgICAgICAgICBhZXMoeD1nZHBQZXJjYXAsCiAgICAgICAgICAgICAgICAgeT1saWZlRXhwKSkKcDIgKyBnZW9tX3BvaW50KCkKYGBgCgoKMi4gVG8gYHAyYCwgbWFrZSB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzIGluZGljYXRlIHRoZSBgeWVhcmAsIGNob29zZSBhIGxldmVsIG9mIGFscGhhIHRyYW5zcGFyZW5jeSB0aGF0IHlvdSdyZSBoYXBweSB3aXRoLCBhbmQgbWFrZSB0aGUgcG9pbnRzIHlvdXIgZmF2b3VyaXRlIGNvbG91ci4KCmBgYHtyfQpwMiArIGdlb21fcG9pbnQoYWVzKHNpemU9eWVhciksCiAgICAgICAgICAgICAgICBjb2xvdXI9ImJsdWUiLCAKICAgICAgICAgICAgICAgIGFscGhhPTAuMSkKIyMgVGhpcyAqZG9lc24ndCogd29yazogKHdoeSBub3Q/KQpwMiArIGdlb21fcG9pbnQoYWVzKHNpemU9eWVhciwgY29sb3VyPSJibHVlIiksIAogICAgICAgICAgICAgICAgYWxwaGE9MC4xKQpgYGAKCgozLiBUbyBgcDJgLCBjb2xvdXIgdGhlIHBvaW50cyBieSBgY29udGluZW50YCwgfn5idXQgdGhpcyB0aW1lfn4gd2l0aCB5ZWFyIGJlaW5nIHJlcHJlc2VudGVkIGJ5IHRoZSBzaXplIG9mIHRoZSBwb2ludHMsIGxpa2Ugd2UgZGlkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZS4KCmBgYHtyfQpwMiArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1jb250aW5lbnQsIHNpemU9eWVhcikpCmBgYAoKCjQuIFRvIGBwMmAsIGFkZCBhbm90aGVyIGxheWVyIGNhbGxlZCBgc2NhbGVfeF9sb2cxMCgpYCBfaW4gYWRkaXRpb24gdG8gdGhlIG9yaWdpbmFsIGBnZW9tX3BvaW50KClgIGxheWVyXy4gTWFrZSBhIHNlY29uZCBwbG90IGJ5IHJlZG9pbmcgdGhlIHBsb3QgaW4gKDEpLCBidXQgcmVwbGFjaW5nIGBnZHBQZXJjYXBgIHdpdGggYGxvZzEwKGdkcFBlcmNhcClgLiBXaGF0IGRvIHlvdSBub3RpY2U/CgpgYGB7cn0KcDIgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV94X2xvZzEwKCkKZ2dwbG90KGdhcG1pbmRlciwgCiAgICAgICAgICAgICBhZXMoeD1sb2coZ2RwUGVyY2FwKSwKICAgICAgICAgICAgICAgICB5PWxpZmVFeHApKSArIGdlb21fcG9pbnQoKQpgYGAKCgotLS0tU3R1ZmYgdGhhdCB1c2VkIHRvIGJlIGhlcmUgaGFzIGJlZW4gbW92ZWQgdG8gW2NtMDA3J3Mgbm90ZXMgYW5kIGV4ZXJjaXNlc10oY20wMDctbm90ZXNfYW5kX2V4ZXJjaXNlcy5odG1sKS0tLS0=