Data Science Module

Topic 2B: Data Visualisation II


Example R code solutions for the Data Science Computer Lab 2, which uses data from Horst, Hill, and Gorman (2020), and the plotly (Sievert 2020) R package, are presented below.


1 Palmer Penguins Data Set

# Install package
install.packages("palmerpenguins")
# This code loads the `palmerpenguins` package into your current R working environment.
library(palmerpenguins)
## Warning: package 'palmerpenguins' was built under R version 4.2.2
# This code summarises the data in the `palmerpenguins` package.
summary(penguins)
##       species          island    bill_length_mm  bill_depth_mm  
##  Adelie   :152   Biscoe   :168   Min.   :32.10   Min.   :13.10  
##  Chinstrap: 68   Dream    :124   1st Qu.:39.23   1st Qu.:15.60  
##  Gentoo   :124   Torgersen: 52   Median :44.45   Median :17.30  
##                                  Mean   :43.92   Mean   :17.15  
##                                  3rd Qu.:48.50   3rd Qu.:18.70  
##                                  Max.   :59.60   Max.   :21.50  
##                                  NA's   :2       NA's   :2      
##  flipper_length_mm  body_mass_g       sex           year     
##  Min.   :172.0     Min.   :2700   female:165   Min.   :2007  
##  1st Qu.:190.0     1st Qu.:3550   male  :168   1st Qu.:2007  
##  Median :197.0     Median :4050   NA's  : 11   Median :2008  
##  Mean   :200.9     Mean   :4202                Mean   :2008  
##  3rd Qu.:213.0     3rd Qu.:4750                3rd Qu.:2009  
##  Max.   :231.0     Max.   :6300                Max.   :2009  
##  NA's   :2         NA's   :2

2 Plotly Scatter Plots

2.1

# Install package
install.packages("plotly")
# Load package
library(plotly)

2.2

penguins_scatter <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm)
penguins_scatter

2.3

No answer required.

2.4

penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex)
penguins_scatter2

2.5

penguins_scatter_colours <- plot_ly(data = penguins, 
                                    x = ~body_mass_g, y = ~flipper_length_mm, 
                                    color = ~sex, colors = c("cyan", "orange"))
penguins_scatter_colours

2.6

penguins_scatter_colours <- plot_ly(data = penguins, 
                                    x = ~body_mass_g, y = ~flipper_length_mm, 
                                    color = ~sex, colors = "Set2")
penguins_scatter_colours

2.7

penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1",
                             type = "scatter", mode = "markers")
penguins_scatter2
penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1",
                             type = "scatter", mode = "lines")
penguins_scatter2

Note that here, R is drawing a line between the individual data points - clearly we don’t want this!

2.8

penguins_scatter3 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1", symbol = ~species, 
                             type = "scatter", mode = "markers")
penguins_scatter3

2.9

Here we have used the symbols cross, diamond and star.

penguins_scatter3 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1", symbol = ~species,
                             symbols = c("cross", "diamond", "star"),
                             type = "scatter", mode = "markers")
penguins_scatter3

2.10

penguins_scatter3 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1", symbol = ~species,
                             symbols = c("cross", "diamond", "star"),
                             type = "scatter", mode = "markers",
                             marker = list(size = 8))
penguins_scatter3

3 Creating your own Plotly Scatter Plot

3.1

penguins_scatter_new <- plot_ly(data = penguins, x = ~body_mass_g, y = ~bill_length_mm,
                                type = "scatter", mode = "markers")
penguins_scatter_new

3.2

penguins_scatter_new2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~bill_length_mm,
                                color = ~island,
                                type = "scatter", mode = "markers")
penguins_scatter_new2

3.3

penguins_scatter_new3 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~bill_length_mm,
                                color = ~island, symbol = ~species,
                                type = "scatter", mode = "markers")
penguins_scatter_new3

3.4

penguins_scatter_new4 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~bill_length_mm,
                                color = ~island, symbol = ~species, 
                                symbols = c("cross", "diamond", "star"),
                                type = "scatter", mode = "markers",
                                marker = list(size=8))
penguins_scatter_new4

3.5

It does seem that penguins living on different islands have noticeably different body_mass_g and bill_length_mm measurements, but this is also due to the fact that some species of penguin only live on one of the three islands - e.g. Gentoo and Chinstrap penguins only live on Biscoe island and Dream island respectively, whereas the Adelie penguins live on all three islands.

