版权声明:本套课程材料开源,使用和分享必须遵守「创作共用许可协议 CC BY-NC-SA」(来源引用-非商业用途使用-以相同方式共享)。


Chap11:多图组合

往期要点回顾

本章要点目录

## 本章所需R包
library(bruceR)
# library(ggplot2)  # 加载bruceR时已默认加载ggplot2
# library(cowplot)  # 加载bruceR时已默认加载cowplot

分面小图

【实践1】facet_wrap():拆解分面小图

## 数据准备
data = airquality
data$Month = as.factor(data$Month)
data$Temp.C = (data$Temp - 32) / 1.8  # 摄氏度 = (华氏度 - 32) / 1.8
str(data)
'data.frame':   153 obs. of  7 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : Factor w/ 5 levels "5","6","7","8",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Temp.C : num  19.4 22.2 23.3 16.7 13.3 ...
## 基础散点图
ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point()

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point()

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point() +
  facet_wrap(~ Month)

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point() +
  facet_wrap(~ Month, ncol=5)

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1)

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", se=FALSE, show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1)
`geom_smooth()` using formula = 'y ~ x'

## 修改因子标签
data$Months = factor(
  data$Month,
  levels = 5:9,
  labels = c("May", "June", "July", "August", "September")
)
ggplot(data, aes(x=Wind, y=Temp.C, color=Months)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", show.legend=FALSE) +
  facet_wrap(~ Months, nrow=1)
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C, color=Months)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", show.legend=FALSE) +
  facet_wrap(~ Months, nrow=1, scales="free")
`geom_smooth()` using formula = 'y ~ x'

【实践2】facet_grid():两个分类变量的交叉组合分面

  • facet_wrap():线性排列(卷饼🌯)
    • ~ 分组变量
  • facet_grid():两因素交叉组合排列(华夫饼🧇)
    • 行分组变量 ~ 列分组变量

