In this lab activity, we’ll discuss Data Wrangling/Transformation via
the dplyr package. We’ll explore ways to choose subsets of
data, aggregate data to create summaries, make new variables, and sort
your data frames. It is recommended you also explore the RStudio
Cheatsheet on Data
Transformation as we discuss this content.
Back to gapminder
Here is a look at the gapminder data frame in the
gapminder package.
library(gapminder)
rmarkdown::paged_table(gapminder)
# Has been set as the default way to print data frames
# via df_print: paged in the YAML at the top
Say we wanted mean life expectancy across all years for Asia
# Base R
asia <- gapminder[gapminder$continent == "Asia", ]
mean(asia$lifeExp)
[1] 60.0649
library(dplyr)
gapminder %>%
filter(continent == "Asia") %>%
summarize(mean_exp = mean(lifeExp))
filter()
- Select a subset of the rows of a data frame.
- The arguments are the “filters” that you’d like to apply.
library(gapminder)
library(dplyr)
gap_2007 <- gapminder %>% filter(year == 2007)
gap_2007
- Use
== to compare a variable to a value
Logical operators
- Use
| to check for any in multiple filters being
true:
gapminder %>%
filter(year == 2002 | continent == "Europe")
- Use
& or , to check for all of
multiple filters being true:
gapminder %>%
filter(year == 2002, continent == "Europe")
- Use
%in% to check for any being true (shortcut to using
| repeatedly with ==)
gapminder %>%
filter(country %in% c("Argentina", "Belgium", "Mexico"),
year %in% c(1987, 1992))
summarize()
- Any numerical summary that you want to apply to a column of a data
frame is specified within
summarize().
max_exp_1997 <- gapminder %>%
filter(year == 1997) %>%
summarize(max_exp = max(lifeExp))
max_exp_1997
Combining summarize() with group_by()
When you’d like to determine a numerical summary for all levels of a
different categorical variable
max_exp_1997_by_cont <- gapminder %>%
filter(year == 1997) %>%
group_by(continent) %>%
summarize(max_exp = max(lifeExp))
max_exp_1997_by_cont
Without the %>%
It’s hard to appreciate the %>% without seeing what
the code would look like without it:
max_exp_1997_by_cont <-
summarize(
group_by(
filter(
gapminder,
year == 1997),
continent),
max_exp = max(lifeExp))
max_exp_1997_by_cont
mutate()
- Allows you to
- create a new variable based on other variables OR
- change the contents of an existing variable
- create a new variable based on other variables
gap_w_gdp <- gapminder %>% mutate(gdp = pop * gdpPercap)
gap_w_gdp
mutate()
- change the contents of an existing variable
gap_weird <- gapminder %>% mutate(pop = pop + 1000)
gap_weird
arrange()
- Reorders the rows in a data frame based on the values of one or more
variables
gapminder %>%
arrange(year, country)
- Can also put into descending order
gapminder %>%
filter(year > 2000) %>%
arrange(desc(lifeExp))
Other useful dplyr verbs
select
top_n
sample_n
slice
glimpse
rename
Your Task
Determine which African country had the highest GDP per capita in
1982 using the gapminder data in the gapminder
package.
library(gapminder)
library(dplyr)
library(tidyr)
highest_gdp <- gapminder %>%
filter(continent == "Africa") %>% #filters for Africa as the continent
arrange(desc(gdpPercap)) %>% #arranges the filtered data in descending order
slice_head(n = 5) #give the top 5
print(highest_gdp)
# A tibble: 5 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Libya Africa 1977 57.4 2721783 21951.
2 Gabon Africa 1977 52.8 706367 21746.
3 Libya Africa 1972 52.8 2183877 21011.
4 Libya Africa 1967 50.2 1759224 18773.
5 Libya Africa 1982 62.2 3344074 17364.
#The country with the highest GDP per capita in Africa is Libya.
Challenge
For both of these problems below, use the bechdel data
frame in the fivethirtyeight package:
- Use the
count function in the dplyr
package to determine how many movies in 2013 fell into each of the
different categories for clean_test
- Determine the percentage of movies that received the value of
"ok" across all years
library(fivethirtyeight)
library(dplyr)
library(tidyr)
movies_count <- fivethirtyeight::bechdel %>% #pulls the bechdel data frame from
#the fivethirtyeight package and names a new variable movies_count with
#the following parameters
filter(year == '2013') %>% #filters for the year 2013
count(clean_test) #from 2013 data, counts the number of values in clean_test
print(movies_count) #prints that count
# A tibble: 5 × 2
clean_test n
<ord> <int>
1 nowomen 6
2 notalk 23
3 men 15
4 dubious 9
5 ok 46
library(fivethirtyeight)
library(dplyr)
library(tidyr)
ok_movies <- fivethirtyeight::bechdel %>%
filter(clean_test == 'ok') #filters data that received 'ok' under clean_test
#creates a new variable called ok_movies
count(ok_movies) #does a count of values in ok_movies
ok_percentage <- (count(ok_movies) / 1794) * 100 #creates a new variable that
#calculates the percentage
print(ok_percentage) #prints percentage
n
1 44.76031
Your Task
Determine the top five movies in terms of domestic return on
investment for 2013 scaled data using the bechdel data
frame in the fivethirtyeight package.
fivethirtyeight::bechdel %>%
filter(domgross_2013 > 0) %>% #filters for the domgross_2013 column
arrange(desc(domgross_2013)) %>% #arranges that domgross_2013 column
#descending order
slice_head(n = 5) #pulls out the top 5 dom grossing movies
LS0tDQp0aXRsZTogJ0RhdGEgV3JhbmdsaW5nIExhYjI6IEludHJvIHRvIGBkcGx5cmAnDQphdXRob3I6ICJKYWNpbmRhIEV2YW5zIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSkNCmZpbHRlciA8LSBkcGx5cjo6ZmlsdGVyDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTkuNSwgZmlnLmhlaWdodD00LjUsIGNvbW1lbnQ9TkEsIHJvd3MucHJpbnQ9MTYsIG91dC53aWR0aCA9ICJcXHRleHR3aWR0aCIpDQp0aGVtZV9zZXQodGhlbWVfZ3JheShiYXNlX3NpemUgPSAyMCkpDQpgYGANCg0KSW4gdGhpcyBsYWIgYWN0aXZpdHksIHdlJ2xsIGRpc2N1c3MgRGF0YSBXcmFuZ2xpbmcvVHJhbnNmb3JtYXRpb24gdmlhIHRoZSBgZHBseXJgIHBhY2thZ2UuIFdlJ2xsIGV4cGxvcmUgd2F5cyB0byBjaG9vc2Ugc3Vic2V0cyBvZiBkYXRhLCBhZ2dyZWdhdGUgZGF0YSB0byBjcmVhdGUgc3VtbWFyaWVzLCBtYWtlIG5ldyB2YXJpYWJsZXMsIGFuZCBzb3J0IHlvdXIgZGF0YSBmcmFtZXMuIEl0IGlzIHJlY29tbWVuZGVkIHlvdSBhbHNvIGV4cGxvcmUgdGhlIFJTdHVkaW8gQ2hlYXRzaGVldCBvbiBbRGF0YSBUcmFuc2Zvcm1hdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9zb3VyY2UvcGRmcy9kYXRhLXRyYW5zZm9ybWF0aW9uLWNoZWF0c2hlZXQucGRmKSBhcyB3ZSBkaXNjdXNzIHRoaXMgY29udGVudC4NCg0KIyMjIEJhY2sgdG8gYGdhcG1pbmRlcmANCg0KSGVyZSBpcyBhIGxvb2sgYXQgdGhlIGBnYXBtaW5kZXJgIGRhdGEgZnJhbWUgaW4gdGhlIGBnYXBtaW5kZXJgIHBhY2thZ2UuDQoNCmBgYHtyfQ0KbGlicmFyeShnYXBtaW5kZXIpDQpybWFya2Rvd246OnBhZ2VkX3RhYmxlKGdhcG1pbmRlcikNCiMgSGFzIGJlZW4gc2V0IGFzIHRoZSBkZWZhdWx0IHdheSB0byBwcmludCBkYXRhIGZyYW1lcw0KIyB2aWEgZGZfcHJpbnQ6IHBhZ2VkIGluIHRoZSBZQU1MIGF0IHRoZSB0b3ANCmBgYA0KDQpTYXkgd2Ugd2FudGVkIG1lYW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyBhbGwgeWVhcnMgZm9yIEFzaWENCg0KYGBge3J9DQojIEJhc2UgUg0KYXNpYSA8LSBnYXBtaW5kZXJbZ2FwbWluZGVyJGNvbnRpbmVudCA9PSAiQXNpYSIsIF0NCm1lYW4oYXNpYSRsaWZlRXhwKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmdhcG1pbmRlciAlPiUgDQogIGZpbHRlcihjb250aW5lbnQgPT0gIkFzaWEiKSAlPiUNCiAgc3VtbWFyaXplKG1lYW5fZXhwID0gbWVhbihsaWZlRXhwKSkNCmBgYA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBgZmlsdGVyKClgDQoNCi0gICBTZWxlY3QgYSBzdWJzZXQgb2YgdGhlIHJvd3Mgb2YgYSBkYXRhIGZyYW1lLg0KLSAgIFRoZSBhcmd1bWVudHMgYXJlIHRoZSAiZmlsdGVycyIgdGhhdCB5b3UnZCBsaWtlIHRvIGFwcGx5Lg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2FwbWluZGVyKQ0KbGlicmFyeShkcGx5cikNCmdhcF8yMDA3IDwtIGdhcG1pbmRlciAlPiUgZmlsdGVyKHllYXIgPT0gMjAwNykNCmdhcF8yMDA3DQpgYGANCg0KLSAgIFVzZSBgPT1gIHRvIGNvbXBhcmUgYSB2YXJpYWJsZSB0byBhIHZhbHVlDQoNCiMjIExvZ2ljYWwgb3BlcmF0b3JzDQoNCi0gICBVc2UgYHxgIHRvIGNoZWNrIGZvciBhbnkgaW4gbXVsdGlwbGUgZmlsdGVycyBiZWluZyB0cnVlOg0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgDQogIGZpbHRlcih5ZWFyID09IDIwMDIgfCBjb250aW5lbnQgPT0gIkV1cm9wZSIpDQpgYGANCg0KLSAgIFVzZSBgJmAgb3IgYCxgIHRvIGNoZWNrIGZvciBhbGwgb2YgbXVsdGlwbGUgZmlsdGVycyBiZWluZyB0cnVlOg0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgDQogIGZpbHRlcih5ZWFyID09IDIwMDIsIGNvbnRpbmVudCA9PSAiRXVyb3BlIikNCmBgYA0KDQotICAgVXNlIGAlaW4lYCB0byBjaGVjayBmb3IgYW55IGJlaW5nIHRydWUgKHNob3J0Y3V0IHRvIHVzaW5nIGB8YCByZXBlYXRlZGx5IHdpdGggYD09YCkNCg0KYGBge3J9DQpnYXBtaW5kZXIgJT4lIA0KICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIkFyZ2VudGluYSIsICJCZWxnaXVtIiwgIk1leGljbyIpLA0KICAgICAgICAgeWVhciAlaW4lIGMoMTk4NywgMTk5MikpDQpgYGANCg0KIyMgYHN1bW1hcml6ZSgpYA0KDQotICAgQW55IG51bWVyaWNhbCBzdW1tYXJ5IHRoYXQgeW91IHdhbnQgdG8gYXBwbHkgdG8gYSBjb2x1bW4gb2YgYSBkYXRhIGZyYW1lIGlzIHNwZWNpZmllZCB3aXRoaW4gYHN1bW1hcml6ZSgpYC4NCg0KYGBge3J9DQptYXhfZXhwXzE5OTcgPC0gZ2FwbWluZGVyICU+JSANCiAgZmlsdGVyKHllYXIgPT0gMTk5NykgJT4lIA0KICBzdW1tYXJpemUobWF4X2V4cCA9IG1heChsaWZlRXhwKSkNCm1heF9leHBfMTk5Nw0KYGBgDQoNCiMjIyBDb21iaW5pbmcgYHN1bW1hcml6ZSgpYCB3aXRoIGBncm91cF9ieSgpYA0KDQpXaGVuIHlvdSdkIGxpa2UgdG8gZGV0ZXJtaW5lIGEgbnVtZXJpY2FsIHN1bW1hcnkgZm9yIGFsbCBsZXZlbHMgb2YgYSBkaWZmZXJlbnQgY2F0ZWdvcmljYWwgdmFyaWFibGUNCg0KYGBge3J9DQptYXhfZXhwXzE5OTdfYnlfY29udCA8LSBnYXBtaW5kZXIgJT4lIA0KICBmaWx0ZXIoeWVhciA9PSAxOTk3KSAlPiUgDQogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lDQogIHN1bW1hcml6ZShtYXhfZXhwID0gbWF4KGxpZmVFeHApKQ0KbWF4X2V4cF8xOTk3X2J5X2NvbnQNCmBgYA0KDQojIyMgV2l0aG91dCB0aGUgYCU+JWANCg0KSXQncyBoYXJkIHRvIGFwcHJlY2lhdGUgdGhlIGAlPiVgIHdpdGhvdXQgc2VlaW5nIHdoYXQgdGhlIGNvZGUgd291bGQgbG9vayBsaWtlIHdpdGhvdXQgaXQ6DQoNCmBgYHtyfQ0KbWF4X2V4cF8xOTk3X2J5X2NvbnQgPC0gDQogIHN1bW1hcml6ZSgNCiAgICBncm91cF9ieSgNCiAgICAgIGZpbHRlcigNCiAgICAgICAgZ2FwbWluZGVyLCANCiAgICAgICAgICB5ZWFyID09IDE5OTcpLCANCiAgICAgIGNvbnRpbmVudCksDQogICAgbWF4X2V4cCA9IG1heChsaWZlRXhwKSkNCm1heF9leHBfMTk5N19ieV9jb250DQpgYGANCg0KIyMgYG11dGF0ZSgpYA0KDQotICAgQWxsb3dzIHlvdSB0bw0KICAgIDEuICBjcmVhdGUgYSBuZXcgdmFyaWFibGUgYmFzZWQgb24gb3RoZXIgdmFyaWFibGVzIE9SDQogICAgMi4gIGNoYW5nZSB0aGUgY29udGVudHMgb2YgYW4gZXhpc3RpbmcgdmFyaWFibGUNCg0KMS4gIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBiYXNlZCBvbiBvdGhlciB2YXJpYWJsZXMNCg0KYGBge3J9DQpnYXBfd19nZHAgPC0gZ2FwbWluZGVyICU+JSBtdXRhdGUoZ2RwID0gcG9wICogZ2RwUGVyY2FwKQ0KZ2FwX3dfZ2RwDQpgYGANCg0KIyMgYG11dGF0ZSgpYA0KDQozLiAgY2hhbmdlIHRoZSBjb250ZW50cyBvZiBhbiBleGlzdGluZyB2YXJpYWJsZQ0KDQpgYGB7cn0NCmdhcF93ZWlyZCA8LSBnYXBtaW5kZXIgJT4lIG11dGF0ZShwb3AgPSBwb3AgKyAxMDAwKQ0KZ2FwX3dlaXJkDQpgYGANCg0KIyMgYGFycmFuZ2UoKWANCg0KLSAgIFJlb3JkZXJzIHRoZSByb3dzIGluIGEgZGF0YSBmcmFtZSBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIG9uZSBvciBtb3JlIHZhcmlhYmxlcw0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUNCiAgYXJyYW5nZSh5ZWFyLCBjb3VudHJ5KQ0KYGBgDQoNCi0gICBDYW4gYWxzbyBwdXQgaW50byBkZXNjZW5kaW5nIG9yZGVyDQoNCmBgYHtyIGRlc2N9DQpnYXBtaW5kZXIgJT4lDQogIGZpbHRlcih5ZWFyID4gMjAwMCkgJT4lDQogIGFycmFuZ2UoZGVzYyhsaWZlRXhwKSkNCmBgYA0KDQojIyBPdGhlciB1c2VmdWwgYGRwbHlyYCB2ZXJicw0KDQotICAgYHNlbGVjdGANCi0gICBgdG9wX25gDQotICAgYHNhbXBsZV9uYA0KLSAgIGBzbGljZWANCi0gICBgZ2xpbXBzZWANCi0gICBgcmVuYW1lYA0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBZb3VyIFRhc2sNCg0KRGV0ZXJtaW5lIHdoaWNoIEFmcmljYW4gY291bnRyeSBoYWQgdGhlIGhpZ2hlc3QgR0RQIHBlciBjYXBpdGEgaW4gMTk4MiB1c2luZyB0aGUgYGdhcG1pbmRlcmAgZGF0YSBpbiB0aGUgYGdhcG1pbmRlcmAgcGFja2FnZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdhcG1pbmRlcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KDQpoaWdoZXN0X2dkcCA8LSBnYXBtaW5kZXIgJT4lDQogICBmaWx0ZXIoY29udGluZW50ID09ICJBZnJpY2EiKSAlPiUgI2ZpbHRlcnMgZm9yIEFmcmljYSBhcyB0aGUgY29udGluZW50DQogIGFycmFuZ2UoZGVzYyhnZHBQZXJjYXApKSAlPiUgI2FycmFuZ2VzIHRoZSBmaWx0ZXJlZCBkYXRhIGluIGRlc2NlbmRpbmcgb3JkZXINCiAgc2xpY2VfaGVhZChuID0gNSkgI2dpdmUgdGhlIHRvcCA1DQoNCnByaW50KGhpZ2hlc3RfZ2RwKQ0KYGBgDQojVGhlIGNvdW50cnkgd2l0aCB0aGUgaGlnaGVzdCBHRFAgcGVyIGNhcGl0YSBpbiBBZnJpY2EgaXMgTGlieWEuIA0KDQoNCioqQ2hhbGxlbmdlKioNCg0KRm9yIGJvdGggb2YgdGhlc2UgcHJvYmxlbXMgYmVsb3csIHVzZSB0aGUgYGJlY2hkZWxgIGRhdGEgZnJhbWUgaW4gdGhlIGBmaXZldGhpcnR5ZWlnaHRgIHBhY2thZ2U6DQoNCi0gICBVc2UgdGhlIGBjb3VudGAgZnVuY3Rpb24gaW4gdGhlIGBkcGx5cmAgcGFja2FnZSB0byBkZXRlcm1pbmUgaG93IG1hbnkgbW92aWVzIGluIDIwMTMgZmVsbCBpbnRvIGVhY2ggb2YgdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzIGZvciBgY2xlYW5fdGVzdGANCi0gICBEZXRlcm1pbmUgdGhlIHBlcmNlbnRhZ2Ugb2YgbW92aWVzIHRoYXQgcmVjZWl2ZWQgdGhlIHZhbHVlIG9mIGAib2siYCBhY3Jvc3MgYWxsIHllYXJzDQoNCg0KDQpgYGB7cn0NCmxpYnJhcnkoZml2ZXRoaXJ0eWVpZ2h0KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQoNCm1vdmllc19jb3VudCA8LSBmaXZldGhpcnR5ZWlnaHQ6OmJlY2hkZWwgJT4lICNwdWxscyB0aGUgYmVjaGRlbCBkYXRhIGZyYW1lIGZyb20NCiAgI3RoZSBmaXZldGhpcnR5ZWlnaHQgcGFja2FnZSBhbmQgbmFtZXMgYSBuZXcgdmFyaWFibGUgbW92aWVzX2NvdW50IHdpdGggDQogICN0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMNCiAgIGZpbHRlcih5ZWFyID09ICcyMDEzJykgJT4lICNmaWx0ZXJzIGZvciB0aGUgeWVhciAyMDEzDQogIGNvdW50KGNsZWFuX3Rlc3QpICNmcm9tIDIwMTMgZGF0YSwgY291bnRzIHRoZSBudW1iZXIgb2YgdmFsdWVzIGluIGNsZWFuX3Rlc3QNCg0KcHJpbnQobW92aWVzX2NvdW50KSAjcHJpbnRzIHRoYXQgY291bnQNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShmaXZldGhpcnR5ZWlnaHQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCg0Kb2tfbW92aWVzIDwtIGZpdmV0aGlydHllaWdodDo6YmVjaGRlbCAlPiUgDQogICBmaWx0ZXIoY2xlYW5fdGVzdCA9PSAnb2snKSAjZmlsdGVycyBkYXRhIHRoYXQgcmVjZWl2ZWQgJ29rJyB1bmRlciBjbGVhbl90ZXN0DQogICNjcmVhdGVzIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBva19tb3ZpZXMNCmNvdW50KG9rX21vdmllcykgI2RvZXMgYSBjb3VudCBvZiB2YWx1ZXMgaW4gb2tfbW92aWVzDQoNCg0Kb2tfcGVyY2VudGFnZSA8LSAoY291bnQob2tfbW92aWVzKSAvIDE3OTQpICogMTAwICNjcmVhdGVzIGEgbmV3IHZhcmlhYmxlIHRoYXQgDQogICNjYWxjdWxhdGVzIHRoZSBwZXJjZW50YWdlIA0KDQoNCnByaW50KG9rX3BlcmNlbnRhZ2UpICNwcmludHMgcGVyY2VudGFnZQ0KYGBgDQoNCg0KIyMgWW91ciBUYXNrDQoNCkRldGVybWluZSB0aGUgdG9wIGZpdmUgbW92aWVzIGluIHRlcm1zIG9mIGRvbWVzdGljIHJldHVybiBvbiBpbnZlc3RtZW50IGZvciAyMDEzIHNjYWxlZCBkYXRhIHVzaW5nIHRoZSBgYmVjaGRlbGAgZGF0YSBmcmFtZSBpbiB0aGUgYGZpdmV0aGlydHllaWdodGAgcGFja2FnZS4NCg0KYGBge3J9DQpmaXZldGhpcnR5ZWlnaHQ6OmJlY2hkZWwgJT4lDQogIGZpbHRlcihkb21ncm9zc18yMDEzID4gMCkgJT4lICNmaWx0ZXJzIGZvciB0aGUgZG9tZ3Jvc3NfMjAxMyBjb2x1bW4gDQogIGFycmFuZ2UoZGVzYyhkb21ncm9zc18yMDEzKSkgJT4lICNhcnJhbmdlcyB0aGF0IGRvbWdyb3NzXzIwMTMgY29sdW1uIA0KICAjZGVzY2VuZGluZyBvcmRlcg0KICBzbGljZV9oZWFkKG4gPSA1KSAjcHVsbHMgb3V0IHRoZSB0b3AgNSBkb20gZ3Jvc3NpbmcgbW92aWVzDQpgYGANCg==