However, we also note that the Adelie penguins living on Torgersen island are much smaller overall than Adelie penguins living on other islands.

4 Mixed Subplots

Recall from our [Week 1 Data Science Computer Lab] how we created some histograms for our palmerpenguins data set. Some of the code used for that lab is reproduced below:

penguin_hist <- plot_ly(data = penguins, x = ~body_mass_g, 
                        color = ~island, type = "histogram", alpha = 0.6)

penguin_hist <- penguin_hist %>% layout(yaxis = list(title = 'count'), 
                                        barmode ="overlay")
penguin_hist

Suppose that we would like to present all our palmerpenguins data visualisations together. We can do this using the subplot function.

4.1

Take a look at the R code below:

penguin_combined_plots <- subplot(penguins_scatter3, penguin_hist, 
                                  nrows = 2, margin = 0.05) 
penguin_combined_plots <- penguin_combined_plots %>% 
                          layout(title = "Palmer Penguin Data",
                                 xaxis = list(title = 'body_mass_g'), 
                                 yaxis = list(title = "flipper_length_mm"),
                                 xaxis2 = list(title = 'body_mass_g'), 
                                 yaxis2 = list(title = "count"))
penguin_combined_plots

4.2

penguin_combined_plots_new <- subplot(penguins_scatter_new4, penguin_hist, 
                                  nrows = 2, margin = 0.05) 
penguin_combined_plots_new <- penguin_combined_plots_new %>% 
                          layout(title = "Palmer Penguin Species Data",
                                 xaxis = list(title = 'body_mass_g'), 
                                 yaxis = list(title = "bill_length_mm"),
                                 xaxis2 = list(title = 'body_mass_g'), 
                                 yaxis2 = list(title = "count"))
penguin_combined_plots_new

Note that this set of graphs is actually more informative than the previous subplots, since the colours for both graphs here align with the data being represented. It is always important to take such presentation possibilities into account when developing your subplots.


That’s everything covered.


References

Horst, Allison Marie, Alison Presmanes Hill, and Kristen B Gorman. 2020. Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data. https://doi.org/10.5281/zenodo.3960218.
Sievert, Carson. 2020. Interactive Web-Based Data Visualization with r, Plotly, and Shiny. Chapman; Hall/CRC. https://plotly-r.com.


