suppressPackageStartupMessages(library(tidyverse))
library(gapminder)
library(broom)

So you want to fit a model to your data. How can you achieve this with R?

Topics:

  1. What is model-fitting?
  2. How do we fit a model in R?
  3. How can we obtain tidy results from the model output?

What is Model-Fitting?

When variables are not independent, then we can gain information about one variable if we know something about the other.

Examples: Use the scatterplot below:

  1. A car weighs 4000 lbs. What can we say about its mpg?
  2. A car weights less than 3000 lbs. What can we say about its mpg?
library(tidyverse)
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  labs(x = "Weight (1000's of lbs)")

Example: What can we say about rear axle ratio if we know something about quarter mile time?

ggplot(mtcars, aes(qsec, drat)) + 
  geom_point() +
  labs(x = "Quarter mile time",
       y = "Rear axle ratio")

If EDA isn’t enough, we can answer these questions by fitting a model: a curve that predicts Y given X. Aka, a regression curve or a machine learning model.

(There are more comprehensive models too, such as modelling entire distributions, but that’s not what we’re doing here)

There are typically two goals of fitting a model:

  1. Make predictions.
  2. Interpret variable relationships.

Fitting a model in R

Model fitting methods tend to use a common format in R:

method(formula, data, options)

They also tend to have a common output: a special list.

Method:

A function such as:

Formula:

In R, takes the form y ~ x1 + x2 + ... + xp (use column names in your data frame).

Data: The data frame.

Options: Specific to the method.

Exercise:

  1. Fit a linear regression model to life expectancy (“Y”) from year (“X”) by filling in the formula. Notice what appears as the output.
  2. On a new line, use the unclass function to uncover the object’s true nature: a list. Note: it might be easier to use the names function to see what components are included in the list.