## 两个分类变量的情况
## 数据变量计算复习(Chap 05 & 06)
data.bfi = as.data.table(psych::bfi)
data.bfi[, let(
  Gender = factor(gender, levels=1:2, labels=c("Male", "Female")),
  Age = as.numeric(age),
  Edu = as.factor(education),
  E = MEAN(data.bfi, "E", 1:5, rev=c(1,2), range=1:6),
  A = MEAN(data.bfi, "A", 1:5, rev=1, range=1:6),
  C = MEAN(data.bfi, "C", 1:5, rev=c(4,5), range=1:6),
  N = MEAN(data.bfi, "N", 1:5, rev=NULL, range=1:6),
  O = MEAN(data.bfi, "O", 1:5, rev=c(2,5), range=1:6)
)]
str(data.bfi)
Classes 'data.table' and 'data.frame':  2800 obs. of  36 variables:
 $ A1       : int  2 2 5 4 2 6 2 4 4 2 ...
 $ A2       : int  4 4 4 4 3 6 5 3 3 5 ...
 $ A3       : int  3 5 5 6 3 5 5 1 6 6 ...
 $ A4       : int  4 2 4 5 4 6 3 5 3 6 ...
 $ A5       : int  4 5 4 5 5 5 5 1 3 5 ...
 $ C1       : int  2 5 4 4 4 6 5 3 6 6 ...
 $ C2       : int  3 4 5 4 4 6 4 2 6 5 ...
 $ C3       : int  3 4 4 3 5 6 4 4 3 6 ...
 $ C4       : int  4 3 2 5 3 1 2 2 4 2 ...
 $ C5       : int  4 4 5 5 2 3 3 4 5 1 ...
 $ E1       : int  3 1 2 5 2 2 4 3 5 2 ...
 $ E2       : int  3 1 4 3 2 1 3 6 3 2 ...
 $ E3       : int  3 6 4 4 5 6 4 4 NA 4 ...
 $ E4       : int  4 4 4 4 4 5 5 2 4 5 ...
 $ E5       : int  4 3 5 4 5 6 5 1 3 5 ...
 $ N1       : int  3 3 4 2 2 3 1 6 5 5 ...
 $ N2       : int  4 3 5 5 3 5 2 3 5 5 ...
 $ N3       : int  2 3 4 2 4 2 2 2 2 5 ...
 $ N4       : int  2 5 2 4 4 2 1 6 3 2 ...
 $ N5       : int  3 5 3 1 3 3 1 4 3 4 ...
 $ O1       : int  3 4 4 3 3 4 5 3 6 5 ...
 $ O2       : int  6 2 2 3 3 3 2 2 6 1 ...
 $ O3       : int  3 4 5 4 4 5 5 4 6 5 ...
 $ O4       : int  4 3 5 3 3 6 6 5 6 5 ...
 $ O5       : int  3 3 2 5 3 1 1 3 1 2 ...
 $ gender   : int  1 2 2 2 1 2 1 1 1 2 ...
 $ education: int  NA NA NA NA NA 3 NA 2 1 NA ...
 $ age      : int  16 18 17 17 17 21 18 19 19 17 ...
 $ Gender   : Factor w/ 2 levels "Male","Female": 1 2 2 2 1 2 1 1 1 2 ...
 $ Age      : num  16 18 17 17 17 21 18 19 19 17 ...
 $ Edu      : Factor w/ 5 levels "1","2","3","4",..: NA NA NA NA NA 3 NA 2 1 NA ...
 $ E        : num  3.8 5 4.2 3.6 4.8 5.6 4.2 2.4 3.25 4.8 ...
 $ A        : num  4 4.2 3.8 4.6 4 4.6 4.6 2.6 3.6 5.4 ...
 $ C        : num  2.8 4 4 3 4.4 5.6 4.4 3.4 4 5.6 ...
 $ N        : num  2.8 3.8 3.6 2.8 3.2 3 1.4 4.2 3.6 4.2 ...
 $ O        : num  3 4 4.8 3.2 3.6 5 5.4 4.2 5 5.2 ...
 - attr(*, ".internal.selfref")=<pointer: 0x0000012b3d1cace0> 
p = ggplot(data.bfi[Age>=18 & Age<60 & !is.na(Edu)],
           aes(x=Age, y=E)) +
  geom_point(alpha=0.1) +
  geom_smooth(method="loess", color="firebrick", fill="orange") +
  labs(y="Extraversion")
p
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender)
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender + Edu)  # 并不理想
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender * Edu)  # 并不理想
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(~ Gender)  # . ~ 列分组变量(.点可以省略)
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Gender ~ .)  # 行分组变量 ~ .(.点不能省略)
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Gender ~ Edu)  # 行变量 ~ 列变量
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Edu ~ Gender)  # 行变量 ~ 列变量
`geom_smooth()` using formula = 'y ~ x'

多图组合

【实践3】cowplot::plot_grid():多图组合

p1 = ggplot(data, aes(x=Temp.C)) +
  geom_histogram(bins=10, color="black", fill="grey") +
  labs(x="Temperature", y="Frequency")

p2 = ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_boxplot() +
  labs(y="Temperature")

p12 = cowplot::plot_grid(p1, p2, nrow=1, labels="AUTO")
p12

p3 = ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point() +
  geom_smooth(method="lm") +
  labs(y="Temperature")

p4 = ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", se=FALSE, show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1) +
  labs(y="Temperature")

p1234 = cowplot::plot_grid(p1, p2, p3, p4, ncol=2, labels="AUTO")
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
p1234

图形文件保存

【实践4】ggsave():图形文件保存

ggsave():保存ggplot对象到文件

  • file:可以是.png.jpg.pdf等文件格式
    • PDF格式是矢量图,无限放大依然清晰
  • width:宽度(英寸inch)
  • height:高度(英寸inch)
  • dpi(dots per inch):每英寸像素点数
ggsave(p1234, file="Fig1.png", width=8, height=6, dpi=300)
# 分辨率:2400 * 1800(比例合适,分辨率高)