These notes have been prepared by Rupert Kuveke. The copyright for the material in these notes resides with the author named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAyQiBTb2x1dGlvbnMiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgTW9kdWxlIHstfQ0KDQojIyMgVG9waWMgMkI6IERhdGEgVmlzdWFsaXNhdGlvbiBJSSB7LX0NCg0KPGJyPg0KDQpFeGFtcGxlIFIgY29kZSBzb2x1dGlvbnMgZm9yIHRoZSBbRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYiAyXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDJfUyksIHdoaWNoIHVzZXMgZGF0YSBmcm9tIEBwZW5ndWlucywgYW5kIHRoZSBgcGxvdGx5YCBbQHBsb3RseV0gUiBwYWNrYWdlLCBhcmUgcHJlc2VudGVkIGJlbG93Lg0KDQo8YnI+DQoNCiMgUGFsbWVyIFBlbmd1aW5zIERhdGEgU2V0IHsjcGVuZ3VpbnN9DQoNCmBgYHtyLCBpbmNsdWRlID0gRn0NCmluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpICMgSW5zdGFsbCBwYWNrYWdlDQppbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikgIyBJbnN0YWxsIHBhY2thZ2UNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KIyBJbnN0YWxsIHBhY2thZ2UNCmluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KIyBUaGlzIGNvZGUgbG9hZHMgdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBpbnRvIHlvdXIgY3VycmVudCBSIHdvcmtpbmcgZW52aXJvbm1lbnQuDQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQ0KIyBUaGlzIGNvZGUgc3VtbWFyaXNlcyB0aGUgZGF0YSBpbiB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlLg0Kc3VtbWFyeShwZW5ndWlucykNCmBgYA0KDQojIFBsb3RseSBTY2F0dGVyIFBsb3RzIHsjc2NhdHRlcn0gDQoNCiMjIA0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQojIEluc3RhbGwgcGFja2FnZQ0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGluY2x1ZGUgPSBGfQ0KIyBMb2FkIHBhY2thZ2UNCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQojIExvYWQgcGFja2FnZQ0KbGlicmFyeShwbG90bHkpDQpgYGANCg0KIyMgeyNzaW1wbGVzY2F0dGVyfQ0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlciA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSkNCnBlbmd1aW5zX3NjYXR0ZXINCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3NjYXR0ZXIgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpzdXBwcmVzc01lc3NhZ2VzKHBlbmd1aW5zX3NjYXR0ZXIpDQpgYGANCg0KIyMNCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIyB7I3NjYXR0ZXJjb2xvdXJ9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyMiA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCkNCnBlbmd1aW5zX3NjYXR0ZXIyDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcjIgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIGNvbG9yID0gfnNleCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpzdXBwcmVzc01lc3NhZ2VzKHBlbmd1aW5zX3NjYXR0ZXIyKQ0KYGBgDQoNCiMjIHsjc2NhdHRlcmNvbG91cnN9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyX2NvbG91cnMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9IGMoImN5YW4iLCAib3JhbmdlIikpDQpwZW5ndWluc19zY2F0dGVyX2NvbG91cnMNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCB3YXJuaW5nID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluc19zY2F0dGVyX2NvbG91cnMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9IGMoImN5YW4iLCAib3JhbmdlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9jb2xvdXJzDQpgYGANCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXJfY29sb3VycyA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDIiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9jb2xvdXJzDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcl9jb2xvdXJzIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9jb2xvdXJzDQpgYGANCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXIyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpwZW5ndWluc19zY2F0dGVyMg0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyMiA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gImxpbmVzIikNCnBlbmd1aW5zX3NjYXR0ZXIyDQpgYGANCg0KTm90ZSB0aGF0IGhlcmUsIFIgaXMgZHJhd2luZyBhIGxpbmUgYmV0d2VlbiB0aGUgaW5kaXZpZHVhbCBkYXRhIHBvaW50cyAtIGNsZWFybHkgd2UgZG9uJ3Qgd2FudCB0aGlzIQ0KDQojIyB7I3NjYXR0ZXJzeW1ib2x9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyMyA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLCBzeW1ib2wgPSB+c3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpwZW5ndWluc19zY2F0dGVyMw0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluc19zY2F0dGVyMyA8LSBwbG90X2x5KGRhdGEgPSByZW1vdmVfbWlzc2luZyhwZW5ndWlucyksIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgc3ltYm9sID0gfnNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KDQpwZW5ndWluc19zY2F0dGVyMw0KYGBgDQoNCiMjDQoNCkhlcmUgd2UgaGF2ZSB1c2VkIHRoZSBzeW1ib2xzIGBjcm9zc2AsIGBkaWFtb25kYCBhbmQgYHN0YXJgLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlcjMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgc3ltYm9sID0gfnNwZWNpZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bWJvbHMgPSBjKCJjcm9zcyIsICJkaWFtb25kIiwgInN0YXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCnBlbmd1aW5zX3NjYXR0ZXIzDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5zX3NjYXR0ZXIzIDwtIHBsb3RfbHkoZGF0YSA9IHJlbW92ZV9taXNzaW5nKHBlbmd1aW5zKSwgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLCBzeW1ib2wgPSB+c3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bWJvbHMgPSBjKCJjcm9zcyIsICJkaWFtb25kIiwgInN0YXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCg0KcGVuZ3VpbnNfc2NhdHRlcjMNCmBgYA0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlcjMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgc3ltYm9sID0gfnNwZWNpZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bWJvbHMgPSBjKCJjcm9zcyIsICJkaWFtb25kIiwgInN0YXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gOCkpDQpwZW5ndWluc19zY2F0dGVyMw0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluc19zY2F0dGVyMyA8LSBwbG90X2x5KGRhdGEgPSByZW1vdmVfbWlzc2luZyhwZW5ndWlucyksIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgc3ltYm9sID0gfnNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzeW1ib2xzID0gYygiY3Jvc3MiLCAiZGlhbW9uZCIsICJzdGFyIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDgpKQ0KDQpwZW5ndWluc19zY2F0dGVyMw0KYGBgDQoNCiMgQ3JlYXRpbmcgeW91ciBvd24gUGxvdGx5IFNjYXR0ZXIgUGxvdCB7I3NjYXR0ZXJwZXJzb25hbH0NCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5zX3NjYXR0ZXJfbmV3IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXcNCmBgYA0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXcyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5pc2xhbmQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpwZW5ndWluc19zY2F0dGVyX25ldzINCmBgYA0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXczIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5pc2xhbmQsIHN5bWJvbCA9IH5zcGVjaWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXczDQpgYGANCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5zX3NjYXR0ZXJfbmV3NCA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5iaWxsX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+aXNsYW5kLCBzeW1ib2wgPSB+c3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bWJvbHMgPSBjKCJjcm9zcyIsICJkaWFtb25kIiwgInN0YXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplPTgpKQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXc0DQpgYGANCg0KIyMNCg0KSXQgZG9lcyBzZWVtIHRoYXQgcGVuZ3VpbnMgbGl2aW5nIG9uIGRpZmZlcmVudCBpc2xhbmRzIGhhdmUgbm90aWNlYWJseSBkaWZmZXJlbnQgYGJvZHlfbWFzc19nYCBhbmQgYGJpbGxfbGVuZ3RoX21tYCBtZWFzdXJlbWVudHMsIGJ1dCB0aGlzIGlzIGFsc28gZHVlIHRvIHRoZSBmYWN0IHRoYXQgc29tZSBzcGVjaWVzIG9mIHBlbmd1aW4gb25seSBsaXZlIG9uIG9uZSBvZiB0aGUgdGhyZWUgaXNsYW5kcyAtIGUuZy4gR2VudG9vIGFuZCBDaGluc3RyYXAgcGVuZ3VpbnMgb25seSBsaXZlIG9uIEJpc2NvZSBpc2xhbmQgYW5kIERyZWFtIGlzbGFuZCByZXNwZWN0aXZlbHksIHdoZXJlYXMgdGhlIEFkZWxpZSBwZW5ndWlucyBsaXZlIG9uIGFsbCB0aHJlZSBpc2xhbmRzLg0KDQpIb3dldmVyLCB3ZSBhbHNvIG5vdGUgdGhhdCB0aGUgQWRlbGllIHBlbmd1aW5zIGxpdmluZyBvbiBUb3JnZXJzZW4gaXNsYW5kIGFyZSBtdWNoIHNtYWxsZXIgb3ZlcmFsbCB0aGFuIEFkZWxpZSBwZW5ndWlucyBsaXZpbmcgb24gb3RoZXIgaXNsYW5kcy4NCg0KIyBNaXhlZCBTdWJwbG90cyB7I3N1YnBsb3RzfQ0KDQpSZWNhbGwgZnJvbSBvdXIgW1dlZWsgMSBEYXRhIFNjaWVuY2UgQ29tcHV0ZXIgTGFiXSBob3cgd2UgY3JlYXRlZCBzb21lIGhpc3RvZ3JhbXMgZm9yIG91ciBgcGFsbWVycGVuZ3VpbnNgIGRhdGEgc2V0Lg0KU29tZSBvZiB0aGUgY29kZSB1c2VkIGZvciB0aGF0IGxhYiBpcyByZXByb2R1Y2VkIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3Vpbl9oaXN0IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfmlzbGFuZCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBhbHBoYSA9IDAuNikNCg0KcGVuZ3Vpbl9oaXN0IDwtIHBlbmd1aW5faGlzdCAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdjb3VudCcpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJtb2RlID0ib3ZlcmxheSIpDQpwZW5ndWluX2hpc3QNCmBgYA0KDQpTdXBwb3NlIHRoYXQgd2Ugd291bGQgbGlrZSB0byBwcmVzZW50IGFsbCBvdXIgYHBhbG1lcnBlbmd1aW5zYCBkYXRhIHZpc3VhbGlzYXRpb25zIHRvZ2V0aGVyLiBXZSBjYW4gZG8gdGhpcyB1c2luZyB0aGUgYHN1YnBsb3RgIGZ1bmN0aW9uLg0KDQojIyB7I3N1YnBsb3R3YWxrdGhyb3VnaH0NCg0KVGFrZSBhIGxvb2sgYXQgdGhlIFIgY29kZSBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyA8LSBzdWJwbG90KHBlbmd1aW5zX3NjYXR0ZXIzLCBwZW5ndWluX2hpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3dzID0gMiwgbWFyZ2luID0gMC4wNSkgDQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzIDwtIHBlbmd1aW5fY29tYmluZWRfcGxvdHMgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQodGl0bGUgPSAiUGFsbWVyIFBlbmd1aW4gRGF0YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiZmxpcHBlcl9sZW5ndGhfbW0iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzMiA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpczIgPSBsaXN0KHRpdGxlID0gImNvdW50IikpDQpgYGANCg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDEwLCA4KSwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzDQpgYGANCg0KDQojIyANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90c19uZXcgPC0gc3VicGxvdChwZW5ndWluc19zY2F0dGVyX25ldzQsIHBlbmd1aW5faGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLjA1KSANCnBlbmd1aW5fY29tYmluZWRfcGxvdHNfbmV3IDwtIHBlbmd1aW5fY29tYmluZWRfcGxvdHNfbmV3ICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIlBhbG1lciBQZW5ndWluIFNwZWNpZXMgRGF0YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiYmlsbF9sZW5ndGhfbW0iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzMiA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpczIgPSBsaXN0KHRpdGxlID0gImNvdW50IikpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuZGltID0gYygxMCwgOCksIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90c19uZXcNCmBgYA0KDQpOb3RlIHRoYXQgdGhpcyBzZXQgb2YgZ3JhcGhzIGlzIGFjdHVhbGx5IG1vcmUgaW5mb3JtYXRpdmUgdGhhbiB0aGUgcHJldmlvdXMgc3VicGxvdHMsIHNpbmNlIHRoZSBjb2xvdXJzIGZvciBib3RoIGdyYXBocyBoZXJlIGFsaWduIHdpdGggdGhlIGRhdGEgYmVpbmcgcmVwcmVzZW50ZWQuIEl0IGlzIGFsd2F5cyBpbXBvcnRhbnQgdG8gdGFrZSBzdWNoIHByZXNlbnRhdGlvbiBwb3NzaWJpbGl0aWVzIGludG8gYWNjb3VudCB3aGVuIGRldmVsb3BpbmcgeW91ciBzdWJwbG90cy4NCg0KPGJyPg0KDQojIyMjIFRoYXQncyBldmVyeXRoaW5nIGNvdmVyZWQuICMjIyMgey19DQoNCjxicj4NCg0KIyBSZWZlcmVuY2VzIHstICNSZWZ9DQo8ZGl2IGlkPSJyZWZzIj48L2Rpdj4NCg0KPGJyPg0KDQo8Zm9udCBjb2xvciA9ICJncmV5Ij4NClRoZXNlIG5vdGVzIGhhdmUgYmVlbiBwcmVwYXJlZCBieSBSdXBlcnQgS3V2ZWtlLiBUaGUgY29weXJpZ2h0IGZvciB0aGUgbWF0ZXJpYWwgaW4gdGhlc2Ugbm90ZXMgcmVzaWRlcyB3aXRoIHRoZSBhdXRob3IgbmFtZWQgYWJvdmUsIHdpdGggdGhlIERlcGFydG1lbnQgb2YgTWF0aGVtYXRpY2FsIGFuZCBQaHlzaWNhbCBTY2llbmNlcyBhbmQgd2l0aCBMYSBUcm9iZSBVbml2ZXJzaXR5LiBDb3B5cmlnaHQgaW4gdGhpcyB3b3JrIGlzIHZlc3RlZCBpbiBMYSBUcm9iZSBVbml2ZXJzaXR5IGluY2x1ZGluZyBhbGwgTGEgVHJvYmUgVW5pdmVyc2l0eSBicmFuZGluZyBhbmQgbmFtaW5nLiBVbmxlc3Mgb3RoZXJ3aXNlIHN0YXRlZCwgbWF0ZXJpYWwgd2l0aGluIHRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uIENvbW1lcmNpYWwtTm9uIERlcml2YXRpdmVzIExpY2Vuc2UgDQo8YSBocmVmID0gImh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1uZC80LjAvQ0MiIHRhcmdldD0iX2JsYW5rIj4gQlktTkMtTkQuIDwvYT4NCjwvZm9udD4=