Introduction to R - Part III

Recap

library(tidyverse)
gapminder <- read_csv("raw_data/gapminder.csv")

Summarising and grouping with dplyr

The summarise function can take any R function that takes a vector of values (i.e. a column from a data frame) and returns a single value. Some of the more useful functions include:

  • min minimum value
  • max maximum value
  • sum sum of values
  • mean mean value
  • sd standard deviation
  • median median value
  • IQR the interquartile range
  • n_distinct the number of distinct values
  • n the number of observations (Note: this is a special function that doesn’t take a vector argument, i.e. column)
summarise(gapminder, min(lifeExp), max(gdpPercap), mean(pop))

It is also possible to summarise using a function that takes more than one value, i.e. from multiple columns. For example, we could compute the correlation between year and life expectancy. Here we also assign names to the table that is produced.

gapminder %>% 
summarise(MinLifeExpectancy = min(lifeExp), 
          MaximumGDP = max(gdpPercap), 
          AveragePop = mean(pop), 
          Correlation = cor(year, lifeExp))

However, it is not particularly useful to calculate such values from the entire table as we have different continents and years. The group_by function allows us to split the table into different categories, and compute summary statistics for each year (for example).

gapminder %>% 
    group_by(year) %>% 
    summarise(MinLifeExpectancy = min(lifeExp), 
              MaximumGDP = max(gdpPercap), 
              AveragePop = mean(pop))

Other summary statistics that can be useful include first and last which are used to report the first and last values for a particular group. For instance, we might want to look at the increase in wealth over time for each country by extracting their gdpPercap in 1952 and 2007.

gapminder %>% 
  group_by(country) %>% 
  arrange(year) %>% 
summarise(StartGDP = first(gdpPercap), EndGDP = last(gdpPercap), GDPIncrease = EndGDP - StartGDP)

The nice thing about summarise is that it can followed up by any of the other dplyr verbs that we have met so far (select, filter, arrange..etc).

Returning to the correlation between life expectancy and year, we can summarise as follows:-

gapminder %>%     
    group_by(country) %>% 
    summarise(Correlation = cor(year , lifeExp))

We can then arrange the table by the correlation to see which countries have the lowest correlation

gapminder %>%      
    group_by(country) %>% 
    summarise(Correlation = cor(year , lifeExp)) %>% 
    arrange(Correlation)

We can filter the results to find obsevations of interest

gapminder %>%      
    group_by(country) %>% 
    summarise(Correlation = cor(year , lifeExp)) %>% 
    filter(Correlation < 0)

The countries we identify could then be used as the basis for a plot.

filter(gapminder, country %in% c("Rwanda","Zambia","Zimbabwe")) %>% 
  ggplot(aes(x=year, y=lifeExp,col=country)) + geom_line()




Exercise

  • Produce a plot to show the change in average gdpPercap for each continent over time.
  • see below for a suggestion
    • HINT: you will need to specifiy the geom_col function to create the bar plot



Joining

In many real life situations, data are spread across multiple tables or spreadsheets. Usually this occurs because different types of information about a subject, e.g. a patient, are collected from different sources. It may be desirable for some analyses to combine data from two or more tables into a single data frame based on a common column, for example, an attribute that uniquely identifies the subject.

dplyr provides a set of join functions for combining two data frames based on matches within specified columns. For those familiar with such SQL, these operations are very similar to carrying out join operations between tables in a relational database.

As a toy example, lets consider two data frames that contain the names of various bands, and the instruments that they play:-

band_instruments
band_members

There are various ways in which we can join these two tables together. We will just consider the case of a “left join”.

Animated gif by Garrick Aden-Buie

left_join returns all rows from the first data frame regardless of whether there is a match in the second data frame. Rows with no match are included in the resulting data frame but have NA values in the additional columns coming from the second data frame.

Animations to illustrate other types of join are available at https://github.com/gadenbuie/tidy-animated-verbs

