library(tidyverse)
[37m-- [1mAttaching packages[22m --------------------------------------- tidyverse 1.2.1 --[39m
[37m[32mv[37m [34mggplot2[37m 3.1.0 [32mv[37m [34mpurrr [37m 0.2.5
[32mv[37m [34mtibble [37m 2.1.3 [32mv[37m [34mdplyr [37m 0.8.3
[32mv[37m [34mtidyr [37m 0.8.2 [32mv[37m [34mstringr[37m 1.3.1
[32mv[37m [34mreadr [37m 1.3.1 [32mv[37m [34mforcats[37m 0.3.0[39m
[37m-- [1mConflicts[22m ------------------------------------------ tidyverse_conflicts() --
[31mx[37m [34mdplyr[37m::[32mfilter()[37m masks [34mstats[37m::filter()
[31mx[37m [34mdplyr[37m::[32mlag()[37m masks [34mstats[37m::lag()[39m
library(sf)
Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(lubridate)
Attaching package: 㤼㸱lubridate㤼㸲
The following object is masked from 㤼㸱package:base㤼㸲:
date
delitos <- read.csv("./data/delitos.csv")
delitos <- delitos %>%
sample_n(5000)
Las cinco instrucciones básicas de dplyr
- Elegir observaciones en función a sus valores:
filter()
.
- Reordenar las filas
arrange()
- Seleccionar variables de acuerdo a su nombre:
select()
- Crear variables nuevas en función de variables ya existentes:
mutate()
- Colapsar valores a un solo resumen:
summarise()
Todas pueden ser usadas en conjunto con group_by()
que cambia el “scope” de cada función: en lugar de operar sobre todo el dataset, lo hace al interior de cada grupo definido.
Las cinco instrucciones básicas de dplyr
Estas 6 funciones proveen los verbos para un “lenguaje” de manipulación de datos. Todas funcionan de forma parecida
- El primer argumento es un dataframe
- Los siguientes argumentos decriben qué hacer con el data frame, usando los nombres de variables sin comillas
- El resultado es un nuevo dataframe
Filtrando filas con filter()
Permite subsetear observaciones basados en sus valores.
- El primer argumento es el dataframe
- Los siguientes, las expresiones filtran el dataset
Filtrando filas con filter()
f <- filter(delitos,
comuna=='Comuna 6' | comuna=='Comuna 10')
head(f)
Filtrando filas con filter()
Para usar los filtros de forma efectiva es importante tener en cuenta los operadores que se usan
Un error común:
filter(delitos, comuna='Comuna 6')
Filtrando filas con filter()
Seleccionemos los registros que ocurrieron en diciembre y enero
filter(delitos, month(ymd(fecha)) == 1 | month(ymd(fecha)) == 12)
Otra forma:
filter(delitos, month(ymd(fecha)) %in% c(1,12))
Filtrando filas con filter()
Simplificar expresiones complicadas Ley de De Morgan:
!(x & y) == !x | !y
!(x | y) == !x & !y
rara traer los delitos que NO fueron en la Comuna 1 y NO en el primer trimestre del año, ambas líneas producen el mismo resultado:
filter(delitos, !(comuna=='Comuna 6' | month(ymd(fecha)) > 3))
filter(delitos, (!comuna=='Comuna 6' & !month(ymd(fecha)) > 3))
Filtrando filas con filter()
Missing data
Los NA
son tramposos en R.
- Casi cualquier operación incluyendo un
NA
va a devolver otro NA
x <- NA
y <- NA
x == y
[1] NA
Filtrando filas con filter()
Missing data
- Para detectar
NA
se puede usar la función is.na(x)
is.na(x)
[1] TRUE
df <- data.frame(x = c(1, NA, 3))
filter(df, x > 1)
filter(df, x > 1 | is.na(x))
Ordenando filas con arrange()
arrange()
funciona de forma similar a filter()
pero en lugar de seleccionar filas, cambia su orden
- Toma un dataframe y un set de columnas (o expresiones más complicadas)
f<-arrange(delitos, comuna, ymd(fecha), hms(hora))
head(f)
Ordenando filas con arrange()
- Usamos
desc()
para ordenar de forma descendente
f<-arrange(delitos, comuna, desc(ymd(fecha)), desc(hms(hora)))
head(f)
- Los
NA
siempre quedan al final de la tabla.
Seleccionando columnas con select()
- Es habitual encontrarse con datasets de cientos o miles de variables.
select()
permite concentrarse en las variables que a uno le resulten relevantes
- Se basa en los nombres de las variables
f<-select(delitos, comuna, barrio, tipo_delito) # Selección por nombre
head(f)
f<-select(delitos, comuna:fecha) # Selección por rango de nombres
head(f)
f<-select(delitos, -(comuna:fecha)) # Selección de todas las columnas excepto las que están en el rango de nombres
head(f)
Seleccionando columnas con select()
Algunas funciones útiles
starts_with("abc")
: matchea nombres que empiezan con “abc”.
ends_with("xyz")
: matchea nombres que terminan con “xyz”.
contains("ijk")
: matchea nombres que contienen con “ijk”.
matches("(.)\\1")
: selecciona variables que matchean con una regex
num_range("x", 1:3)
: matchea x1, x2 y x3.
Seleccionando columnas con select()
- Para cambiar el orden de las columnas es útil usar
everything()
f<-select(delitos, comuna, barrio, tipo_delito, everything())
head(f)
Seleccionando columnas con select()
- Para renombrar puede usarse
rename()
head(rename(delitos, COMUNA=comuna))
group_by
+ summarize()
group_by
cambia la unidad de análsisi del dataset a grupos. Así, al llamar a cualqyuer verbo de dplyr
se aplica “por grupo”,
- COnteo de delitos por mes
f<-group_by(delitos, f=month(ymd(fecha), label=TRUE))
summarize(f, delay=n())
LS0tDQp0aXRsZTogIkludHJvZHVjY2nDs24gYSBsYSBDaWVuY2lhIGRlIERhdG9zIHBhcmEgQ2llbnTDrWZpY29zIFNvY2lhbGVzIg0Kc3VidGl0bGU6ICJVbmlkYWQgMS4gUHJvZ3JhbWFjacOzbiBFc3RhZMOtc3RpY2EgZW4gUjogSW50cm9kdWNjacOzbiBhIFIgYmFzZSINCmF1dGhvcjogIkRyLiBHZXJtYW4gUm9zYXRpIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmBgYCAgICAgDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpkZWxpdG9zIDwtIHJlYWQuY3N2KCIuLi9kYXRhL2RlbGl0b3MuY3N2IikNCmRlbGl0b3MgPC0gZGVsaXRvcyAlPiUNCiAgICAgICAgICAgICAgICBzYW1wbGVfbig1MDAwKQ0KYGBgDQoNCg0KIyMgTGFzIGNpbmNvIGluc3RydWNjaW9uZXMgYsOhc2ljYXMgZGUgYGRwbHlyYA0KDQoNCiogRWxlZ2lyIG9ic2VydmFjaW9uZXMgZW4gZnVuY2nDs24gYSBzdXMgdmFsb3JlczogYGZpbHRlcigpYC4NCiogUmVvcmRlbmFyIGxhcyBmaWxhcyBgYXJyYW5nZSgpYA0KKiBTZWxlY2Npb25hciB2YXJpYWJsZXMgZGUgYWN1ZXJkbyBhIHN1IG5vbWJyZTogYHNlbGVjdCgpYA0KKiBDcmVhciB2YXJpYWJsZXMgbnVldmFzIGVuIGZ1bmNpw7NuIGRlIHZhcmlhYmxlcyB5YSBleGlzdGVudGVzOiBgbXV0YXRlKClgDQoqIENvbGFwc2FyIHZhbG9yZXMgYSB1biBzb2xvIHJlc3VtZW46IGBzdW1tYXJpc2UoKWANCg0KDQpUb2RhcyBwdWVkZW4gc2VyIHVzYWRhcyBlbiBjb25qdW50byBjb24gYGdyb3VwX2J5KClgIHF1ZSBjYW1iaWEgZWwgInNjb3BlIiBkZSBjYWRhIGZ1bmNpw7NuOiBlbiBsdWdhciBkZSBvcGVyYXIgc29icmUgdG9kbyBlbCBkYXRhc2V0LCBsbyBoYWNlIGFsIGludGVyaW9yIGRlIGNhZGEgZ3J1cG8gZGVmaW5pZG8uDQoNCg0KIyMgTGFzIGNpbmNvIGluc3RydWNjaW9uZXMgYsOhc2ljYXMgZGUgYGRwbHlyYA0KDQpFc3RhcyA2IGZ1bmNpb25lcyBwcm92ZWVuIGxvcyB2ZXJib3MgcGFyYSB1biAibGVuZ3VhamUiIGRlIG1hbmlwdWxhY2nDs24gZGUgZGF0b3MuDQpUb2RhcyBmdW5jaW9uYW4gZGUgZm9ybWEgcGFyZWNpZGENCg0KMS4gRWwgcHJpbWVyIGFyZ3VtZW50byBlcyB1biBkYXRhZnJhbWUNCjIuIExvcyBzaWd1aWVudGVzIGFyZ3VtZW50b3MgZGVjcmliZW4gcXXDqSBoYWNlciBjb24gZWwgZGF0YSBmcmFtZSwgdXNhbmRvIGxvcyBub21icmVzIGRlIHZhcmlhYmxlcyBzaW4gY29taWxsYXMNCjMuIEVsIHJlc3VsdGFkbyBlcyB1biBudWV2byBkYXRhZnJhbWUNCg0KDQojIyBGaWx0cmFuZG8gZmlsYXMgY29uIGBmaWx0ZXIoKWANCg0KUGVybWl0ZSBzdWJzZXRlYXIgb2JzZXJ2YWNpb25lcyBiYXNhZG9zIGVuIHN1cyB2YWxvcmVzLiANCg0KKiBFbCBwcmltZXIgYXJndW1lbnRvIGVzIGVsIGRhdGFmcmFtZQ0KKiBMb3Mgc2lndWllbnRlcywgbGFzIGV4cHJlc2lvbmVzIGZpbHRyYW4gZWwgZGF0YXNldA0KDQojIyBGaWx0cmFuZG8gZmlsYXMgY29uIGBmaWx0ZXIoKWANCiAgICAgICAgDQpgYGB7ciBlY2hvPVRSVUV9DQpmIDwtIGZpbHRlcihkZWxpdG9zLCANCiAgICAgICAgICAgIGNvbXVuYT09J0NvbXVuYSA2JyB8IGNvbXVuYT09J0NvbXVuYSAxMCcpDQpoZWFkKGYpDQpgYGANCg0KIyMgRmlsdHJhbmRvIGZpbGFzIGNvbiBgZmlsdGVyKClgDQpQYXJhIHVzYXIgbG9zIGZpbHRyb3MgZGUgZm9ybWEgZWZlY3RpdmEgZXMgaW1wb3J0YW50ZSB0ZW5lciBlbiBjdWVudGEgbG9zIG9wZXJhZG9yZXMgcXVlIHNlIHVzYW4NCg0KVW4gZXJyb3IgY29tw7puOiANCg0KKiBgZmlsdGVyKGRlbGl0b3MsIGNvbXVuYT0nQ29tdW5hIDYnKWANCg0KIyMjIyBPcGVyYWRvcmVzIGzDs2dpY29zDQohW10oLi4vUm1hcmtkb3ducy9pbWdzL3RyYW5zZm9ybS1sb2dpY2FsLnBuZykNCg0KDQojIyBGaWx0cmFuZG8gZmlsYXMgY29uIGBmaWx0ZXIoKWANClNlbGVjY2lvbmVtb3MgbG9zIHJlZ2lzdHJvcyBxdWUgb2N1cnJpZXJvbiBlbiBkaWNpZW1icmUgeSBlbmVybw0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZmlsdGVyKGRlbGl0b3MsIG1vbnRoKHltZChmZWNoYSkpID09IDEgfCBtb250aCh5bWQoZmVjaGEpKSA9PSAxMikNCmBgYA0KDQpPdHJhIGZvcm1hOg0KDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpmaWx0ZXIoZGVsaXRvcywgbW9udGgoeW1kKGZlY2hhKSkgJWluJSBjKDEsMTIpKQ0KYGBgDQoNCg0KIyMgRmlsdHJhbmRvIGZpbGFzIGNvbiBgZmlsdGVyKClgDQpTaW1wbGlmaWNhciBleHByZXNpb25lcyBjb21wbGljYWRhcw0KKkxleSBkZSBEZSBNb3JnYW46Kg0KIA0KKiBgISh4ICYgeSkgPT0gIXggfCAheWANCiogYCEoeCB8IHkpID09ICF4ICYgIXlgDQoNCnJhcmEgdHJhZXIgbG9zIGRlbGl0b3MgcXVlIE5PIGZ1ZXJvbiBlbiBsYSBDb211bmEgMSB5IE5PIGVuIGVsIHByaW1lciB0cmltZXN0cmUgZGVsIGHDsW8sIGFtYmFzIGzDrW5lYXMgcHJvZHVjZW4gZWwgbWlzbW8gcmVzdWx0YWRvOg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZmlsdGVyKGRlbGl0b3MsICEoY29tdW5hPT0nQ29tdW5hIDYnIHwgbW9udGgoeW1kKGZlY2hhKSkgPiAzKSkNCmZpbHRlcihkZWxpdG9zLCAoIWNvbXVuYT09J0NvbXVuYSA2JyAmICFtb250aCh5bWQoZmVjaGEpKSA+IDMpKQ0KYGBgDQoNCg0KIyMgRmlsdHJhbmRvIGZpbGFzIGNvbiBgZmlsdGVyKClgDQojIyMgTWlzc2luZyBkYXRhDQoNCkxvcyBgTkFgIHNvbiB0cmFtcG9zb3MgZW4gUi4NCg0KKiBDYXNpIGN1YWxxdWllciBvcGVyYWNpw7NuIGluY2x1eWVuZG8gdW4gYE5BYCB2YSBhIGRldm9sdmVyIG90cm8gYE5BYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KeCA8LSBOQQ0KeSA8LSBOQQ0KDQp4ID09IHkNCmBgYA0KDQoNCiMjIEZpbHRyYW5kbyBmaWxhcyBjb24gYGZpbHRlcigpYA0KIyMjIE1pc3NpbmcgZGF0YQ0KDQoqIFBhcmEgZGV0ZWN0YXIgYE5BYCBzZSBwdWVkZSB1c2FyIGxhIGZ1bmNpw7NuIGBpcy5uYSh4KWANCg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KaXMubmEoeCkNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gYygxLCBOQSwgMykpDQoNCmZpbHRlcihkZiwgeCA+IDEpDQoNCmZpbHRlcihkZiwgeCA+IDEgfCBpcy5uYSh4KSkNCmBgYA0KDQojIyBPcmRlbmFuZG8gZmlsYXMgY29uIGBhcnJhbmdlKClgDQoqIGBhcnJhbmdlKClgIGZ1bmNpb25hIGRlIGZvcm1hIHNpbWlsYXIgYSBgZmlsdGVyKClgIHBlcm8gZW4gbHVnYXIgZGUgc2VsZWNjaW9uYXIgZmlsYXMsIGNhbWJpYSBzdSBvcmRlbg0KKiBUb21hIHVuIGRhdGFmcmFtZSB5IHVuIHNldCBkZSBjb2x1bW5hcyAobyBleHByZXNpb25lcyBtw6FzIGNvbXBsaWNhZGFzKQ0KDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpmPC1hcnJhbmdlKGRlbGl0b3MsIGNvbXVuYSwgeW1kKGZlY2hhKSwgaG1zKGhvcmEpKQ0KaGVhZChmKQ0KYGBgDQoNCiMjIE9yZGVuYW5kbyBmaWxhcyBjb24gYGFycmFuZ2UoKWANCiogVXNhbW9zIGBkZXNjKClgIHBhcmEgb3JkZW5hciBkZSBmb3JtYSBkZXNjZW5kZW50ZQ0KDQpgYGB7ciBlY2hvPVRSVUV9DQpmPC1hcnJhbmdlKGRlbGl0b3MsIGNvbXVuYSwgZGVzYyh5bWQoZmVjaGEpKSwgZGVzYyhobXMoaG9yYSkpKQ0KaGVhZChmKQ0KYGBgDQoNCiogTG9zIGBOQWAgc2llbXByZSBxdWVkYW4gYWwgZmluYWwgZGUgbGEgdGFibGEuDQoNCg0KIyMgU2VsZWNjaW9uYW5kbyBjb2x1bW5hcyBjb24gYHNlbGVjdCgpYA0KKiBFcyBoYWJpdHVhbCBlbmNvbnRyYXJzZSBjb24gZGF0YXNldHMgZGUgY2llbnRvcyBvIG1pbGVzIGRlIHZhcmlhYmxlcy4gDQoqIGBzZWxlY3QoKWAgcGVybWl0ZSBjb25jZW50cmFyc2UgZW4gbGFzIHZhcmlhYmxlcyBxdWUgYSB1bm8gbGUgcmVzdWx0ZW4gcmVsZXZhbnRlcw0KKiBTZSBiYXNhIGVuIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMgDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpmPC1zZWxlY3QoZGVsaXRvcywgY29tdW5hLCBiYXJyaW8sIHRpcG9fZGVsaXRvKSAjIFNlbGVjY2nDs24gcG9yIG5vbWJyZQ0KaGVhZChmKQ0KDQpmPC1zZWxlY3QoZGVsaXRvcywgY29tdW5hOmZlY2hhKSAjIFNlbGVjY2nDs24gcG9yIHJhbmdvIGRlIG5vbWJyZXMNCmhlYWQoZikNCg0KZjwtc2VsZWN0KGRlbGl0b3MsIC0oY29tdW5hOmZlY2hhKSkgIyBTZWxlY2Npw7NuIGRlIHRvZGFzIGxhcyBjb2x1bW5hcyBleGNlcHRvIGxhcyBxdWUgZXN0w6FuIGVuIGVsIHJhbmdvIGRlIG5vbWJyZXMNCmhlYWQoZikNCmBgYA0KDQojIyBTZWxlY2Npb25hbmRvIGNvbHVtbmFzIGNvbiBgc2VsZWN0KClgDQojIyMgQWxndW5hcyBmdW5jaW9uZXMgw7p0aWxlcw0KDQoqIGBzdGFydHNfd2l0aCgiYWJjIilgOiBtYXRjaGVhIG5vbWJyZXMgcXVlIGVtcGllemFuIGNvbiDigJxhYmPigJ0uDQoqIGBlbmRzX3dpdGgoInh5eiIpYDogbWF0Y2hlYSBub21icmVzIHF1ZSB0ZXJtaW5hbiBjb24g4oCceHl64oCdLg0KKiBgY29udGFpbnMoImlqayIpYDogbWF0Y2hlYSBub21icmVzIHF1ZSBjb250aWVuZW4gY29uIOKAnGlqa+KAnS4NCiogYG1hdGNoZXMoIiguKVxcMSIpYDogc2VsZWNjaW9uYSB2YXJpYWJsZXMgcXVlIG1hdGNoZWFuIGNvbiB1bmEgcmVnZXgNCiogYG51bV9yYW5nZSgieCIsIDE6MylgOiBtYXRjaGVhIHgxLCB4MiB5IHgzLg0KDQoNCiMjIFNlbGVjY2lvbmFuZG8gY29sdW1uYXMgY29uIGBzZWxlY3QoKWANCiogUGFyYSBjYW1iaWFyIGVsIG9yZGVuIGRlIGxhcyBjb2x1bW5hcyBlcyDDunRpbCB1c2FyIGBldmVyeXRoaW5nKClgDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpmPC1zZWxlY3QoZGVsaXRvcywgY29tdW5hLCBiYXJyaW8sIHRpcG9fZGVsaXRvLCBldmVyeXRoaW5nKCkpDQpoZWFkKGYpDQpgYGANCg0KIyMgU2VsZWNjaW9uYW5kbyBjb2x1bW5hcyBjb24gYHNlbGVjdCgpYA0KKiBQYXJhIHJlbm9tYnJhciBwdWVkZSB1c2Fyc2UgYHJlbmFtZSgpYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KaGVhZChyZW5hbWUoZGVsaXRvcywgQ09NVU5BPWNvbXVuYSkpDQpgYGANCg0KIyMgVHJhbnNmb3JtYW5kbyBjb2x1bW5hcyBjb24gYG11dGF0ZSgpYA0KKiBTdWVsZSBzZXIgw7p0aWwgY3JlYXIgbnVldmFzIGNvbHVtbmFzIGNvbW8gZnVuY2lvbmVzIGRlIGNvbHVtbmFzIHlhIGV4aXN0ZW50ZXMuDQoqIGBtdXRhdGUoKWAgc2llbXByZSBhZ3JlZ2EgbnVldmFzIGNvbHVtbmFzIGFsIGZpbmFsIGRlbCBkYXRhc2V0DQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpmPC1tdXRhdGUoZGVsaXRvcywgDQogICAgICAgICAgaG9yYV9zaW1wbGUgPSBob3VyKGhtcyhob3JhKSksDQogICAgICAgICAgZGlhPWRheSh5bWQoZmVjaGEpKSwNCiAgICAgICAgICBtZXM9bW9udGgoeW1kKGZlY2hhKSksDQogICAgICAgICAgeWVhcj15ZWFyKHltZChmZWNoYSkpKQ0KaGVhZChmKQ0KYGBgDQoNCiMjIFRyYW5zZm9ybWFuZG8gY29sdW1uYXMgY29uIGBtdXRhdGUoKWANCiMjIyBBbGd1bmFzIG9wZXJhY2lvbmVzIGRlIGNyZWFjacOzbiDDunRpbGVzDQoNCiogT3BlcmFkb3JlcyBhcml0bcOpdGljb3M6IGArYCwgYC1gLCBgKmAsIGAvYCwgYF5gLCBzb24gw7p0aWxlcyBlbiBjb25qdW5jacOzbiBjb24gZXN0YWTDrXN0aWNvczoNCmB6c2NvcmUgPSAoeCAtIG1lYW4oeCkpL3NkKHgpYA0KKiBMb2dzOiBgbG9nKClgLCBgbG9nMigpYCwgYGxvZzEwKClgOiDDunRpbGVzIGN1YW5kbyBub3MgZW5jb250cmFtb3MgY29uIGRhdG9zIGRlIGVzY2FsYXMgbXV5IGRpdmVyc2FzLiBUYW1iacOpbiBwYXJhIGNvbnZlcnRpciByZWxhY2lvbmVzIG11bHRpcGxpY2F0aXZhcyBlbiBhZGl0aXZhcyAtc3VtYW1lbnRlIHByw6FjdGljbyBwYXJhIGxhIGV0YXBhIGRlIG1vZGVsYWRvLS4NCiogT2Zmc2V0czogYGxlYWQoKWAgeSBgbGFnKClgDQoqIEFjdW11bGFkb3JlczogYGN1bXN1bSgpYCwgYGN1bXByb2QoKWAsIGBjdW1taW4oKWAsIGBjdW1tYXgoKWAsIGBjdW1tZWFuKClgDQouLi4NCg0KKiBNdWNob3MgbcOhcyBlbiBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RyYW5zZm9ybS5odG1sI2FkZC1uZXctdmFyaWFibGVzLXdpdGgtbXV0YXRlKQ0KDQoNCiMjIGBncm91cF9ieWAgKyBgc3VtbWFyaXplKClgDQoqIGBncm91cF9ieWAgY2FtYmlhIGxhIHVuaWRhZCBkZSBhbsOhbHNpc2kgZGVsIGRhdGFzZXQgYSBncnVwb3MuIEFzw60sIGFsIGxsYW1hciBhIGN1YWxxeXVlciB2ZXJibyBkZSBgZHBseXJgIHNlIGFwbGljYSAicG9yIGdydXBvIiwNCiogQ09udGVvIGRlIGRlbGl0b3MgcG9yIG1lcw0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZjwtZ3JvdXBfYnkoZGVsaXRvcywgZj1tb250aCh5bWQoZmVjaGEpLCBsYWJlbD1UUlVFKSkNCnN1bW1hcml6ZShmLCBkZWxheT1uKCkpDQpgYGANCg==