First, create a subset of the gapminder dataset containing only the country of `France

Now, using the lm() function we will create the linear model

my_lm

Call:
lm(formula = lifeExp ~ year, data = gapminder_France)

Coefficients:
(Intercept)         year  
  -397.7646       0.2385  

Does that mean that the life expectency at “year 0” was equal to -397.7646?! We are interested in the modeling results around the modeling period which starts at year 1952. To get a meaniningful “interpretable” intercept we can use the I() function.

my_lm

Call:
lm(formula = lifeExp ~ I(year - 1952), data = gapminder_France)

Coefficients:
   (Intercept)  I(year - 1952)  
       67.7901          0.2385  

Use the unclass() function to take a look at how the lm() object actually looks like.

unclass(my_lm)
$coefficients
   (Intercept) I(year - 1952) 
    67.7901282      0.2385014 

$residuals
          1           2           3           4           5           6           7           8           9          10 
-0.38012821 -0.05263520  0.33485781  0.18235082 -0.18015618  0.07733683 -0.05517016  0.20232284  0.12981585  0.11730886 
         11          12 
-0.12519814 -0.25070513 

$effects
   (Intercept) I(year - 1952)                                                                                           
 -257.55220231    14.26030956     0.41516662     0.26479522    -0.09557618     0.16405242     0.03368103     0.29330963 
                                                            
    0.22293823     0.21256684    -0.02780456    -0.15117596 

$rank
[1] 2

$fitted.values
       1        2        3        4        5        6        7        8        9       10       11       12 
67.79013 68.98264 70.17514 71.36765 72.56016 73.75266 74.94517 76.13768 77.33018 78.52269 79.71520 80.90771 

$assign
[1] 0 1

$qr
$qr
   (Intercept) I(year - 1952)
1   -3.4641016   -95.26279442
2    0.2886751    59.79130372
3    0.2886751     0.18965544
4    0.2886751     0.10603124
5    0.2886751     0.02240704
6    0.2886751    -0.06121716
7    0.2886751    -0.14484136
8    0.2886751    -0.22846557
9    0.2886751    -0.31208977
10   0.2886751    -0.39571397
11   0.2886751    -0.47933817
12   0.2886751    -0.56296237
attr(,"assign")
[1] 0 1

$qraux
[1] 1.288675 1.273280

$pivot
[1] 1 2

$tol
[1] 1e-07

$rank
[1] 2

attr(,"class")
[1] "qr"

$df.residual
[1] 10

$xlevels
named list()

$call
lm(formula = lifeExp ~ I(year - 1952), data = gapminder_France)

$terms
lifeExp ~ I(year - 1952)
attr(,"variables")
list(lifeExp, I(year - 1952))
attr(,"factors")
               I(year - 1952)
lifeExp                     0
I(year - 1952)              1
attr(,"term.labels")
[1] "I(year - 1952)"
attr(,"order")
[1] 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
attr(,"predvars")
list(lifeExp, I(year - 1952))
attr(,"dataClasses")
       lifeExp I(year - 1952) 
     "numeric"      "numeric" 

$model
NA

To complicate things further, some info is stored in another list after applying the summary function:

We can use the predict() function to make predictions from the model (default is to use fitting/training data). Here are the predictions:

Or we can predict on a new dataset:

predict(my_lm,years1)
      1       2       3       4       5       6 
79.2382 79.4767 79.7152 79.9537 80.1922 80.4307 

We can plot models (with one predictor/ X variable) using ggplot2 through the geom_smooth() layer. Specifying method="lm" gives us the linear regression fit (but only visually!):

Lets consider another country “Zimbabwe”, which has a unique behavior in the lifeExp and year relationship.

Let’s try fitting a linear model to this relationship

Now we will try to fit a second degree polynomial and see what would that look like.

lm_linear <- lm(data = gapminder,formula = FILL_THIS_IN)
lm_poly <- lm(data = gapminder,formula = FILL_THIS_IN))

anova lets you compare between different models.

anova(lm_linear,lm_poly)

Regression with categorical variables

(lm_cat <- lm(gdpPercap ~ I(year - 1952) + continent, data = gapminder))

How did R know that continent was a categorical variable?

class(gapminder$continent)
levels(gapminder$continent)
contrasts(gapminder$continent)

How can we change the reference level?

gapminder$continent <- relevel(gapminder$continent, ref = "Oceania")

Let’s build a new model

lm_cat2 <- lm(gdpPercap ~ I(year - 1952) + continent, data = gapminder)

Broom

Let’s make it easier to extract info, using the broom package. There are three crown functions in this package, all of which input a fitted model, and outputs a tidy data frame.

  1. tidy: extract statistical summaries about each component of the model.
    • Useful for interpretation task.
  2. augment: add columns to the original data frame, giving information corresponding to each row.
    • Useful for prediction task.
  3. glance: extract statistical summaries about the model as a whole (1-row tibble).
    • Useful for checking goodness of fit.

Exercise: apply all three functions to our fitted model, my_lm. What do you see?

LS0tCnRpdGxlOiAiY20wMTQgV29ya3NoZWV0OiBUaGUgTW9kZWwtRml0dGluZyBQYXJhZGlnbSBpbiBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKbGlicmFyeShnYXBtaW5kZXIpCmxpYnJhcnkoYnJvb20pCmBgYAoKU28geW91IHdhbnQgdG8gZml0IGEgbW9kZWwgdG8geW91ciBkYXRhLiBIb3cgY2FuIHlvdSBhY2hpZXZlIHRoaXMgd2l0aCBSPwoKVG9waWNzOgoKMS4gV2hhdCBfaXNfIG1vZGVsLWZpdHRpbmc/CjIuIEhvdyBkbyB3ZSBmaXQgYSBtb2RlbCBpbiBSPwozLiBIb3cgY2FuIHdlIG9idGFpbiB0aWR5IHJlc3VsdHMgZnJvbSB0aGUgbW9kZWwgb3V0cHV0PwoKIyMgV2hhdCBpcyBNb2RlbC1GaXR0aW5nPwoKV2hlbiB2YXJpYWJsZXMgYXJlIG5vdCBpbmRlcGVuZGVudCwgdGhlbiB3ZSBjYW4gZ2FpbiBpbmZvcm1hdGlvbiBhYm91dCBvbmUgdmFyaWFibGUgaWYgd2Uga25vdyBzb21ldGhpbmcgYWJvdXQgdGhlIG90aGVyLgoKRXhhbXBsZXM6IFVzZSB0aGUgc2NhdHRlcnBsb3QgYmVsb3c6CgoxLiBBIGNhciB3ZWlnaHMgNDAwMCBsYnMuIFdoYXQgY2FuIHdlIHNheSBhYm91dCBpdHMgbXBnPwoyLiBBIGNhciB3ZWlnaHRzIGxlc3MgdGhhbiAzMDAwIGxicy4gV2hhdCBjYW4gd2Ugc2F5IGFib3V0IGl0cyBtcGc/CgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9M30KbGlicmFyeSh0aWR5dmVyc2UpCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIldlaWdodCAoMTAwMCdzIG9mIGxicykiKQpgYGAKCkV4YW1wbGU6IFdoYXQgY2FuIHdlIHNheSBhYm91dCByZWFyIGF4bGUgcmF0aW8gaWYgd2Uga25vdyBzb21ldGhpbmcgYWJvdXQgcXVhcnRlciBtaWxlIHRpbWU/CgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9M30KZ2dwbG90KG10Y2FycywgYWVzKHFzZWMsIGRyYXQpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIlF1YXJ0ZXIgbWlsZSB0aW1lIiwKICAgICAgIHkgPSAiUmVhciBheGxlIHJhdGlvIikKYGBgCgoKSWYgRURBIGlzbid0IGVub3VnaCwgd2UgY2FuIGFuc3dlciB0aGVzZSBxdWVzdGlvbnMgYnkgZml0dGluZyBhIG1vZGVsOiBhIGN1cnZlIHRoYXQgcHJlZGljdHMgWSBnaXZlbiBYLiBBa2EsIGEgX19yZWdyZXNzaW9uIGN1cnZlX18gb3IgYSBfX21hY2hpbmUgbGVhcm5pbmcgbW9kZWxfXy4gCgooVGhlcmUgYXJlIG1vcmUgY29tcHJlaGVuc2l2ZSBtb2RlbHMgdG9vLCBzdWNoIGFzIG1vZGVsbGluZyBlbnRpcmUgZGlzdHJpYnV0aW9ucywgYnV0IHRoYXQncyBub3Qgd2hhdCB3ZSdyZSBkb2luZyBoZXJlKQoKVGhlcmUgYXJlIHR5cGljYWxseSB0d28gZ29hbHMgb2YgZml0dGluZyBhIG1vZGVsOgoKMS4gTWFrZSBwcmVkaWN0aW9ucy4KMi4gSW50ZXJwcmV0IHZhcmlhYmxlIHJlbGF0aW9uc2hpcHMuCgojIyBGaXR0aW5nIGEgbW9kZWwgaW4gUgoKTW9kZWwgZml0dGluZyBtZXRob2RzIHRlbmQgdG8gdXNlIGEgY29tbW9uIGZvcm1hdCBpbiBSOgoKYGBgCm1ldGhvZChmb3JtdWxhLCBkYXRhLCBvcHRpb25zKQpgYGAKClRoZXkgYWxzbyB0ZW5kIHRvIGhhdmUgYSBjb21tb24gb3V0cHV0OiBhIHNwZWNpYWwgX2xpc3RfLiAKCl9fTWV0aG9kX186CgpBIGZ1bmN0aW9uIHN1Y2ggYXM6CgotIExpbmVhciBSZWdyZXNzaW9uOiBgbG1gCi0gR2VuZXJhbGl6ZWQgTGluZWFyIFJlZ3Jlc3Npb246IGBnbG1gCi0gTG9jYWwgcmVncmVzc2lvbjogYGxvZXNzYAotIFF1YW50aWxlIHJlZ3Jlc3Npb246IGBxdWFudHJlZzo6cnFgCi0gLi4uCgpfX0Zvcm11bGFfXzoKCkluIFIsIHRha2VzIHRoZSBmb3JtIGB5IH4geDEgKyB4MiArIC4uLiArIHhwYCAodXNlIGNvbHVtbiBuYW1lcyBpbiB5b3VyIGRhdGEgZnJhbWUpLgoKX19EYXRhX186IFRoZSBkYXRhIGZyYW1lLgoKX19PcHRpb25zX186IFNwZWNpZmljIHRvIHRoZSBtZXRob2QuCgpFeGVyY2lzZToKCjEuIEZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIGxpZmUgZXhwZWN0YW5jeSAoIlkiKSBmcm9tIHllYXIgKCJYIikgYnkgZmlsbGluZyBpbiB0aGUgZm9ybXVsYS4gTm90aWNlIHdoYXQgYXBwZWFycyBhcyB0aGUgb3V0cHV0LgoyLiBPbiBhIG5ldyBsaW5lLCB1c2UgdGhlIGB1bmNsYXNzYCBmdW5jdGlvbiB0byB1bmNvdmVyIHRoZSBvYmplY3QncyB0cnVlIG5hdHVyZTogYSBsaXN0LiBOb3RlOiBpdCBtaWdodCBiZSBlYXNpZXIgdG8gdXNlIHRoZSBgbmFtZXNgIGZ1bmN0aW9uIHRvIHNlZSB3aGF0IGNvbXBvbmVudHMgYXJlIGluY2x1ZGVkIGluIHRoZSBsaXN0LiAKCkZpcnN0LCBjcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGBnYXBtaW5kZXJgIGRhdGFzZXQgY29udGFpbmluZyBvbmx5IHRoZSBjb3VudHJ5IG9mIGBGcmFuY2UKYGBge3J9CmdhcG1pbmRlcl9GcmFuY2UgPC0gZ2FwbWluZGVyICU+JSAKICAgZmlsdGVyKGNvdW50cnkgPT0gIkZyYW5jZSIpCmdhcG1pbmRlcl9GcmFuY2UKYGBgCgpOb3csIHVzaW5nIHRoZSBgbG0oKWAgZnVuY3Rpb24gd2Ugd2lsbCBjcmVhdGUgdGhlIGxpbmVhciBtb2RlbApgYGB7cn0KbXlfbG0gPC0gbG0obGlmZUV4cCB+IHllYXIsIGdhcG1pbmRlcl9GcmFuY2UpCm15X2xtCmBgYApEb2VzIHRoYXQgbWVhbiB0aGF0IHRoZSBsaWZlIGV4cGVjdGVuY3kgYXQgInllYXIgMCIgd2FzIGVxdWFsIHRvIC0zOTcuNzY0Nj8hCldlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBtb2RlbGluZyByZXN1bHRzIGFyb3VuZCB0aGUgbW9kZWxpbmcgcGVyaW9kIHdoaWNoIHN0YXJ0cyBhdCB5ZWFyIDE5NTIuIFRvIGdldCBhIG1lYW5pbmluZ2Z1bCAiaW50ZXJwcmV0YWJsZSIgaW50ZXJjZXB0IHdlIGNhbiB1c2UgdGhlIGBJKClgIGZ1bmN0aW9uLgpgYGB7cn0KbXlfbG0gPC0gbG0obGlmZUV4cCB+IEkoeWVhci0xOTUyKSwgZGF0YSA9IGdhcG1pbmRlcl9GcmFuY2UpCm15X2xtCmBgYAoKVXNlIHRoZSBgdW5jbGFzcygpYCBmdW5jdGlvbiB0byB0YWtlIGEgbG9vayBhdCBob3cgdGhlIGBsbSgpYCBvYmplY3QgYWN0dWFsbHkgbG9va3MgbGlrZS4KYGBge3J9CnVuY2xhc3MobXlfbG0pCmBgYAoKVG8gY29tcGxpY2F0ZSB0aGluZ3MgZnVydGhlciwgc29tZSBpbmZvIGlzIHN0b3JlZCBpbiBfYW5vdGhlcl8gbGlzdCBhZnRlciBhcHBseWluZyB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uOgoKYGBge3J9CnN1bW1hcnkobXlfbG0pCmxtX3Jlc2lkIDwtIGF1Z21lbnQobXlfbG0pCmdncGxvdChsbV9yZXNpZCwgYWVzKC5yZXNpZCkpICsgCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuNSkKYGBgCgpXZSBjYW4gdXNlIHRoZSBgcHJlZGljdCgpYCBmdW5jdGlvbiB0byBtYWtlIHByZWRpY3Rpb25zIGZyb20gdGhlIG1vZGVsIChkZWZhdWx0IGlzIHRvIHVzZSBmaXR0aW5nL3RyYWluaW5nIGRhdGEpLiBIZXJlIGFyZSB0aGUgcHJlZGljdGlvbnM6CgpgYGB7cn0KZ2FwbWluZGVyX0ZyYW5jZQpwcmVkaWN0KG15X2xtKSAlPiUgCiAgaGVhZCgpCnBsb3QobXlfbG0pCmBgYApPciB3ZSBjYW4gcHJlZGljdCBvbiBhIG5ldyBkYXRhc2V0OgpgYGB7cn0KeWVhcnMxID0gZGF0YS5mcmFtZSh5ZWFyID0gc2VxKDIwMDAsIDIwMDUpKQpwcmVkaWN0KG15X2xtLHllYXJzMSkKYGBgCgoKCldlIGNhbiBwbG90IG1vZGVscyAod2l0aCBvbmUgcHJlZGljdG9yLyBYIHZhcmlhYmxlKSB1c2luZyBgZ2dwbG90MmAgdGhyb3VnaCB0aGUgYGdlb21fc21vb3RoKClgIGxheWVyLiBTcGVjaWZ5aW5nIGBtZXRob2Q9ImxtImAgZ2l2ZXMgdXMgdGhlIGxpbmVhciByZWdyZXNzaW9uIGZpdCAoYnV0IG9ubHkgdmlzdWFsbHkhKToKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZSA9IEYpICsKICAgIHNjYWxlX3hfbG9nMTAoKQpgYGAKTGV0cyBjb25zaWRlciBhbm90aGVyIGNvdW50cnkgIlppbWJhYndlIiwgd2hpY2ggaGFzIGEgdW5pcXVlIGJlaGF2aW9yIGluIHRoZSBgbGlmZUV4cGAgYW5kIGB5ZWFyYCByZWxhdGlvbnNoaXAuCmBgYHtyfQpnYXBtaW5kZXJfWmltYmFid2UgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSA9PSAiWmltYmFid2UiKQpnYXBtaW5kZXJfWmltYmFid2UgJT4lIGdncGxvdChhZXMoeWVhciwgbGlmZUV4cCkpICsgZ2VvbV9wb2ludCgpCmBgYApMZXQncyB0cnkgZml0dGluZyBhIGxpbmVhciBtb2RlbCB0byB0aGlzIHJlbGF0aW9uc2hpcApgYGB7cn0KZ2dwbG90KGdhcG1pbmRlcl9aaW1iYWJ3ZSwgYWVzKHllYXIsbGlmZUV4cCkpICsgZ2VvbV9wb2ludCgpK2dlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikKYGBgCk5vdyB3ZSB3aWxsIHRyeSB0byBmaXQgYSBzZWNvbmQgZGVncmVlIHBvbHlub21pYWwgYW5kIHNlZSB3aGF0IHdvdWxkIHRoYXQgbG9vayBsaWtlLgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlcl9aaW1iYWJ3ZSwgYWVzKHllYXIsIGxpZmVFeHApKSArIGdlb21fcG9pbnQoKStnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoSSh4LTE5NTIpLCBkZWdyZWUgPSAyKSkKYGBgCgpgYGB7cn0KbG1fbGluZWFyIDwtIGxtKGRhdGEgPSBnYXBtaW5kZXIsZm9ybXVsYSA9IEZJTExfVEhJU19JTikKbG1fcG9seSA8LSBsbShkYXRhID0gZ2FwbWluZGVyLGZvcm11bGEgPSBGSUxMX1RISVNfSU4pKQpgYGAKYGFub3ZhYCBsZXRzIHlvdSBjb21wYXJlIGJldHdlZW4gZGlmZmVyZW50IG1vZGVscy4KYGBge3J9CmFub3ZhKGxtX2xpbmVhcixsbV9wb2x5KQpgYGAKIyMgUmVncmVzc2lvbiB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKYGBge3J9CihsbV9jYXQgPC0gbG0oZ2RwUGVyY2FwIH4gSSh5ZWFyIC0gMTk1MikgKyBjb250aW5lbnQsIGRhdGEgPSBnYXBtaW5kZXIpKQpgYGAKSG93IGRpZCBSIGtub3cgdGhhdCBjb250aW5lbnQgd2FzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGU/CmBgYHtyfQpjbGFzcyhnYXBtaW5kZXIkY29udGluZW50KQpsZXZlbHMoZ2FwbWluZGVyJGNvbnRpbmVudCkKY29udHJhc3RzKGdhcG1pbmRlciRjb250aW5lbnQpCmBgYApIb3cgY2FuIHdlIGNoYW5nZSB0aGUgcmVmZXJlbmNlIGxldmVsPwpgYGB7cn0KZ2FwbWluZGVyJGNvbnRpbmVudCA8LSByZWxldmVsKGdhcG1pbmRlciRjb250aW5lbnQsIHJlZiA9ICJPY2VhbmlhIikKYGBgCkxldCdzIGJ1aWxkIGEgbmV3IG1vZGVsCmBgYHtyfQpsbV9jYXQyIDwtIGxtKGdkcFBlcmNhcCB+IEkoeWVhciAtIDE5NTIpICsgY29udGluZW50LCBkYXRhID0gZ2FwbWluZGVyKQpgYGAKCgojIyBCcm9vbQoKTGV0J3MgbWFrZSBpdCBlYXNpZXIgdG8gZXh0cmFjdCBpbmZvLCB1c2luZyB0aGUgYGJyb29tYCBwYWNrYWdlLiBUaGVyZSBhcmUgdGhyZWUgY3Jvd24gZnVuY3Rpb25zIGluIHRoaXMgcGFja2FnZSwgYWxsIG9mIHdoaWNoIGlucHV0IGEgZml0dGVkIG1vZGVsLCBhbmQgb3V0cHV0cyBhIHRpZHkgZGF0YSBmcmFtZS4KCjEuIGB0aWR5YDogZXh0cmFjdCBzdGF0aXN0aWNhbCBzdW1tYXJpZXMgYWJvdXQgZWFjaCBjb21wb25lbnQgb2YgdGhlIG1vZGVsLgogICAgLSBVc2VmdWwgZm9yIF9pbnRlcnByZXRhdGlvbl8gdGFzay4KMi4gYGF1Z21lbnRgOiBhZGQgY29sdW1ucyB0byB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSwgZ2l2aW5nIGluZm9ybWF0aW9uIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCByb3cuCiAgICAtIFVzZWZ1bCBmb3IgX3ByZWRpY3Rpb25fIHRhc2suCjMuIGBnbGFuY2VgOiBleHRyYWN0IHN0YXRpc3RpY2FsIHN1bW1hcmllcyBhYm91dCB0aGUgbW9kZWwgYXMgYSB3aG9sZSAoMS1yb3cgdGliYmxlKS4KICAgIC0gVXNlZnVsIGZvciBjaGVja2luZyBnb29kbmVzcyBvZiBmaXQuCgpFeGVyY2lzZTogYXBwbHkgYWxsIHRocmVlIGZ1bmN0aW9ucyB0byBvdXIgZml0dGVkIG1vZGVsLCBgbXlfbG1gLiBXaGF0IGRvIHlvdSBzZWU/CgpgYGB7cn0KYGBg