left_join(band_members, band_instruments)
Joining, by = "name"

right_join is similar but returns all rows from the second data frame that have a match with rows in the first data frame based on the specified column.

right_join(band_members, band_instruments)
Joining, by = "name"

inner_join only returns those rows where matches could be made

inner_join(band_members, band_instruments)
Joining, by = "name"



Exercise (open-ended)

  • The file medal_table.csv in the raw_data/ project sub-directory contains data about how many medals how been won by various countries at the Beijing summer olympics of 2008.
  • Read this csv file into R and join with the gapminder data from 2007
  • What interesting summaries / plots can you make from the data? For example…
  • what countries have the greatest proportion of gold medals (ignore countries with too few medals)
  • calculate the number of medals won per million people and re-arrange by this new measure. What countries perform best?
  • how similar is the distribution of total medals between continents?
  • do countries with a larger population tend to win more medals?
  • do countries with larger GDP tend to win more medals?
  • are these trends consistent among different continents?



LS0tCnRpdGxlOiAiUiBDcmFzaCBDb3Vyc2UiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIEludHJvZHVjdGlvbiB0byBSIC0gUGFydCBJSUkKCiMjIFJlY2FwCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZ2FwbWluZGVyIDwtIHJlYWRfY3N2KCJyYXdfZGF0YS9nYXBtaW5kZXIuY3N2IikKYGBgCgoKIyBTdW1tYXJpc2luZyBhbmQgZ3JvdXBpbmcgd2l0aCBkcGx5cgoKVGhlIGBzdW1tYXJpc2VgIGZ1bmN0aW9uIGNhbiB0YWtlIGFueSBSIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYSB2ZWN0b3Igb2YgdmFsdWVzIChpLmUuIGEgY29sdW1uIGZyb20gYSBkYXRhIGZyYW1lKSBhbmQgcmV0dXJucyBhIHNpbmdsZSB2YWx1ZS4gU29tZSBvZiB0aGUgbW9yZSB1c2VmdWwgZnVuY3Rpb25zIGluY2x1ZGU6CgotIGBtaW5gIG1pbmltdW0gdmFsdWUKLSBgbWF4YCBtYXhpbXVtIHZhbHVlCi0gYHN1bWAgc3VtIG9mIHZhbHVlcwotIGBtZWFuYCBtZWFuIHZhbHVlCi0gYHNkYCBzdGFuZGFyZCBkZXZpYXRpb24KLSBgbWVkaWFuYCBtZWRpYW4gdmFsdWUKLSBgSVFSYCB0aGUgaW50ZXJxdWFydGlsZSByYW5nZQotIGBuX2Rpc3RpbmN0YCB0aGUgbnVtYmVyIG9mIGRpc3RpbmN0IHZhbHVlcwotIGBuYCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAoTm90ZTogdGhpcyBpcyBhIHNwZWNpYWwgZnVuY3Rpb24gdGhhdCBkb2VzbuKAmXQgdGFrZSBhIHZlY3RvciBhcmd1bWVudCwgaS5lLiBjb2x1bW4pCgoKYGBge3J9CnN1bW1hcmlzZShnYXBtaW5kZXIsIG1pbihsaWZlRXhwKSwgbWF4KGdkcFBlcmNhcCksIG1lYW4ocG9wKSkKYGBgCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHN1bW1hcmlzZSB1c2luZyBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgbW9yZSB0aGFuIG9uZSB2YWx1ZSwgaS5lLiBmcm9tIG11bHRpcGxlIGNvbHVtbnMuIEZvciBleGFtcGxlLCB3ZSBjb3VsZCBjb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHllYXIgYW5kIGxpZmUgZXhwZWN0YW5jeS4gSGVyZSB3ZSBhbHNvIGFzc2lnbiBuYW1lcyB0byB0aGUgdGFibGUgdGhhdCBpcyBwcm9kdWNlZC4KCmBgYHtyfQpnYXBtaW5kZXIgJT4lIApzdW1tYXJpc2UoTWluTGlmZUV4cGVjdGFuY3kgPSBtaW4obGlmZUV4cCksIAogICAgICAgICAgTWF4aW11bUdEUCA9IG1heChnZHBQZXJjYXApLCAKICAgICAgICAgIEF2ZXJhZ2VQb3AgPSBtZWFuKHBvcCksIAogICAgICAgICAgQ29ycmVsYXRpb24gPSBjb3IoeWVhciwgbGlmZUV4cCkpCmBgYAoKSG93ZXZlciwgaXQgaXMgbm90IHBhcnRpY3VsYXJseSB1c2VmdWwgdG8gY2FsY3VsYXRlIHN1Y2ggdmFsdWVzIGZyb20gdGhlIGVudGlyZSB0YWJsZSBhcyB3ZSBoYXZlIGRpZmZlcmVudCBjb250aW5lbnRzIGFuZCB5ZWFycy4gVGhlIGBncm91cF9ieWAgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIHNwbGl0IHRoZSB0YWJsZSBpbnRvIGRpZmZlcmVudCBjYXRlZ29yaWVzLCBhbmQgY29tcHV0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIGVhY2ggeWVhciAoZm9yIGV4YW1wbGUpLgoKYGBge3J9CmdhcG1pbmRlciAlPiUgCiAgICBncm91cF9ieSh5ZWFyKSAlPiUgCiAgICBzdW1tYXJpc2UoTWluTGlmZUV4cGVjdGFuY3kgPSBtaW4obGlmZUV4cCksIAogICAgICAgICAgICAgIE1heGltdW1HRFAgPSBtYXgoZ2RwUGVyY2FwKSwgCiAgICAgICAgICAgICAgQXZlcmFnZVBvcCA9IG1lYW4ocG9wKSkKYGBgCgpPdGhlciBzdW1tYXJ5IHN0YXRpc3RpY3MgdGhhdCBjYW4gYmUgdXNlZnVsIGluY2x1ZGUgYGZpcnN0YCBhbmQgYGxhc3RgIHdoaWNoIGFyZSB1c2VkIHRvIHJlcG9ydCB0aGUgZmlyc3QgYW5kIGxhc3QgdmFsdWVzIGZvciBhIHBhcnRpY3VsYXIgZ3JvdXAuIEZvciBpbnN0YW5jZSwgd2UgbWlnaHQgd2FudCB0byBsb29rIGF0IHRoZSBpbmNyZWFzZSBpbiB3ZWFsdGggb3ZlciB0aW1lIGZvciBlYWNoIGNvdW50cnkgYnkgZXh0cmFjdGluZyB0aGVpciBgZ2RwUGVyY2FwYCBpbiBgMTk1MmAgYW5kIGAyMDA3YC4KCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogIGdyb3VwX2J5KGNvdW50cnkpICU+JSAKICBhcnJhbmdlKHllYXIpICU+JSAKc3VtbWFyaXNlKFN0YXJ0R0RQID0gZmlyc3QoZ2RwUGVyY2FwKSwgRW5kR0RQID0gbGFzdChnZHBQZXJjYXApLCBHRFBJbmNyZWFzZSA9IEVuZEdEUCAtIFN0YXJ0R0RQKQoKYGBgCgoKVGhlIG5pY2UgdGhpbmcgYWJvdXQgYHN1bW1hcmlzZWAgaXMgdGhhdCBpdCBjYW4gZm9sbG93ZWQgdXAgYnkgYW55IG9mIHRoZSBvdGhlciBgZHBseXJgIHZlcmJzIHRoYXQgd2UgaGF2ZSBtZXQgc28gZmFyIChgc2VsZWN0YCwgYGZpbHRlcmAsIGBhcnJhbmdlYC4uZXRjKS4gCgpSZXR1cm5pbmcgdG8gdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gbGlmZSBleHBlY3RhbmN5IGFuZCB5ZWFyLCB3ZSBjYW4gc3VtbWFyaXNlIGFzIGZvbGxvd3M6LQoKYGBge3J9CmdhcG1pbmRlciAlPiUgICAgIAogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXNlKENvcnJlbGF0aW9uID0gY29yKHllYXIgLCBsaWZlRXhwKSkKYGBgCldlIGNhbiB0aGVuIGFycmFuZ2UgdGhlIHRhYmxlIGJ5IHRoZSBjb3JyZWxhdGlvbiB0byBzZWUgd2hpY2ggY291bnRyaWVzIGhhdmUgdGhlIGxvd2VzdCBjb3JyZWxhdGlvbgoKYGBge3J9CmdhcG1pbmRlciAlPiUgICAgICAKICAgIGdyb3VwX2J5KGNvdW50cnkpICU+JSAKICAgIHN1bW1hcmlzZShDb3JyZWxhdGlvbiA9IGNvcih5ZWFyICwgbGlmZUV4cCkpICU+JSAKICAgIGFycmFuZ2UoQ29ycmVsYXRpb24pCmBgYAoKV2UgY2FuIGZpbHRlciB0aGUgcmVzdWx0cyB0byBmaW5kIG9ic2V2YXRpb25zIG9mIGludGVyZXN0CgpgYGB7cn0KZ2FwbWluZGVyICU+JSAgICAgIAogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXNlKENvcnJlbGF0aW9uID0gY29yKHllYXIgLCBsaWZlRXhwKSkgJT4lIAogICAgZmlsdGVyKENvcnJlbGF0aW9uIDwgMCkKYGBgCgpUaGUgY291bnRyaWVzIHdlIGlkZW50aWZ5IGNvdWxkIHRoZW4gYmUgdXNlZCBhcyB0aGUgYmFzaXMgZm9yIGEgcGxvdC4KCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ICVpbiUgYygiUndhbmRhIiwiWmFtYmlhIiwiWmltYmFid2UiKSkgJT4lIAogIGdncGxvdChhZXMoeD15ZWFyLCB5PWxpZmVFeHAsY29sPWNvdW50cnkpKSArIGdlb21fbGluZSgpCmBgYCAKCioqKioqKgoqKioqKioKKioqKioqCgojIyMgRXhlcmNpc2UgCgotIFByb2R1Y2UgYSBwbG90IHRvIHNob3cgdGhlIGNoYW5nZSBpbiBhdmVyYWdlIGBnZHBQZXJjYXBgIGZvciBlYWNoIGNvbnRpbmVudCBvdmVyIHRpbWUuCi0gc2VlIGJlbG93IGZvciBhIHN1Z2dlc3Rpb24KICAgICsgSElOVDogeW91IHdpbGwgbmVlZCB0byBzcGVjaWZpeSB0aGUgYGdlb21fY29sYCBmdW5jdGlvbiB0byBjcmVhdGUgdGhlIGJhciBwbG90CgoqKioqKioKKioqKioqCioqKioqKgoKCgoKYGBge3IgZWNobz1GQUxTRX0KZ2FwbWluZGVyICU+JSBncm91cF9ieSh5ZWFyLGNvbnRpbmVudCkgJT4lIAogIHN1bW1hcmlzZShXZWFsdGg9bWVhbihnZHBQZXJjYXApKSAlPiUgZ2dwbG90KGFlcyh4PXllYXIseT1XZWFsdGgsZmlsbD1jb250aW5lbnQpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgICsgZmFjZXRfd3JhcCh+Y29udGluZW50KQpgYGAKCgojIEpvaW5pbmcKCkluIG1hbnkgcmVhbCBsaWZlIHNpdHVhdGlvbnMsIGRhdGEgYXJlIHNwcmVhZCBhY3Jvc3MgbXVsdGlwbGUgdGFibGVzIG9yIHNwcmVhZHNoZWV0cy4gVXN1YWxseSB0aGlzIG9jY3VycyBiZWNhdXNlIGRpZmZlcmVudCB0eXBlcyBvZiBpbmZvcm1hdGlvbiBhYm91dCBhIHN1YmplY3QsIGUuZy4gYSBwYXRpZW50LCBhcmUgY29sbGVjdGVkIGZyb20gZGlmZmVyZW50IHNvdXJjZXMuIEl0IG1heSBiZSBkZXNpcmFibGUgZm9yIHNvbWUgYW5hbHlzZXMgdG8gY29tYmluZSBkYXRhIGZyb20gdHdvIG9yIG1vcmUgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBiYXNlZCBvbiBhIGNvbW1vbiBjb2x1bW4sIGZvciBleGFtcGxlLCBhbiBhdHRyaWJ1dGUgdGhhdCB1bmlxdWVseSBpZGVudGlmaWVzIHRoZSBzdWJqZWN0LgoKYGRwbHlyYCBwcm92aWRlcyBhIHNldCBvZiBqb2luIGZ1bmN0aW9ucyBmb3IgY29tYmluaW5nIHR3byBkYXRhIGZyYW1lcyBiYXNlZCBvbiBtYXRjaGVzIHdpdGhpbiBzcGVjaWZpZWQgY29sdW1ucy4gRm9yIHRob3NlIGZhbWlsaWFyIHdpdGggc3VjaCBTUUwsIHRoZXNlIG9wZXJhdGlvbnMgYXJlIHZlcnkgc2ltaWxhciB0byBjYXJyeWluZyBvdXQgam9pbiBvcGVyYXRpb25zIGJldHdlZW4gdGFibGVzIGluIGEgcmVsYXRpb25hbCBkYXRhYmFzZS4KCkFzIGEgdG95IGV4YW1wbGUsIGxldHMgY29uc2lkZXIgdHdvIGRhdGEgZnJhbWVzIHRoYXQgY29udGFpbiB0aGUgbmFtZXMgb2YgdmFyaW91cyBiYW5kcywgYW5kIHRoZSBpbnN0cnVtZW50cyB0aGF0IHRoZXkgcGxheTotCmBgYHtyfQpiYW5kX2luc3RydW1lbnRzCmJhbmRfbWVtYmVycwpgYGAKClRoZXJlIGFyZSB2YXJpb3VzIHdheXMgaW4gd2hpY2ggd2UgY2FuIGpvaW4gdGhlc2UgdHdvIHRhYmxlcyB0b2dldGhlci4gV2Ugd2lsbCBqdXN0IGNvbnNpZGVyIHRoZSBjYXNlIG9mIGEgImxlZnQgam9pbiIuCgohW10oaW1hZ2VzL2xlZnQtam9pbi5naWYpCgoqQW5pbWF0ZWQgZ2lmIGJ5IEdhcnJpY2sgQWRlbi1CdWllKgoKYGxlZnRfam9pbmAgcmV0dXJucyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCBkYXRhIGZyYW1lIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aGVyZSBpcyBhIG1hdGNoIGluIHRoZSBzZWNvbmQgZGF0YSBmcmFtZS4gUm93cyB3aXRoIG5vIG1hdGNoIGFyZSBpbmNsdWRlZCBpbiB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgYnV0IGhhdmUgTkEgdmFsdWVzIGluIHRoZSBhZGRpdGlvbmFsIGNvbHVtbnMgY29taW5nIGZyb20gdGhlIHNlY29uZCBkYXRhIGZyYW1lLgoKQW5pbWF0aW9ucyB0byBpbGx1c3RyYXRlIG90aGVyIHR5cGVzIG9mIGpvaW4gYXJlIGF2YWlsYWJsZSBhdCBbaHR0cHM6Ly9naXRodWIuY29tL2dhZGVuYnVpZS90aWR5LWFuaW1hdGVkLXZlcmJzXShodHRwczovL2dpdGh1Yi5jb20vZ2FkZW5idWllL3RpZHktYW5pbWF0ZWQtdmVyYnMpCgpgYGB7cn0KbGVmdF9qb2luKGJhbmRfbWVtYmVycywgYmFuZF9pbnN0cnVtZW50cykKYGBgCgpgcmlnaHRfam9pbmAgaXMgc2ltaWxhciBidXQgcmV0dXJucyBhbGwgcm93cyBmcm9tIHRoZSBzZWNvbmQgZGF0YSBmcmFtZSB0aGF0IGhhdmUgYSBtYXRjaCB3aXRoIHJvd3MgaW4gdGhlIGZpcnN0IGRhdGEgZnJhbWUgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBjb2x1bW4uCgpgYGB7cn0KcmlnaHRfam9pbihiYW5kX21lbWJlcnMsIGJhbmRfaW5zdHJ1bWVudHMpCmBgYAoKYGlubmVyX2pvaW5gIG9ubHkgcmV0dXJucyB0aG9zZSByb3dzIHdoZXJlIG1hdGNoZXMgY291bGQgYmUgbWFkZQoKYGBge3J9CmlubmVyX2pvaW4oYmFuZF9tZW1iZXJzLCBiYW5kX2luc3RydW1lbnRzKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKCgojIyMgRXhlcmNpc2UgKG9wZW4tZW5kZWQpCgotIFRoZSBmaWxlIGBtZWRhbF90YWJsZS5jc3ZgIGluIHRoZSBgcmF3X2RhdGEvYCBwcm9qZWN0IHN1Yi1kaXJlY3RvcnkgY29udGFpbnMgZGF0YSBhYm91dCBob3cgbWFueSBtZWRhbHMgaG93IGJlZW4gd29uIGJ5IHZhcmlvdXMgY291bnRyaWVzIGF0IHRoZSBCZWlqaW5nIHN1bW1lciBvbHltcGljcyBvZiAyMDA4LgotIFJlYWQgdGhpcyBjc3YgZmlsZSBpbnRvIFIgYW5kIGpvaW4gd2l0aCB0aGUgYGdhcG1pbmRlcmAgZGF0YSBmcm9tIDIwMDcKLSBXaGF0IGludGVyZXN0aW5nIHN1bW1hcmllcyAvIHBsb3RzIGNhbiB5b3UgbWFrZSBmcm9tIHRoZSBkYXRhPyBGb3IgZXhhbXBsZS4uLgogICsgd2hhdCBjb3VudHJpZXMgaGF2ZSB0aGUgZ3JlYXRlc3QgcHJvcG9ydGlvbiBvZiBnb2xkIG1lZGFscyAoaWdub3JlIGNvdW50cmllcyB3aXRoIHRvbyBmZXcgbWVkYWxzKQogICsgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgbWVkYWxzIHdvbiBwZXIgbWlsbGlvbiBwZW9wbGUgYW5kIHJlLWFycmFuZ2UgYnkgdGhpcyBuZXcgbWVhc3VyZS4gV2hhdCBjb3VudHJpZXMgcGVyZm9ybSBiZXN0PwogICsgaG93IHNpbWlsYXIgaXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0b3RhbCBtZWRhbHMgYmV0d2VlbiBjb250aW5lbnRzPwogICsgZG8gY291bnRyaWVzIHdpdGggYSBsYXJnZXIgcG9wdWxhdGlvbiB0ZW5kIHRvIHdpbiBtb3JlIG1lZGFscz8KICArIGRvIGNvdW50cmllcyB3aXRoIGxhcmdlciBHRFAgdGVuZCB0byB3aW4gbW9yZSBtZWRhbHM/CiAgKyBhcmUgdGhlc2UgdHJlbmRzIGNvbnNpc3RlbnQgYW1vbmcgZGlmZmVyZW50IGNvbnRpbmVudHM/CiAgCioqKioqKgoqKioqKioKKioqKioqCg==