La visualización de datos es útil no sólo para explorar los datos e
identificar la relación entre diferentes variables, sino también para
comunicar el resultado del análisis. El paquete ggplot2
nos permite generar gráficos de alta calidad con unas pocas líneas de
código. Cualquier gráfico de ggplot tendrá al menos 3 componentes: los
datos, un sistema de coordenadas y una
geometría (la representación visual de los datos) y se
construirá por capas.
¡Empecemos a hacer gráficos!
Primera capa: el área del gráfico
La función principal de ggplot2 se llama también
ggplot()
, y nos permite iniciar el gráfico y definir las
características globales. El primer argumento de esta función serán los
datos que queremos visualizar, siempre en un data.frame. En este caso
utilizamos pinguinos
.
El segundo argumento se llama mapping
porque es donde
definimos cómo se “mapean” las columnas del data.frame o las variables
de los datos a las propiedades visuales de las geometrías. Este mapeo
está definido por la función aes()
. En este caso indicamos
que en el eje x queremos graficar la variable largo_pico_mm
y en el eje y la variable alto_pico_mm
.
Todo esto sólo generará la primera capa: el área del gráfico.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm))
Segunda capa: geometrías
Necesitamos añadir una nueva capa a nuestro gráfico, los elementos
geométricos o “geoms” que representarán los datos. Para ello añadimos
una función geom, por ejemplo si queremos representar los datos con
puntos utilizaremos geom_point()
. Para hacer esto
necesitaremos agregar un +
al final de la primera capaz
para sumar una segunda.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point()
Ya tenemos nuestro primer gráfico!
Tal vez observaste que los puntos están agrupados de una manera
particular. Quizá alguna otra variable explique este comportamiento.
Para incluir información de otras variables en nuestro gráfico
podemos aprovechar las características estéticas de las geometrías. En
este caso, podemos “pintar” los puntos según la especie de pingüino.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point(aes(color = especie))
De nuevo, utilizamos la función aes()
para asignar una
variable de nuestros datos a un elemento del gráfico. ¡Y tada! ¡Cada
especie de pingüinos tiene características diferentes!
Añadiendo geometrías
Muchas veces no basta con mirar los datos en bruto para identificar
la relación entre las variables; es necesario utilizar alguna
transformación estadística para resaltar esas relaciones, ya sea
ajustando un modelo o calculando alguna estadística.
Para ello, ggplot2 dispone de geoms que calculan transformaciones
estadísticas comunes. Vamos a probar con geom_smoth()
para
ajustar un modelo lineal a cada especie.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point(aes(color = especie)) +
geom_smooth(aes(color = especie), method = "lm")
Por defecto geom_smooth()
ajusta los datos utilizando el
método loess (regresión lineal local) cuando hay menos de 1000 datos
disponibles. Pero es muy común que se quiera ajustar una regresión
lineal global. En ese caso, tenemos que agregar el argumento
method = "lm"
.
Hablemos del aspecto del gráfico
Por ahora utilizamos el aspecto por defecto de ggplot. Podríamos
cambiar el aspecto del gráfico para adaptarlo al estilo de la
institución donde trabajamos, de la revista donde lo vamos a publicar o
simplemente para hacerlo más llamativo.
Empecemos por el color. Para cambiar el aspecto estético de un
elemento del gráfico, añadimos una nueva capa con la función
scale_*
. En este caso utilizaremos
scale_color_manual()
para elegir los colores de los puntos
manualmente. También podríamos utilizar paletas de colores previamente
definidas como las familias Viridis o Color Brewer.
Necesitaremos 3 colores para las 3 especies, usaremos
"darkorange"
, "purple"
y "cyan4"
siguiendo las hermosas visualizaciones hechas por Allison Horst.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point(aes(color = especie)) +
geom_smooth(aes(color = especie), method = "lm") +
scale_color_manual(values = c("darkorange","purple","cyan4"))
¡Va quedando! Ahora, vamos a añadir algunos elementos de texto con
una nueva capa ggplot: labs()
.
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point(aes(color = especie)) +
geom_smooth(aes(color = especie), method = "lm") +
scale_color_manual(values = c("darkorange","purple","cyan4")) +
labs(title = "Dimensiones del pico de los pingüinos",
subtitle = "Pingüinos Adelia, Barbijo y Papúaen la Estación Palmer LTER",
x = "Largo del pico (mm)",
y = "Alto del pico (mm)",
color = "Especie",
shape = "Especie")
Ahora las etiquetas de los ejes son más legibles y tenemos un título
y un subtítulo que explican de qué trata el gráfico.
Podríamos seguir cambiando esto infinitamente pero terminaremos con
el aspecto general del gráfico.
El aspecto general de un gráfico está definido por su tema. ggplot2
tiene muchos temas disponibles y para todos los gustos. Pero también hay
otros paquetes que amplían las posibilidades, por ejemplo ggthemes. Por
defecto ggplot2 utiliza theme_grey()
, probemos con
theme_minimal()
:
ggplot(data = pinguinos, mapping = aes(x = largo_pico_mm, y = alto_pico_mm)) +
geom_point(aes(color = especie)) +
geom_smooth(aes(color = especie), method = "lm") +
scale_color_manual(values = c("darkorange","purple","cyan4")) +
labs(title = "Dimensiones del pico de los pingüinos",
subtitle = "Pingüinos Adelia, Barbijo y Papúaen la Estación Palmer LTER",
x = "Largo del pico (mm)",
y = "Alto del pico (mm)",
color = "Especie",
shape = "Especie") +
theme_minimal()
Ahora es tu turno. Elige un tema que te guste y pruébalo. Además, si
se te ocurre un título mejor, ¡modifícalo!
LS0tDQp0aXRsZTogIlZpc3VhbGl6YW5kbyBkYXRvcyINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogZmFsc2UNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQoJZWNobyA9IFRSVUUsDQoJbWVzc2FnZSA9IEZBTFNFLA0KCXdhcm5pbmcgPSBGQUxTRQ0KKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCnBpbmd1aW5vcyA8LSByZWFkX2NzdigiZGF0b3MvcGluZ3Vpbm9zLmNzdiIpDQpgYGANCg0KTGEgdmlzdWFsaXphY2nDs24gZGUgZGF0b3MgZXMgw7p0aWwgbm8gc8OzbG8gcGFyYSBleHBsb3JhciBsb3MgZGF0b3MgZSBpZGVudGlmaWNhciBsYSByZWxhY2nDs24gZW50cmUgZGlmZXJlbnRlcyB2YXJpYWJsZXMsIHNpbm8gdGFtYmnDqW4gcGFyYSBjb211bmljYXIgZWwgcmVzdWx0YWRvIGRlbCBhbsOhbGlzaXMuIEVsIHBhcXVldGUgKipnZ3Bsb3QyKiogbm9zIHBlcm1pdGUgZ2VuZXJhciBncsOhZmljb3MgZGUgYWx0YSBjYWxpZGFkIGNvbiB1bmFzIHBvY2FzIGzDrW5lYXMgZGUgY8OzZGlnby4gQ3VhbHF1aWVyIGdyw6FmaWNvIGRlIGdncGxvdCB0ZW5kcsOhIGFsIG1lbm9zIDMgY29tcG9uZW50ZXM6IGxvcyAqKmRhdG9zKiosIHVuICoqc2lzdGVtYSBkZSBjb29yZGVuYWRhcyoqIHkgdW5hICoqZ2VvbWV0csOtYSoqIChsYSByZXByZXNlbnRhY2nDs24gdmlzdWFsIGRlIGxvcyBkYXRvcykgeSBzZSBjb25zdHJ1aXLDoSBwb3IgY2FwYXMuDQoNCsKhRW1wZWNlbW9zIGEgaGFjZXIgZ3LDoWZpY29zIQ0KDQojIyBQcmltZXJhIGNhcGE6IGVsIMOhcmVhIGRlbCBncsOhZmljbw0KDQpMYSBmdW5jacOzbiBwcmluY2lwYWwgZGUgZ2dwbG90MiBzZSBsbGFtYSB0YW1iacOpbiBgZ2dwbG90KClgLCB5IG5vcyBwZXJtaXRlIGluaWNpYXIgZWwgZ3LDoWZpY28geSBkZWZpbmlyIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGdsb2JhbGVzLiBFbCBwcmltZXIgYXJndW1lbnRvIGRlIGVzdGEgZnVuY2nDs24gc2Vyw6FuIGxvcyBkYXRvcyBxdWUgcXVlcmVtb3MgdmlzdWFsaXphciwgc2llbXByZSBlbiB1biBkYXRhLmZyYW1lLiBFbiBlc3RlIGNhc28gdXRpbGl6YW1vcyBgcGluZ3Vpbm9zYC4NCg0KRWwgc2VndW5kbyBhcmd1bWVudG8gc2UgbGxhbWEgYG1hcHBpbmdgIHBvcnF1ZSBlcyBkb25kZSBkZWZpbmltb3MgY8OzbW8gc2UgIm1hcGVhbiIgbGFzIGNvbHVtbmFzIGRlbCBkYXRhLmZyYW1lIG8gbGFzIHZhcmlhYmxlcyBkZSBsb3MgZGF0b3MgYSBsYXMgcHJvcGllZGFkZXMgdmlzdWFsZXMgZGUgbGFzIGdlb21ldHLDrWFzLiBFc3RlIG1hcGVvIGVzdMOhIGRlZmluaWRvIHBvciBsYSBmdW5jacOzbiBgYWVzKClgLiBFbiBlc3RlIGNhc28gaW5kaWNhbW9zIHF1ZSBlbiBlbCBlamUgeCBxdWVyZW1vcyBncmFmaWNhciBsYSB2YXJpYWJsZSBgbGFyZ29fcGljb19tbWAgeSBlbiBlbCBlamUgeSBsYSB2YXJpYWJsZSBgYWx0b19waWNvX21tYC4NCg0KVG9kbyBlc3RvIHPDs2xvIGdlbmVyYXLDoSBsYSBwcmltZXJhIGNhcGE6IGVsIMOhcmVhIGRlbCBncsOhZmljby4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBpbmd1aW5vcywgbWFwcGluZyA9IGFlcyh4ID0gbGFyZ29fcGljb19tbSwgeSA9IGFsdG9fcGljb19tbSkpIA0KYGBgDQoNCiMjIFNlZ3VuZGEgY2FwYTogZ2VvbWV0csOtYXMNCg0KTmVjZXNpdGFtb3MgYcOxYWRpciB1bmEgbnVldmEgY2FwYSBhIG51ZXN0cm8gZ3LDoWZpY28sIGxvcyBlbGVtZW50b3MgZ2VvbcOpdHJpY29zIG8gImdlb21zIiBxdWUgcmVwcmVzZW50YXLDoW4gbG9zIGRhdG9zLiBQYXJhIGVsbG8gYcOxYWRpbW9zIHVuYSBmdW5jacOzbiBnZW9tLCBwb3IgZWplbXBsbyBzaSBxdWVyZW1vcyByZXByZXNlbnRhciBsb3MgZGF0b3MgY29uIHB1bnRvcyB1dGlsaXphcmVtb3MgYGdlb21fcG9pbnQoKWAuIFBhcmEgaGFjZXIgZXN0byBuZWNlc2l0YXJlbW9zIGFncmVnYXIgdW4gYCtgIGFsIGZpbmFsIGRlIGxhIHByaW1lcmEgY2FwYXogcGFyYSBzdW1hciB1bmEgc2VndW5kYS4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwaW5ndWlub3MsIG1hcHBpbmcgPSBhZXMoeCA9IGxhcmdvX3BpY29fbW0sIHkgPSBhbHRvX3BpY29fbW0pKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCllhIHRlbmVtb3MgbnVlc3RybyBwcmltZXIgZ3LDoWZpY28hIA0KDQpUYWwgdmV6IG9ic2VydmFzdGUgcXVlIGxvcyBwdW50b3MgZXN0w6FuIGFncnVwYWRvcyBkZSB1bmEgbWFuZXJhIHBhcnRpY3VsYXIuIFF1aXrDoSBhbGd1bmEgb3RyYSB2YXJpYWJsZSBleHBsaXF1ZSBlc3RlIGNvbXBvcnRhbWllbnRvLiANCg0KUGFyYSBpbmNsdWlyIGluZm9ybWFjacOzbiBkZSBvdHJhcyB2YXJpYWJsZXMgZW4gbnVlc3RybyBncsOhZmljbyBwb2RlbW9zIGFwcm92ZWNoYXIgbGFzIGNhcmFjdGVyw61zdGljYXMgZXN0w6l0aWNhcyBkZSBsYXMgZ2VvbWV0csOtYXMuIEVuIGVzdGUgY2FzbywgcG9kZW1vcyAicGludGFyIiBsb3MgcHVudG9zIHNlZ8O6biBsYSBlc3BlY2llIGRlIHBpbmfDvGluby4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwaW5ndWlub3MsIG1hcHBpbmcgPSBhZXMoeCA9IGxhcmdvX3BpY29fbW0sIHkgPSBhbHRvX3BpY29fbW0pKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZXNwZWNpZSkpDQpgYGANCg0KRGUgbnVldm8sIHV0aWxpemFtb3MgbGEgZnVuY2nDs24gYGFlcygpYCBwYXJhIGFzaWduYXIgdW5hIHZhcmlhYmxlIGRlIG51ZXN0cm9zIGRhdG9zIGEgdW4gZWxlbWVudG8gZGVsIGdyw6FmaWNvLiDCoVkgdGFkYSEgwqFDYWRhIGVzcGVjaWUgZGUgcGluZ8O8aW5vcyB0aWVuZSBjYXJhY3RlcsOtc3RpY2FzIGRpZmVyZW50ZXMhDQoNCiMjIEHDsWFkaWVuZG8gZ2VvbWV0csOtYXMNCg0KTXVjaGFzIHZlY2VzIG5vIGJhc3RhIGNvbiBtaXJhciBsb3MgZGF0b3MgZW4gYnJ1dG8gcGFyYSBpZGVudGlmaWNhciBsYSByZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlczsgZXMgbmVjZXNhcmlvIHV0aWxpemFyIGFsZ3VuYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhIHBhcmEgcmVzYWx0YXIgZXNhcyByZWxhY2lvbmVzLCB5YSBzZWEgYWp1c3RhbmRvIHVuIG1vZGVsbyBvIGNhbGN1bGFuZG8gYWxndW5hIGVzdGFkw61zdGljYS4gDQoNClBhcmEgZWxsbywgZ2dwbG90MiBkaXNwb25lIGRlIGdlb21zIHF1ZSBjYWxjdWxhbiB0cmFuc2Zvcm1hY2lvbmVzIGVzdGFkw61zdGljYXMgY29tdW5lcy4gVmFtb3MgYSBwcm9iYXIgY29uIGBnZW9tX3Ntb3RoKClgIHBhcmEgYWp1c3RhciB1biBtb2RlbG8gbGluZWFsIGEgY2FkYSBlc3BlY2llLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBpbmd1aW5vcywgbWFwcGluZyA9IGFlcyh4ID0gbGFyZ29fcGljb19tbSwgeSA9IGFsdG9fcGljb19tbSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBlc3BlY2llKSkgKw0KICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBlc3BlY2llKSwgbWV0aG9kID0gImxtIikNCmBgYA0KDQpQb3IgZGVmZWN0byBgZ2VvbV9zbW9vdGgoKWAgYWp1c3RhIGxvcyBkYXRvcyB1dGlsaXphbmRvIGVsIG3DqXRvZG8gbG9lc3MgKHJlZ3Jlc2nDs24gbGluZWFsIGxvY2FsKSBjdWFuZG8gaGF5IG1lbm9zIGRlIDEwMDAgZGF0b3MgZGlzcG9uaWJsZXMuIFBlcm8gZXMgbXV5IGNvbcO6biBxdWUgc2UgcXVpZXJhIGFqdXN0YXIgdW5hIHJlZ3Jlc2nDs24gbGluZWFsIGdsb2JhbC4gRW4gZXNlIGNhc28sIHRlbmVtb3MgcXVlIGFncmVnYXIgZWwgYXJndW1lbnRvIGBtZXRob2QgPSAibG0iYC4NCg0KIyMgSGFibGVtb3MgZGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvICANCiAgDQpQb3IgYWhvcmEgdXRpbGl6YW1vcyBlbCBhc3BlY3RvIHBvciBkZWZlY3RvIGRlIGdncGxvdC4gUG9kcsOtYW1vcyBjYW1iaWFyIGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvIHBhcmEgYWRhcHRhcmxvIGFsIGVzdGlsbyBkZSBsYSBpbnN0aXR1Y2nDs24gZG9uZGUgdHJhYmFqYW1vcywgZGUgbGEgcmV2aXN0YSBkb25kZSBsbyB2YW1vcyBhIHB1YmxpY2FyIG8gc2ltcGxlbWVudGUgcGFyYSBoYWNlcmxvIG3DoXMgbGxhbWF0aXZvLiANCiAgDQpFbXBlY2Vtb3MgcG9yIGVsIGNvbG9yLiBQYXJhIGNhbWJpYXIgZWwgYXNwZWN0byBlc3TDqXRpY28gZGUgdW4gZWxlbWVudG8gZGVsIGdyw6FmaWNvLCBhw7FhZGltb3MgdW5hIG51ZXZhIGNhcGEgY29uIGxhIGZ1bmNpw7NuIGBzY2FsZV8qYC4gRW4gZXN0ZSBjYXNvIHV0aWxpemFyZW1vcyBgc2NhbGVfY29sb3JfbWFudWFsKClgIHBhcmEgZWxlZ2lyIGxvcyBjb2xvcmVzIGRlIGxvcyBwdW50b3MgbWFudWFsbWVudGUuIFRhbWJpw6luIHBvZHLDrWFtb3MgdXRpbGl6YXIgcGFsZXRhcyBkZSBjb2xvcmVzIHByZXZpYW1lbnRlIGRlZmluaWRhcyBjb21vIGxhcyBmYW1pbGlhcyBWaXJpZGlzIG8gQ29sb3IgQnJld2VyLiANCiAgDQpOZWNlc2l0YXJlbW9zIDMgY29sb3JlcyBwYXJhIGxhcyAzIGVzcGVjaWVzLCB1c2FyZW1vcyBgImRhcmtvcmFuZ2UiYCwgYCJwdXJwbGUiYCB5IGAiY3lhbjQiYCBzaWd1aWVuZG8gbGFzIGhlcm1vc2FzIHZpc3VhbGl6YWNpb25lcyBoZWNoYXMgcG9yIEFsbGlzb24gSG9yc3QuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwaW5ndWlub3MsIG1hcHBpbmcgPSBhZXMoeCA9IGxhcmdvX3BpY29fbW0sIHkgPSBhbHRvX3BpY29fbW0pKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZXNwZWNpZSkpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yID0gZXNwZWNpZSksIG1ldGhvZCA9ICJsbSIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtvcmFuZ2UiLCJwdXJwbGUiLCJjeWFuNCIpKSANCmBgYA0KDQrCoVZhIHF1ZWRhbmRvISBBaG9yYSwgdmFtb3MgYSBhw7FhZGlyIGFsZ3Vub3MgZWxlbWVudG9zIGRlIHRleHRvIGNvbiB1bmEgbnVldmEgY2FwYSBnZ3Bsb3Q6IGBsYWJzKClgLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGluZ3Vpbm9zLCBtYXBwaW5nID0gYWVzKHggPSBsYXJnb19waWNvX21tLCB5ID0gYWx0b19waWNvX21tKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGVzcGVjaWUpKSArDQogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGVzcGVjaWUpLCBtZXRob2QgPSAibG0iKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrb3JhbmdlIiwicHVycGxlIiwiY3lhbjQiKSkgKw0KICBsYWJzKHRpdGxlID0gIkRpbWVuc2lvbmVzIGRlbCBwaWNvIGRlIGxvcyBwaW5nw7xpbm9zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJQaW5nw7xpbm9zIEFkZWxpYSwgQmFyYmlqbyB5IFBhcMO6YWVuIGxhIEVzdGFjacOzbiBQYWxtZXIgTFRFUiIsDQogICAgICAgeCA9ICJMYXJnbyBkZWwgcGljbyAobW0pIiwNCiAgICAgICB5ID0gIkFsdG8gZGVsIHBpY28gKG1tKSIsDQogICAgICAgY29sb3IgPSAiRXNwZWNpZSIsDQogICAgICAgc2hhcGUgPSAiRXNwZWNpZSIpIA0KYGBgDQoNCkFob3JhIGxhcyBldGlxdWV0YXMgZGUgbG9zIGVqZXMgc29uIG3DoXMgbGVnaWJsZXMgeSB0ZW5lbW9zIHVuIHTDrXR1bG8geSB1biBzdWJ0w610dWxvIHF1ZSBleHBsaWNhbiBkZSBxdcOpIHRyYXRhIGVsIGdyw6FmaWNvLiANCg0KUG9kcsOtYW1vcyBzZWd1aXIgY2FtYmlhbmRvIGVzdG8gaW5maW5pdGFtZW50ZSBwZXJvIHRlcm1pbmFyZW1vcyBjb24gZWwgYXNwZWN0byBnZW5lcmFsIGRlbCBncsOhZmljby4gDQoNCkVsIGFzcGVjdG8gZ2VuZXJhbCBkZSB1biBncsOhZmljbyBlc3TDoSBkZWZpbmlkbyBwb3Igc3UgdGVtYS4gZ2dwbG90MiB0aWVuZSBtdWNob3MgdGVtYXMgZGlzcG9uaWJsZXMgeSBwYXJhIHRvZG9zIGxvcyBndXN0b3MuIFBlcm8gdGFtYmnDqW4gaGF5IG90cm9zIHBhcXVldGVzIHF1ZSBhbXBsw61hbiBsYXMgcG9zaWJpbGlkYWRlcywgcG9yIGVqZW1wbG8gZ2d0aGVtZXMuIFBvciBkZWZlY3RvIGdncGxvdDIgdXRpbGl6YSBgdGhlbWVfZ3JleSgpYCwgcHJvYmVtb3MgY29uIGB0aGVtZV9taW5pbWFsKClgOg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGluZ3Vpbm9zLCBtYXBwaW5nID0gYWVzKHggPSBsYXJnb19waWNvX21tLCB5ID0gYWx0b19waWNvX21tKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGVzcGVjaWUpKSArDQogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGVzcGVjaWUpLCBtZXRob2QgPSAibG0iKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrb3JhbmdlIiwicHVycGxlIiwiY3lhbjQiKSkgKw0KICBsYWJzKHRpdGxlID0gIkRpbWVuc2lvbmVzIGRlbCBwaWNvIGRlIGxvcyBwaW5nw7xpbm9zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJQaW5nw7xpbm9zIEFkZWxpYSwgQmFyYmlqbyB5IFBhcMO6YWVuIGxhIEVzdGFjacOzbiBQYWxtZXIgTFRFUiIsDQogICAgICAgeCA9ICJMYXJnbyBkZWwgcGljbyAobW0pIiwNCiAgICAgICB5ID0gIkFsdG8gZGVsIHBpY28gKG1tKSIsDQogICAgICAgY29sb3IgPSAiRXNwZWNpZSIsDQogICAgICAgc2hhcGUgPSAiRXNwZWNpZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KPiBBaG9yYSBlcyB0dSB0dXJuby4gRWxpZ2UgdW4gdGVtYSBxdWUgdGUgZ3VzdGUgeSBwcnXDqWJhbG8uIEFkZW3DoXMsIHNpIHNlIHRlIG9jdXJyZSB1biB0w610dWxvIG1lam9yLCDCoW1vZGlmw61jYWxvIQ0K