ggsave(p1234, file="Fig2.png", width=4, height=3, dpi=300)
# 分辨率:1200 * 900(比例不合适,虽然分辨率高)

ggsave(p1234, file="Fig3.png", width=8, height=6, dpi=100)
# 分辨率:800 * 600(比例合适,但分辨率低)

ggsave(p1234, file="Fig4.png", width=24, height=18, dpi=100)
# 分辨率:2400 * 1800(比例不合适,分辨率也低)

【作业10】多图组合练习

作业要求:

  • 使用cowplot::plot_grid()函数,把【作业9】完成的4张图拼合在一起,添加A~D标签,最后保存为合适尺寸的高清晰度图形文件(.png格式,清晰度dpi至少为300)
  • 使用R Markdown完成,对关键代码及结果要有注释说明

平台提交:

  • 关键代码截图及保存的PNG图形

返回课程主页

© 包寒吴霜

LS0tDQp0aXRsZTogIuOAilLor63oqIDjgIvnrKwxMeeroO+8muWkmuWbvue7hOWQiCINCnN1YnRpdGxlOiA8YSBocmVmPSJodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvIj7ov5Tlm57or77nqIvkuLvpobU8L2E+DQphdXRob3I6ICLmjojor77mlZnluIjvvJrljIXlr5LlkLTpnJwiDQojIGRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICBjc3M6IFJtZENTUy5jc3MNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8cCBzdHlsZT0iZm9udC1zaXplOiAxMnB4Ij7niYjmnYPlo7DmmI7vvJrmnKzlpZfor77nqIvmnZDmlpnlvIDmupDvvIzkvb/nlKjlkozliIbkuqvlv4XpobvpgbXlrojjgIzliJvkvZzlhbHnlKjorrjlj6/ljY/orq4gQ0MgQlktTkMtU0HjgI3vvIjmnaXmupDlvJXnlKgt6Z2e5ZWG5Lia55So6YCU5L2/55SoLeS7peebuOWQjOaWueW8j+WFseS6q++8ieOAgjxpbWcgc3JjPSJpbWcvQ0MtQlktTkMtU0EuanBnIiB3aWR0aD0iMTIwcHgiIGhlaWdodD0iNDJweCIgc3R5bGU9ImZsb2F0OiByaWdodCIgLz48L3A+DQpgYGANCg0KYGBge3IgQ29uZmlnLCBpbmNsdWRlPUZBTFNFfQ0Kb3B0aW9ucygNCiAga25pdHIua2FibGUuTkEgPSAiIiwNCiAgZGlnaXRzID0gNA0KKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiIsDQogIGZpZy53aWR0aCA9IDYsDQogIGZpZy5oZWlnaHQgPSA0LA0KICBkcGkgPSAzMDANCikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDaGFwMTHvvJrlpJrlm77nu4TlkIgNCg0KIyMjIyDlvoDmnJ/opoHngrnlm57pob4NCg0KLSBbQ2hhcDEwIFwjIOe7mOWItuWPmOmHj+WIhuW4g10oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAxMCMlRTclQkIlOTglRTUlODglQjYlRTUlOEYlOTglRTklODclOEYlRTUlODglODYlRTUlQjglODMpey51cml977yI55u05pa55Zu+77yJDQotIFtDaGFwMTAgXCMg57uY5Yi25Y+Y6YeP5aSn5bCPXShodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvQ2hhcDEwIyVFNyVCQiU5OCVFNSU4OCVCNiVFNSU4RiU5OCVFOSU4NyU4RiVFNSVBNCVBNyVFNSVCMCU4Ril7LnVyaX3vvIjmn7HlvaLlm77vvIkNCi0gW0NoYXAxMCBcIyDnu5jliLblj5jph4/lhbPns7tdKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS9DaGFwMTAjJUU3JUJCJTk4JUU1JTg4JUI2JUU1JThGJTk4JUU5JTg3JThGJUU1JTg1JUIzJUU3JUIzJUJCKXsudXJpfe+8iOaVo+eCueWbvu+8iQ0KLSBbQ2hhcDEwIFwjIOe7mOWItuWPmOmHj+i2i+WKv10oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAxMCMlRTclQkIlOTglRTUlODglQjYlRTUlOEYlOTglRTklODclOEYlRTglQjYlOEIlRTUlOEElQkYpey51cml977yI5oqY57q/5Zu+77yJDQoNCiMjIyMg5pys56ug6KaB54K555uu5b2VDQoNCi0gW+OAkOWunui3tTHjgJFmYWNldF93cmFwKCnvvJrmi4bop6PliIbpnaLlsI/lm75dKCPlrp7ot7UxZmFjZXRfd3JhcOaLhuino+WIhumdouWwj+WbvikNCi0gW+OAkOWunui3tTLjgJFmYWNldF9ncmlkKCnvvJrkuKTkuKrliIbnsbvlj5jph4/nmoTkuqTlj4nnu4TlkIjliIbpnaJdKCPlrp7ot7UyZmFjZXRfZ3JpZOS4pOS4quWIhuexu+WPmOmHj+eahOS6pOWPiee7hOWQiOWIhumdoikNCi0gW+OAkOWunui3tTPjgJFjb3dwbG90OjpwbG90X2dyaWQoKe+8muWkmuWbvue7hOWQiF0oI+Wunui3tTNjb3dwbG90cGxvdF9ncmlk5aSa5Zu+57uE5ZCIKe+8iOmHjeeCue+8iQ0KLSBb44CQ5a6e6Le1NOOAkWdnc2F2ZSgp77ya5Zu+5b2i5paH5Lu25L+d5a2YXSgj5a6e6Le1NGdnc2F2ZeWbvuW9ouaWh+S7tuS/neWtmCnvvIjph43ngrnvvIkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyDmnKznq6DmiYDpnIBS5YyFDQpsaWJyYXJ5KGJydWNlUikNCiMgbGlicmFyeShnZ3Bsb3QyKSAgIyDliqDovb1icnVjZVLml7blt7Lpu5jorqTliqDovb1nZ3Bsb3QyDQojIGxpYnJhcnkoY293cGxvdCkgICMg5Yqg6L29YnJ1Y2VS5pe25bey6buY6K6k5Yqg6L29Y293cGxvdA0KYGBgDQoNCiMg5YiG6Z2i5bCP5Zu+DQoNCiMjIyMg44CQ5a6e6Le1MeOAkWZhY2V0X3dyYXAoKe+8muaLhuino+WIhumdouWwj+WbviB7I+Wunui3tTFmYWNldF93cmFw5ouG6Kej5YiG6Z2i5bCP5Zu+fQ0KDQpgYGB7cn0NCiMjIOaVsOaNruWHhuWkhw0KZGF0YSA9IGFpcnF1YWxpdHkNCmRhdGEkTW9udGggPSBhcy5mYWN0b3IoZGF0YSRNb250aCkNCmRhdGEkVGVtcC5DID0gKGRhdGEkVGVtcCAtIDMyKSAvIDEuOCAgIyDmkYTmsI/luqYgPSAo5Y2O5rCP5bqmIC0gMzIpIC8gMS44DQpzdHIoZGF0YSkNCg0KIyMg5Z+656GA5pWj54K55Zu+DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZmFjZXRfd3JhcCh+IE1vbnRoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF93cmFwKH4gTW9udGgsIG5jb2w9NSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBmYWNldF93cmFwKH4gTW9udGgsIG5yb3c9MSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArDQogIGZhY2V0X3dyYXAofiBNb250aCwgbnJvdz0xKQ0KDQojIyDkv67mlLnlm6DlrZDmoIfnrb4NCmRhdGEkTW9udGhzID0gZmFjdG9yKA0KICBkYXRhJE1vbnRoLA0KICBsZXZlbHMgPSA1OjksDQogIGxhYmVscyA9IGMoIk1heSIsICJKdW5lIiwgIkp1bHkiLCAiQXVndXN0IiwgIlNlcHRlbWJlciIpDQopDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRocykpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2hvdy5sZWdlbmQ9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IE1vbnRocywgbnJvdz0xKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRocykpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2hvdy5sZWdlbmQ9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IE1vbnRocywgbnJvdz0xLCBzY2FsZXM9ImZyZWUiKQ0KYGBgDQoNCiMjIyMg44CQ5a6e6Le1MuOAkWZhY2V0X2dyaWQoKe+8muS4pOS4quWIhuexu+WPmOmHj+eahOS6pOWPiee7hOWQiOWIhumdoiB7I+Wunui3tTJmYWNldF9ncmlk5Lik5Liq5YiG57G75Y+Y6YeP55qE5Lqk5Y+J57uE5ZCI5YiG6Z2ifQ0KDQotIGBmYWNldF93cmFwKClg77ya57q/5oCn5o6S5YiX77yI5Y236aW88J+Mr++8iQ0KICAtIGB+IOWIhue7hOWPmOmHj2ANCi0gYGZhY2V0X2dyaWQoKWDvvJrkuKTlm6DntKDkuqTlj4nnu4TlkIjmjpLliJfvvIjljY7lpKvppbzwn6eH77yJDQogIC0gYOihjOWIhue7hOWPmOmHjyB+IOWIl+WIhue7hOWPmOmHj2ANCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTUwNTM3MjMxNS5wbmcpDQoNCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KIyMg5Lik5Liq5YiG57G75Y+Y6YeP55qE5oOF5Ya1DQojIyDmlbDmja7lj5jph4/orqHnrpflpI3kuaDvvIhDaGFwIDA1ICYgMDbvvIkNCmRhdGEuYmZpID0gYXMuZGF0YS50YWJsZShwc3ljaDo6YmZpKQ0KZGF0YS5iZmlbLCBsZXQoDQogIEdlbmRlciA9IGZhY3RvcihnZW5kZXIsIGxldmVscz0xOjIsIGxhYmVscz1jKCJNYWxlIiwgIkZlbWFsZSIpKSwNCiAgQWdlID0gYXMubnVtZXJpYyhhZ2UpLA0KICBFZHUgPSBhcy5mYWN0b3IoZWR1Y2F0aW9uKSwNCiAgRSA9IE1FQU4oZGF0YS5iZmksICJFIiwgMTo1LCByZXY9YygxLDIpLCByYW5nZT0xOjYpLA0KICBBID0gTUVBTihkYXRhLmJmaSwgIkEiLCAxOjUsIHJldj0xLCByYW5nZT0xOjYpLA0KICBDID0gTUVBTihkYXRhLmJmaSwgIkMiLCAxOjUsIHJldj1jKDQsNSksIHJhbmdlPTE6NiksDQogIE4gPSBNRUFOKGRhdGEuYmZpLCAiTiIsIDE6NSwgcmV2PU5VTEwsIHJhbmdlPTE6NiksDQogIE8gPSBNRUFOKGRhdGEuYmZpLCAiTyIsIDE6NSwgcmV2PWMoMiw1KSwgcmFuZ2U9MTo2KQ0KKV0NCnN0cihkYXRhLmJmaSkNCg0KcCA9IGdncGxvdChkYXRhLmJmaVtBZ2U+PTE4ICYgQWdlPDYwICYgIWlzLm5hKEVkdSldLA0KICAgICAgICAgICBhZXMoeD1BZ2UsIHk9RSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYT0wLjEpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIGNvbG9yPSJmaXJlYnJpY2siLCBmaWxsPSJvcmFuZ2UiKSArDQogIGxhYnMoeT0iRXh0cmF2ZXJzaW9uIikNCnANCg0KcCArIGZhY2V0X3dyYXAofiBHZW5kZXIpDQpwICsgZmFjZXRfd3JhcCh+IEdlbmRlciArIEVkdSkgICMg5bm25LiN55CG5oOzDQpwICsgZmFjZXRfd3JhcCh+IEdlbmRlciAqIEVkdSkgICMg5bm25LiN55CG5oOzDQpwICsgZmFjZXRfZ3JpZCh+IEdlbmRlcikgICMgLiB+IOWIl+WIhue7hOWPmOmHj++8iC7ngrnlj6/ku6XnnIHnlaXvvIkNCnAgKyBmYWNldF9ncmlkKEdlbmRlciB+IC4pICAjIOihjOWIhue7hOWPmOmHjyB+IC7vvIgu54K55LiN6IO955yB55Wl77yJDQpwICsgZmFjZXRfZ3JpZChHZW5kZXIgfiBFZHUpICAjIOihjOWPmOmHjyB+IOWIl+WPmOmHjw0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMn0NCnAgKyBmYWNldF9ncmlkKEVkdSB+IEdlbmRlcikgICMg6KGM5Y+Y6YePIH4g5YiX5Y+Y6YePDQpgYGANCg0KIyDlpJrlm77nu4TlkIgNCg0KIyMjIyDjgJDlrp7ot7Uz44CRY293cGxvdDo6cGxvdF9ncmlkKCnvvJrlpJrlm77nu4TlkIggeyPlrp7ot7UzY293cGxvdHBsb3RfZ3JpZOWkmuWbvue7hOWQiH0NCg0KYGBge3J9DQpwMSA9IGdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnM9MTAsIGNvbG9yPSJibGFjayIsIGZpbGw9ImdyZXkiKSArDQogIGxhYnMoeD0iVGVtcGVyYXR1cmUiLCB5PSJGcmVxdWVuY3kiKQ0KDQpwMiA9IGdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh5PSJUZW1wZXJhdHVyZSIpDQoNCnAxMiA9IGNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIsIG5yb3c9MSwgbGFiZWxzPSJBVVRPIikNCnAxMg0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KcDMgPSBnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArDQogIGxhYnMoeT0iVGVtcGVyYXR1cmUiKQ0KDQpwNCA9IGdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQ9RkFMU0UpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBmYWNldF93cmFwKH4gTW9udGgsIG5yb3c9MSkgKw0KICBsYWJzKHk9IlRlbXBlcmF0dXJlIikNCg0KcDEyMzQgPSBjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBwMywgcDQsIG5jb2w9MiwgbGFiZWxzPSJBVVRPIikNCnAxMjM0DQpgYGANCg0KIyDlm77lvaLmlofku7bkv53lrZgNCg0KIyMjIyDjgJDlrp7ot7U044CRZ2dzYXZlKCnvvJrlm77lvaLmlofku7bkv53lrZggeyPlrp7ot7U0Z2dzYXZl5Zu+5b2i5paH5Lu25L+d5a2YfQ0KDQpgZ2dzYXZlKClg77ya5L+d5a2YYGdncGxvdGDlr7nosaHliLDmlofku7YNCg0KLSBgZmlsZWDvvJrlj6/ku6XmmK9gLnBuZ2DjgIFgLmpwZ2DjgIFgLnBkZmDnrYnmlofku7bmoLzlvI8NCiAgLSBQREbmoLzlvI/mmK/nn6Lph4/lm77vvIzml6DpmZDmlL7lpKfkvp3nhLbmuIXmmbANCi0gYHdpZHRoYO+8muWuveW6pu+8iOiLseWvuGluY2jvvIkNCi0gYGhlaWdodGDvvJrpq5jluqbvvIjoi7Hlr7hpbmNo77yJDQotIGBkcGlg77yIZG90cyBwZXIgaW5jaO+8ie+8muavj+iLseWvuOWDj+e0oOeCueaVsA0KDQpgYGB7cn0NCmdnc2F2ZShwMTIzNCwgZmlsZT0iRmlnMS5wbmciLCB3aWR0aD04LCBoZWlnaHQ9NiwgZHBpPTMwMCkNCiMg5YiG6L6o546H77yaMjQwMCAqIDE4MDDvvIjmr5TkvovlkIjpgILvvIzliIbovqjnjofpq5jvvIkNCmBgYA0KDQohW10oaW1nL0ZpZzEucG5nKQ0KDQpgYGB7cn0NCmdnc2F2ZShwMTIzNCwgZmlsZT0iRmlnMi5wbmciLCB3aWR0aD00LCBoZWlnaHQ9MywgZHBpPTMwMCkNCiMg5YiG6L6o546H77yaMTIwMCAqIDkwMO+8iOavlOS+i+S4jeWQiOmAgu+8jOiZveeEtuWIhui+qOeOh+mrmO+8iQ0KYGBgDQoNCiFbXShpbWcvRmlnMi5wbmcpDQoNCmBgYHtyfQ0KZ2dzYXZlKHAxMjM0LCBmaWxlPSJGaWczLnBuZyIsIHdpZHRoPTgsIGhlaWdodD02LCBkcGk9MTAwKQ0KIyDliIbovqjnjofvvJo4MDAgKiA2MDDvvIjmr5TkvovlkIjpgILvvIzkvYbliIbovqjnjofkvY7vvIkNCmBgYA0KDQohW10oaW1nL0ZpZzMucG5nKQ0KDQpgYGB7cn0NCmdnc2F2ZShwMTIzNCwgZmlsZT0iRmlnNC5wbmciLCB3aWR0aD0yNCwgaGVpZ2h0PTE4LCBkcGk9MTAwKQ0KIyDliIbovqjnjofvvJoyNDAwICogMTgwMO+8iOavlOS+i+S4jeWQiOmAgu+8jOWIhui+qOeOh+S5n+S9ju+8iQ0KYGBgDQoNCiFbXShpbWcvRmlnNC5wbmcpDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KdW5saW5rKGMoIkZpZzEucG5nIiwgIkZpZzIucG5nIiwgIkZpZzMucG5nIiwgIkZpZzQucG5nIikpDQpgYGANCg0KIyDjgJDkvZzkuJoxMOOAkeWkmuWbvue7hOWQiOe7g+S5oA0KDQrkvZzkuJropoHmsYLvvJoNCg0KLSDkvb/nlKhgY293cGxvdDo6cGxvdF9ncmlkKClg5Ye95pWw77yM5oqKW+OAkOS9nOS4mjnjgJFdKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS9DaGFwMTAjJUU0JUJEJTlDJUU0JUI4JTlBOSVFNSU5RiVCQSVFNyVBMSU4MCVFNyVCQiU5OCVFNSU5QiVCRSVFNyVCQiU4MyVFNCVCOSVBMCl7LnVyaX3lrozmiJDnmoQ05byg5Zu+5ou85ZCI5Zyo5LiA6LW377yM5re75YqgQVx+ROagh+etvu+8jOacgOWQjuS/neWtmOS4uuWQiOmAguWwuuWvuOeahOmrmOa4heaZsOW6puWbvuW9ouaWh+S7tu+8iGAucG5nYOagvOW8j++8jOa4heaZsOW6pmBkcGlg6Iez5bCR5Li6MzAw77yJDQotIOS9v+eUqFIgTWFya2Rvd27lrozmiJDvvIzlr7nlhbPplK7ku6PnoIHlj4rnu5PmnpzopoHmnInms6jph4ror7TmmI4NCg0K5bmz5Y+w5o+Q5Lqk77yaDQoNCi0g5YWz6ZSu5Luj56CB5oiq5Zu+5Y+K5L+d5a2Y55qEUE5H5Zu+5b2iDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpb6L+U5Zue6K++56iL5Li76aG1XShodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvKQ0KDQrCqSDljIXlr5LlkLTpnJwNCg==