Instrucciones de control y estadística descriptiva

Ejemplo sobre estadística descriptiva

Paquetes requeridos
Paquete Uso
ggplot2 Gráficos
viridis Paleta de colores de los gráficos
corrplot Correlación de graficos
progress Mostrar el progreso de las iteraciones

Contexto

In June 2016, a referendum was held in the United Kingdom (UK) to decide whether or not to remain part of the European Union (EU). 72% of registered voters took part, and of those, 51.2% voted to leave the EU. In February 2017, Martin Rosenbaum, freedom of information specialist at BBC News, published the article, Local Voting Figures Shed New Light on EU Referendum (http://www.bbc.co.uk/news/uk-politics-38762034). He obtained data from 1,070 electoral wards (the smallest administrative division for electoral purposes in the UK), with numbers for Leave and Remain votes in each ward.

Martin Rosenbaum calculated some statistical associations between the proportion of Leave votes in a ward and some of its social, economic, and demographic characteristics by making use of the most recent UK census, which was conducted in 2011. He used his data for a university class, and that’s the data we will use in this example, with some variables removed. ### Los datos Los datos que se usaron se van a leer de un documento en CSV separado por comas. Para una importación sencilla usaremos la función read.csv

Ejercicio 1

Abre el archivo y conoce los datos con los que vamos a trabajar

archivo <-  read.csv("data_brexit_referendum.csv")

Conociendo el contenido

Una manera de conocer lo que tiene el conjunto de datos que acabamos de leer es con la función View. Sin embargo, esta función no resulta útil cuando queremos darnos una idea global del contenido de un cierto objeto. En este caso, podemos usar la función summary

View(archivo)

summary(archivo)

Si deseamos conocer si un vector es o no numérico, podemos usar la función is.numeric().

# Salida para datos numéricos
is.numeric(archivo$Proporcion)
# Salida para datos no numéricos
is.numeric(archivo$Voto)

Ejercicio 2

¿qué significa el siguiente código?

archivo[archivo$Leave == -1, "Leave"] <- NA

Limpieza de los datos

A veces nos faltarán datos dentro de nuestras encuestas o registros. Una forma de contarlos o identificarlos es con la función is.na con la que obtendremos una respuesta booleana que podemos sumar puesto que R va a tratar los valores lógicos con los siguientes puntajes: TRUE = 1 FALSE = 0

Ejercicio 3

Calcula la cantidad de registros vacíos de la columna “Leave”

sum(is.na(archivo$Leave))

Obteniendo proporciones

Para este ejemplo, agreguemos una columna nueva con la proporción de los votos en favor de dejar la Unión Europea (la columna Leave) y la cantidad total de votos (colmna NVotes)

archivo$Proporcion <- archivo$Leave / archivo$NVotes

Para pasar de los números a las palabras, creemos con la función ifelse una nueva columna para indicar “Dejar” si la proporción es superior al 50% o “Permanecer” si es menor.

Control de flujo

La instruión de if o ifelse puede ser aplicada, como todo en R, sobre cada elemento de un vector de manera directa. Además de if, en R podemos usar la función which que nos permite seleccionar sólo aquellos campos que cumplan una cierta condición y maneja de mejor manera los errores de los casos vacíos. Por ejemplo, el código siguiente se desea asignar a los registros con una proporción superior al 50% la etiqueta de “Abandonar” y una de “Permanecer” a aquellos registros con una proporción menor o igual al 50%.

archivo[archivo$Proporcion > 0.5, "Voto"] <- "Abandonar"
archivo[archivo$Proporcion <= 0.5, "Voto"] <- "Permanecer"

Este error sucede porque hay renglones con la etiqueta NA de la proporción, pues desde su cálculo tenían el error que en uno de sus campos, contaban con NA. Una manera de resolver este error es con La función which. Con esta función aquellos campos que contengan una NA no serán considerados

archivo[which(archivo$Proporcion > 0.5), "Voto"] <- "Abandonar"
archivo[which(archivo$Proporcion <= 0.5), "Voto"] <- "Permanecer"

En esta ocasión, el proceso es más sencillo con el uso de ifelse

archivo$Voto <- ifelse(archivo$Proporcion > 0.5, "Dejar", "Permanecer")

Haciendo un subconjunto de las columnas numéricas

Para hacer un análisis gráfico, vamos a extraer de nuestro arreglo original sólo las columnas numéricas. Esto lo podemos hacer de dos maneras. Mismas que podremos explorar en el Ejercicio 4

Ejercicio 4

Genera un dataframe sólo con las columnas numéricas salvo la de ID.

Solución usando for

solonumeros1 <- data.frame(matrix(ncol = 0, nrow = length(archivo[,1])))
j <- 1
for(i in 1:length(archivo)){
  if(is.numeric(archivo[,i])){
    solonumeros1[,j] <- archivo[,i]
    j <- j+1
  }
}
# summary(solonumeros1)
length(solonumeros1)

Solución usando sapply

solonumeros2 <- archivo[,sapply(archivo, is.numeric)]
# summary(solonumeros2)
length(solonumeros2)

Ahora borremos de este nuevo conjunto, la columna de los identificadores. Esto lo podemos hacer creando un subset sólo con las columnas que queremos conservar o antecediendo el signo - a un vector de índices.

solonumeros2 <- solonumeros2[, -c(1)]
length(solonumeros2)
summary(solonumeros2)

Guardando los resumenes

Para no calcular más de una vez la media, mediana, valor máximo y demás valores que nos presenta la función summary, podemos guardarlos en una lista con La función lapply, La función cbind, La función do.call.

Resumen estadístico

VEstadisticos <- do.call(cbind, lapply(solonumeros2, summary))
View(VEstadisticos)

Si deseamos que las columnas sean las medidas estadísticas, entonces debemos usar rbind en lugar de cbind.

do.call(rbind, lapply(solonumeros2, summary))

Resumen específico

columnas_deseadas <- c("RegionName", "Proporcion", "AdultMeanAge", "NoQuals", "L4Quals_plus")
archivo[which.max(archivo$Proporcion), columnas_deseadas]
archivo[which.min(archivo$Proporcion), columnas_deseadas]

Graficación exploratoria

Gráfica de barras

Con los datos que tenemos crearemos un histograma entre los condados y sus proporciones con la función barplot()

# archivo$RegionName
# Table crea una tabla con los nombres de las regioes y la cantidad de registros que tienen.
table(archivo$RegionName)
# Con prop.table obtendremos la tabla anterior pero en porcentajes
prop.table(table(archivo$RegionName))

barplot(
  height = prop.table(table(archivo$RegionName)),
  main = "Proporción de los votos por región",
  ylab = "Frecuencia",
  xlab = "Región",
  col = "white"
)

Para visualizar de mejor manera los nombres de las regiones podemos hacer lo siguiente:

archivo$RegionName <- as.character(archivo$RegionName)
Regiones <- factor(archivo$RegionName)
ciudades <- data.frame("Nombre" = levels(Regiones), "Abreviatura" = c("EM", "EE", "L", "NE", "NW", "SE", "SW", "WM", "YH"))
View(ciudades)
for (i in 1:9) {
  print("Nombre completo")
  print(ciudades[i,"Nombre"])
  print("Abreviatura")
  print(ciudades[i,"Abreviatura"])
  archivo[which(archivo$RegionName == ciudades[i, "Nombre"]), "RegionName"] <- ciudades[i, "Abreviatura"]
}

Gráficos avanzados: ggplot2 y viridis

Ahora haremos un gráfico que cruze la proporción y los votos para dejar la Unión Europea de cada condado coloreados por la proporción de la población blanca en esa región. Para realizar esta gráfica necesitamos de dos bibliotecas: ggplot2 y viridisLite

library(ggplot2)

Una vez instalados estos paquetes:

grafico <- ggplot(archivo, aes(x=RegionName, y=Proporcion, color=White))
grafico <- grafico + geom_point()
print(grafico)

La función which

Esta función devolverá el índice del valor que satisface cierta condición. El parámetro de entrada será un vector lógico.

Ejemplo

Usando el vector predefinido de letras, encontraremos el índice de la letra R

letters
which(letters == "r")

En el siguiente ejemplo se crea un vector con una secuencia que va de 3 en 3, del 1 hasta el 150. Posteriormente se extraen los índices de los números mayores a 50

# Creando un vector de prueba
df<- seq(from=1, to=150, by=3)
which(df > 50)

Miniejercicio con which

¿Cuáles son los índices de los números pares?

which((df %% 2 )== 0)
df[which((df %% 2 )== 0)]

La función sapply

La función sapply permite iterar sobre una lista o vector sin la necesidad de usar el ciclo for, que es conocido por ser lento en R.

sapply(1:4, sqrt)
sapply(1:6,function(i) i**2)

En el ejemplo anterior hemos visto como usar sapply sobre vectores, podemos ver su uso en matrices dentro de la sección “Haciendo un subconjunto de las columnas numéricas

La función lapply

La función lapply aplica una función a una lista o a un vector y devuelve una lista de la misma longitud que el objeto de entrada.

lapply(1:4, sqrt)
lapply(1:6,function(i) i**2)

La función cbind

Esta función (column-bind) se puede usar para agregar vectores, matrices y marcos de datos por columna a un nuevo marco de datos.

a <- c(1,6,9,8,3,5,4)
b <- c(8,6,2,3,7,6,7)
c <- cbind(a,b)
c
d <- c(32,41,7,11,35,66,44)
df <- data.frame("A" = a, "B" = b, "D" = d)
df
e <- c(1,5,9,6,4,3,78)
df2 <- cbind(df, e)
df2

La función do.call

Esta función permite aplicar una cierta función a una lista de argumentos

mifuncion <- function(a,b){
  a + b
}
milista <- list(c(1,6,8), c(9,6,7))
do.call(mifuncion, milista)

Referencias

  1. Ejemplo traducido y adaptado del libro R Programming By Example de Omar Trejo y Peter C. Figliozzi
  2. The which function in R programming, Journal Dev.
  3. La función sapply en R, R Coder
  4. Función lapply en R, R coder
  5. How to Use cbind in R (With Examples), Statology
  6. Do.call: Execute a function call, RDocumentation
  7. ggplot2, ggplot2
  8. What is the do.call() function in R?, educative
LS0tDQp0aXRsZTogIk5vdGVib29rIHNvYnJlIGFuw6FsaXNpcyBlc3RhZMOtc3RpY28gZGUgdW4gZGF0YWZyYW1lIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQojIEluc3RydWNjaW9uZXMgZGUgY29udHJvbCB5IGVzdGFkw61zdGljYSBkZXNjcmlwdGl2YSB7LnRhYnNldH0NCg0KIyMgRWplbXBsbyBzb2JyZSBlc3RhZMOtc3RpY2EgZGVzY3JpcHRpdmENCg0KfCBQYXF1ZXRlICB8IFVzbyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGdncGxvdDIgIHwgR3LDoWZpY29zICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgdmlyaWRpcyAgfCBQYWxldGEgZGUgY29sb3JlcyBkZSBsb3MgZ3LDoWZpY29zICAgICAgfA0KfCBjb3JycGxvdCB8IENvcnJlbGFjacOzbiBkZSBncmFmaWNvcyAgICAgICAgICAgICAgICB8DQp8IHByb2dyZXNzIHwgTW9zdHJhciBlbCBwcm9ncmVzbyBkZSBsYXMgaXRlcmFjaW9uZXMgfA0KDQo6IFBhcXVldGVzIHJlcXVlcmlkb3MNCg0KIyMjIENvbnRleHRvDQoNCkluIEp1bmUgMjAxNiwgYSByZWZlcmVuZHVtIHdhcyBoZWxkIGluIHRoZSBVbml0ZWQgS2luZ2RvbSAoVUspIHRvIGRlY2lkZQ0Kd2hldGhlciBvciBub3QgdG8gcmVtYWluIHBhcnQgb2YgdGhlIEV1cm9wZWFuIFVuaW9uIChFVSkuIDcyJSBvZg0KcmVnaXN0ZXJlZCB2b3RlcnMgdG9vayBwYXJ0LCBhbmQgb2YgdGhvc2UsIDUxLjIlIHZvdGVkIHRvIGxlYXZlIHRoZSBFVS4NCkluIEZlYnJ1YXJ5IDIwMTcsIE1hcnRpbiBSb3NlbmJhdW0sIGZyZWVkb20gb2YgaW5mb3JtYXRpb24gc3BlY2lhbGlzdCBhdA0KQkJDIE5ld3MsIHB1Ymxpc2hlZCB0aGUgYXJ0aWNsZSwgTG9jYWwgVm90aW5nIEZpZ3VyZXMgU2hlZCBOZXcgTGlnaHQgb24NCkVVIFJlZmVyZW5kdW0gKDxodHRwOi8vd3d3LmJiYy5jby51ay9uZXdzL3VrLXBvbGl0aWNzLTM4NzYyMDM0PikuIEhlDQpvYnRhaW5lZCBkYXRhIGZyb20gMSwwNzAgZWxlY3RvcmFsIHdhcmRzICh0aGUgc21hbGxlc3QgYWRtaW5pc3RyYXRpdmUNCmRpdmlzaW9uIGZvciBlbGVjdG9yYWwgcHVycG9zZXMgaW4gdGhlIFVLKSwgd2l0aCBudW1iZXJzIGZvciBMZWF2ZSBhbmQNClJlbWFpbiB2b3RlcyBpbiBlYWNoIHdhcmQuDQoNCk1hcnRpbiBSb3NlbmJhdW0gY2FsY3VsYXRlZCBzb21lIHN0YXRpc3RpY2FsIGFzc29jaWF0aW9ucyBiZXR3ZWVuIHRoZQ0KcHJvcG9ydGlvbiBvZiBMZWF2ZSB2b3RlcyBpbiBhIHdhcmQgYW5kIHNvbWUgb2YgaXRzIHNvY2lhbCwgZWNvbm9taWMsDQphbmQgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIGJ5IG1ha2luZyB1c2Ugb2YgdGhlIG1vc3QgcmVjZW50IFVLDQpjZW5zdXMsIHdoaWNoIHdhcyBjb25kdWN0ZWQgaW4gMjAxMS4gSGUgdXNlZCBoaXMgZGF0YSBmb3IgYSB1bml2ZXJzaXR5DQpjbGFzcywgYW5kIHRoYXQncyB0aGUgZGF0YSB3ZSB3aWxsIHVzZSBpbiB0aGlzIGV4YW1wbGUsIHdpdGggc29tZQ0KdmFyaWFibGVzIHJlbW92ZWQuIFwjIyMgTG9zIGRhdG9zIExvcyBkYXRvcyBxdWUgc2UgdXNhcm9uIHNlIHZhbiBhIGxlZXINCmRlIHVuIGRvY3VtZW50byBlbiBDU1Ygc2VwYXJhZG8gcG9yIGNvbWFzLiBQYXJhIHVuYSBpbXBvcnRhY2nDs24gc2VuY2lsbGENCnVzYXJlbW9zIGxhIGZ1bmNpw7NuIHJlYWQuY3N2DQoNCiMjIyMgRWplcmNpY2lvIDENCg0KQWJyZSBlbCBhcmNoaXZvIHkgY29ub2NlIGxvcyBkYXRvcyBjb24gbG9zIHF1ZSB2YW1vcyBhIHRyYWJhamFyDQoNCmBgYHtyIExlY3R1cmEgZGUgYXJjaGl2b30NCmFyY2hpdm8gPC0gIHJlYWQuY3N2KCJkYXRhX2JyZXhpdF9yZWZlcmVuZHVtLmNzdiIpDQpgYGANCg0KIyMjIENvbm9jaWVuZG8gZWwgY29udGVuaWRvDQoNClVuYSBtYW5lcmEgZGUgY29ub2NlciBsbyBxdWUgdGllbmUgZWwgY29uanVudG8gZGUgZGF0b3MgcXVlIGFjYWJhbW9zIGRlIGxlZXIgZXMgY29uIGxhIGZ1bmNpw7NuIFZpZXcuIFNpbiBlbWJhcmdvLCBlc3RhIGZ1bmNpw7NuIG5vIHJlc3VsdGEgw7p0aWwgY3VhbmRvIHF1ZXJlbW9zIGRhcm5vcyB1bmEgaWRlYSBnbG9iYWwgZGVsIGNvbnRlbmlkbyBkZSB1biBjaWVydG8gb2JqZXRvLiBFbiBlc3RlIGNhc28sIHBvZGVtb3MgdXNhciBsYSBmdW5jacOzbiBzdW1tYXJ5DQoNCmBgYHtyIFVzbyBkZSBzdW1tYXJ5fQ0KVmlldyhhcmNoaXZvKQ0KDQpzdW1tYXJ5KGFyY2hpdm8pDQpgYGANCg0KU2kgZGVzZWFtb3MgY29ub2NlciBzaSB1biB2ZWN0b3IgZXMgbyBubyBudW3DqXJpY28sIHBvZGVtb3MgdXNhciBsYSBmdW5jacOzbiBpcy5udW1lcmljKCkuDQoNCmBgYHtyIEN1w6FsZXMgc29uIG7Dum1lcm9zfQ0KIyBTYWxpZGEgcGFyYSBkYXRvcyBudW3DqXJpY29zDQppcy5udW1lcmljKGFyY2hpdm8kUHJvcG9yY2lvbikNCiMgU2FsaWRhIHBhcmEgZGF0b3Mgbm8gbnVtw6lyaWNvcw0KaXMubnVtZXJpYyhhcmNoaXZvJFZvdG8pDQoNCmBgYA0KDQojIyMjIEVqZXJjaWNpbyAyDQoNCsK/cXXDqSBzaWduaWZpY2EgZWwgc2lndWllbnRlIGPDs2RpZ28/DQoNCmBgYHtyIFRyYW5zZm9ybWFjacOzbiBkZSAtMXN9DQphcmNoaXZvW2FyY2hpdm8kTGVhdmUgPT0gLTEsICJMZWF2ZSJdIDwtIE5BDQpgYGANCg0KIyMjIExpbXBpZXphIGRlIGxvcyBkYXRvcw0KDQpBIHZlY2VzIG5vcyBmYWx0YXLDoW4gZGF0b3MgZGVudHJvIGRlIG51ZXN0cmFzIGVuY3Vlc3RhcyBvIHJlZ2lzdHJvcy4gVW5hDQpmb3JtYSBkZSBjb250YXJsb3MgbyBpZGVudGlmaWNhcmxvcyBlcyBjb24gbGEgZnVuY2nDs24gaXMubmEgY29uIGxhIHF1ZQ0Kb2J0ZW5kcmVtb3MgdW5hIHJlc3B1ZXN0YSBib29sZWFuYSBxdWUgcG9kZW1vcyBzdW1hciBwdWVzdG8gcXVlIFIgdmEgYQ0KdHJhdGFyIGxvcyB2YWxvcmVzIGzDs2dpY29zIGNvbiBsb3Mgc2lndWllbnRlcyBwdW50YWplczogVFJVRSA9IDEgRkFMU0UgPQ0KMCANCg0KIyMjIyBFamVyY2ljaW8gMyANCg0KQ2FsY3VsYSBsYSBjYW50aWRhZCBkZSByZWdpc3Ryb3MgdmFjw61vcyBkZSBsYSBjb2x1bW5hICJMZWF2ZSINCg0KYGBge3IgTGltcGllemEgZGUgZGF0b3N9DQpzdW0oaXMubmEoYXJjaGl2byRMZWF2ZSkpDQpgYGANCg0KIyMjIE9idGVuaWVuZG8gcHJvcG9yY2lvbmVzDQoNClBhcmEgZXN0ZSBlamVtcGxvLCBhZ3JlZ3VlbW9zIHVuYSBjb2x1bW5hIG51ZXZhIGNvbiBsYSBwcm9wb3JjacOzbiBkZSBsb3MNCnZvdG9zIGVuIGZhdm9yIGRlIGRlamFyIGxhIFVuacOzbiBFdXJvcGVhIChsYSBjb2x1bW5hIExlYXZlKSB5IGxhDQpjYW50aWRhZCB0b3RhbCBkZSB2b3RvcyAoY29sbW5hIE5Wb3RlcykNCg0KYGBge3IgUHJvcG9yY2nDs259DQphcmNoaXZvJFByb3BvcmNpb24gPC0gYXJjaGl2byRMZWF2ZSAvIGFyY2hpdm8kTlZvdGVzDQpgYGANCg0KUGFyYSBwYXNhciBkZSBsb3MgbsO6bWVyb3MgYSBsYXMgcGFsYWJyYXMsIGNyZWVtb3MgY29uIGxhIGZ1bmNpw7NuIGlmZWxzZQ0KdW5hIG51ZXZhIGNvbHVtbmEgcGFyYSBpbmRpY2FyICJEZWphciIgc2kgbGEgcHJvcG9yY2nDs24gZXMgc3VwZXJpb3IgYWwNCjUwJSBvICJQZXJtYW5lY2VyIiBzaSBlcyBtZW5vci4NCg0KIyMjIyBDb250cm9sIGRlIGZsdWpvDQoNCkxhIGluc3RydWnDs24gZGUgaWYgbyBpZmVsc2UgcHVlZGUgc2VyIGFwbGljYWRhLCBjb21vIHRvZG8gZW4gUiwgc29icmUNCmNhZGEgZWxlbWVudG8gZGUgdW4gdmVjdG9yIGRlIG1hbmVyYSBkaXJlY3RhLiBBZGVtw6FzIGRlIGlmLCBlbiBSIHBvZGVtb3MNCnVzYXIgbGEgZnVuY2nDs24gd2hpY2ggcXVlIG5vcyBwZXJtaXRlIHNlbGVjY2lvbmFyIHPDs2xvIGFxdWVsbG9zIGNhbXBvcw0KcXVlIGN1bXBsYW4gdW5hIGNpZXJ0YSBjb25kaWNpw7NuIHkgbWFuZWphIGRlIG1lam9yIG1hbmVyYSBsb3MgZXJyb3JlcyBkZQ0KbG9zIGNhc29zIHZhY8Otb3MuIFBvciBlamVtcGxvLCBlbCBjw7NkaWdvIHNpZ3VpZW50ZSBzZSBkZXNlYSBhc2lnbmFyIGENCmxvcyByZWdpc3Ryb3MgY29uIHVuYSBwcm9wb3JjacOzbiBzdXBlcmlvciBhbCA1MCUgbGEgZXRpcXVldGEgZGUNCiJBYmFuZG9uYXIiIHkgdW5hIGRlICJQZXJtYW5lY2VyIiBhIGFxdWVsbG9zIHJlZ2lzdHJvcyBjb24gdW5hDQpwcm9wb3JjacOzbiBtZW5vciBvIGlndWFsIGFsIDUwJS4NCg0KYGBge3IgQW7DoWxpc2lzIGRlIGxhIHByb3BvcmNpw7NuIDF9DQphcmNoaXZvW2FyY2hpdm8kUHJvcG9yY2lvbiA+IDAuNSwgIlZvdG8iXSA8LSAiQWJhbmRvbmFyIg0KYXJjaGl2b1thcmNoaXZvJFByb3BvcmNpb24gPD0gMC41LCAiVm90byJdIDwtICJQZXJtYW5lY2VyIg0KYGBgDQoNCkVzdGUgZXJyb3Igc3VjZWRlIHBvcnF1ZSBoYXkgcmVuZ2xvbmVzIGNvbiBsYSBldGlxdWV0YSBOQSBkZSBsYQ0KcHJvcG9yY2nDs24sIHB1ZXMgZGVzZGUgc3UgY8OhbGN1bG8gdGVuw61hbiBlbCBlcnJvciBxdWUgZW4gdW5vIGRlIHN1cw0KY2FtcG9zLCBjb250YWJhbiBjb24gTkEuIFVuYSBtYW5lcmEgZGUgcmVzb2x2ZXIgZXN0ZSBlcnJvciBlcyBjb24gW0xhDQpmdW5jacOzbiB3aGljaF0uIENvbiBlc3RhIGZ1bmNpw7NuIGFxdWVsbG9zIGNhbXBvcyBxdWUgY29udGVuZ2FuIHVuYSBOQSBubw0Kc2Vyw6FuIGNvbnNpZGVyYWRvcw0KDQpgYGB7ciBBbsOhbGlzaXMgZGUgbGEgcHJvcG9yY2nDs24gY29uIHdoaWNofQ0KYXJjaGl2b1t3aGljaChhcmNoaXZvJFByb3BvcmNpb24gPiAwLjUpLCAiVm90byJdIDwtICJBYmFuZG9uYXIiDQphcmNoaXZvW3doaWNoKGFyY2hpdm8kUHJvcG9yY2lvbiA8PSAwLjUpLCAiVm90byJdIDwtICJQZXJtYW5lY2VyIg0KYGBgDQoNCkVuIGVzdGEgb2Nhc2nDs24sIGVsIHByb2Nlc28gZXMgbcOhcyBzZW5jaWxsbyBjb24gZWwgdXNvIGRlIGlmZWxzZQ0KDQpgYGB7ciBBbsOhbGlzaXMgZGUgbGEgcHJvcG9yY2nDs24gMn0NCmFyY2hpdm8kVm90byA8LSBpZmVsc2UoYXJjaGl2byRQcm9wb3JjaW9uID4gMC41LCAiRGVqYXIiLCAiUGVybWFuZWNlciIpDQpgYGANCg0KIyMjIEhhY2llbmRvIHVuIHN1YmNvbmp1bnRvIGRlIGxhcyBjb2x1bW5hcyBudW3DqXJpY2FzDQoNClBhcmEgaGFjZXIgdW4gYW7DoWxpc2lzIGdyw6FmaWNvLCB2YW1vcyBhIGV4dHJhZXIgZGUgbnVlc3RybyBhcnJlZ2xvIG9yaWdpbmFsIHPDs2xvIGxhcyBjb2x1bW5hcyBudW3DqXJpY2FzLiBFc3RvIGxvIHBvZGVtb3MgaGFjZXIgZGUgZG9zIG1hbmVyYXMuIE1pc21hcyBxdWUgcG9kcmVtb3MgZXhwbG9yYXIgZW4gZWwgW0VqZXJjaWNpbyA0XSANCg0KIyMjIyBFamVyY2ljaW8gNCANCg0KR2VuZXJhIHVuIGRhdGFmcmFtZSBzw7NsbyBjb24gbGFzIGNvbHVtbmFzIG51bcOpcmljYXMgc2Fsdm8gbGEgZGUgSUQuDQoNCl9Tb2x1Y2nDs24gdXNhbmRvIGZvcl8NCmBgYHtyIFJlc3B1ZXN0YSwgb3BjacOzbiAxfQ0Kc29sb251bWVyb3MxIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAwLCBucm93ID0gbGVuZ3RoKGFyY2hpdm9bLDFdKSkpDQpqIDwtIDENCmZvcihpIGluIDE6bGVuZ3RoKGFyY2hpdm8pKXsNCiAgaWYoaXMubnVtZXJpYyhhcmNoaXZvWyxpXSkpew0KICAgIHNvbG9udW1lcm9zMVssal0gPC0gYXJjaGl2b1ssaV0NCiAgICBqIDwtIGorMQ0KICB9DQp9DQojIHN1bW1hcnkoc29sb251bWVyb3MxKQ0KbGVuZ3RoKHNvbG9udW1lcm9zMSkNCmBgYA0KX1NvbHVjacOzbiB1c2FuZG8gc2FwcGx5Xw0KYGBge3IgUmVzcHVlc3RhLCBvcGNpw7NuIDJ9DQpzb2xvbnVtZXJvczIgPC0gYXJjaGl2b1ssc2FwcGx5KGFyY2hpdm8sIGlzLm51bWVyaWMpXQ0KIyBzdW1tYXJ5KHNvbG9udW1lcm9zMikNCmxlbmd0aChzb2xvbnVtZXJvczIpDQpgYGANCg0KQWhvcmEgYm9ycmVtb3MgZGUgZXN0ZSBudWV2byBjb25qdW50bywgbGEgY29sdW1uYSBkZSBsb3MgaWRlbnRpZmljYWRvcmVzLiBFc3RvIGxvIHBvZGVtb3MgaGFjZXIgY3JlYW5kbyB1biBzdWJzZXQgc8OzbG8gY29uIGxhcyBjb2x1bW5hcyBxdWUgcXVlcmVtb3MgY29uc2VydmFyIG8gYW50ZWNlZGllbmRvIGVsIHNpZ25vIC0gYSB1biB2ZWN0b3IgZGUgw61uZGljZXMuDQoNCmBgYHtyfQ0Kc29sb251bWVyb3MyIDwtIHNvbG9udW1lcm9zMlssIC1jKDEpXQ0KbGVuZ3RoKHNvbG9udW1lcm9zMikNCnN1bW1hcnkoc29sb251bWVyb3MyKQ0KYGBgDQojIyMgR3VhcmRhbmRvIGxvcyByZXN1bWVuZXMNCg0KUGFyYSBubyBjYWxjdWxhciBtw6FzIGRlIHVuYSB2ZXogbGEgbWVkaWEsIG1lZGlhbmEsIHZhbG9yIG3DoXhpbW8geSBkZW3DoXMgdmFsb3JlcyBxdWUgbm9zIHByZXNlbnRhIGxhIGZ1bmNpw7NuIHN1bW1hcnksIHBvZGVtb3MgZ3VhcmRhcmxvcyBlbiB1bmEgbGlzdGEgY29uIFtMYSBmdW5jacOzbiBsYXBwbHldLCBbTGEgZnVuY2nDs24gY2JpbmRdLCBbTGEgZnVuY2nDs24gZG8uY2FsbF0uDQoNCiMjIyMgUmVzdW1lbiBlc3RhZMOtc3RpY28NCg0KYGBge3IgR2VuZXJhY2nDs24gZGUgdW4gbWFyY28gIGNvbiBsb3MgcmVzdW1lbmVzfQ0KVkVzdGFkaXN0aWNvcyA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkoc29sb251bWVyb3MyLCBzdW1tYXJ5KSkNClZpZXcoVkVzdGFkaXN0aWNvcykNCmBgYA0KDQpTaSBkZXNlYW1vcyBxdWUgbGFzIGNvbHVtbmFzIHNlYW4gbGFzIG1lZGlkYXMgZXN0YWTDrXN0aWNhcywgZW50b25jZXMgZGViZW1vcyB1c2FyIHJiaW5kIGVuIGx1Z2FyIGRlIGNiaW5kLg0KYGBge3IgR2VuZXJhY2nDs24gZGUgdW4gbWFyY28gdHJhbnNwdWVzdG99DQpkby5jYWxsKHJiaW5kLCBsYXBwbHkoc29sb251bWVyb3MyLCBzdW1tYXJ5KSkNCmBgYA0KDQojIyMjIFJlc3VtZW4gZXNwZWPDrWZpY28NCg0KYGBge3J9DQpjb2x1bW5hc19kZXNlYWRhcyA8LSBjKCJSZWdpb25OYW1lIiwgIlByb3BvcmNpb24iLCAiQWR1bHRNZWFuQWdlIiwgIk5vUXVhbHMiLCAiTDRRdWFsc19wbHVzIikNCmFyY2hpdm9bd2hpY2gubWF4KGFyY2hpdm8kUHJvcG9yY2lvbiksIGNvbHVtbmFzX2Rlc2VhZGFzXQ0KYXJjaGl2b1t3aGljaC5taW4oYXJjaGl2byRQcm9wb3JjaW9uKSwgY29sdW1uYXNfZGVzZWFkYXNdDQpgYGANCg0KIyMgR3JhZmljYWNpw7NuIGV4cGxvcmF0b3JpYQ0KDQojIyMgR3LDoWZpY2EgZGUgYmFycmFzDQpDb24gbG9zIGRhdG9zIHF1ZSB0ZW5lbW9zIGNyZWFyZW1vcyB1biBoaXN0b2dyYW1hIGVudHJlIGxvcyBjb25kYWRvcyB5IHN1cyBwcm9wb3JjaW9uZXMgY29uIGxhIGZ1bmNpw7NuIGJhcnBsb3QoKQ0KDQpgYGB7ciBDcmVhY2nDs24gZGUgbGEgZ3LDoWZpY2EgZGUgYmFycmFzfQ0KIyBhcmNoaXZvJFJlZ2lvbk5hbWUNCiMgVGFibGUgY3JlYSB1bmEgdGFibGEgY29uIGxvcyBub21icmVzIGRlIGxhcyByZWdpb2VzIHkgbGEgY2FudGlkYWQgZGUgcmVnaXN0cm9zIHF1ZSB0aWVuZW4uDQp0YWJsZShhcmNoaXZvJFJlZ2lvbk5hbWUpDQojIENvbiBwcm9wLnRhYmxlIG9idGVuZHJlbW9zIGxhIHRhYmxhIGFudGVyaW9yIHBlcm8gZW4gcG9yY2VudGFqZXMNCnByb3AudGFibGUodGFibGUoYXJjaGl2byRSZWdpb25OYW1lKSkNCg0KYmFycGxvdCgNCiAgaGVpZ2h0ID0gcHJvcC50YWJsZSh0YWJsZShhcmNoaXZvJFJlZ2lvbk5hbWUpKSwNCiAgbWFpbiA9ICJQcm9wb3JjacOzbiBkZSBsb3Mgdm90b3MgcG9yIHJlZ2nDs24iLA0KICB5bGFiID0gIkZyZWN1ZW5jaWEiLA0KICB4bGFiID0gIlJlZ2nDs24iLA0KICBjb2wgPSAid2hpdGUiDQopDQpgYGANCg0KUGFyYSB2aXN1YWxpemFyIGRlIG1lam9yIG1hbmVyYSBsb3Mgbm9tYnJlcyBkZSBsYXMgcmVnaW9uZXMgcG9kZW1vcyBoYWNlciBsbyBzaWd1aWVudGU6DQpgYGB7ciBNb2RpZmljYWNpw7NuIGRlIGxvcyBub21icmVzfQ0KYXJjaGl2byRSZWdpb25OYW1lIDwtIGFzLmNoYXJhY3RlcihhcmNoaXZvJFJlZ2lvbk5hbWUpDQpSZWdpb25lcyA8LSBmYWN0b3IoYXJjaGl2byRSZWdpb25OYW1lKQ0KY2l1ZGFkZXMgPC0gZGF0YS5mcmFtZSgiTm9tYnJlIiA9IGxldmVscyhSZWdpb25lcyksICJBYnJldmlhdHVyYSIgPSBjKCJFTSIsICJFRSIsICJMIiwgIk5FIiwgIk5XIiwgIlNFIiwgIlNXIiwgIldNIiwgIllIIikpDQpWaWV3KGNpdWRhZGVzKQ0KZm9yIChpIGluIDE6OSkgew0KICBwcmludCgiTm9tYnJlIGNvbXBsZXRvIikNCiAgcHJpbnQoY2l1ZGFkZXNbaSwiTm9tYnJlIl0pDQogIHByaW50KCJBYnJldmlhdHVyYSIpDQogIHByaW50KGNpdWRhZGVzW2ksIkFicmV2aWF0dXJhIl0pDQogIGFyY2hpdm9bd2hpY2goYXJjaGl2byRSZWdpb25OYW1lID09IGNpdWRhZGVzW2ksICJOb21icmUiXSksICJSZWdpb25OYW1lIl0gPC0gY2l1ZGFkZXNbaSwgIkFicmV2aWF0dXJhIl0NCn0NCmBgYA0KDQojIyMgR3LDoWZpY29zIGF2YW56YWRvczogZ2dwbG90MiB5IHZpcmlkaXMNCkFob3JhIGhhcmVtb3MgdW4gZ3LDoWZpY28gcXVlIGNydXplIGxhIHByb3BvcmNpw7NuIHkgbG9zIHZvdG9zIHBhcmEgZGVqYXIgbGEgVW5pw7NuIEV1cm9wZWEgZGUgY2FkYSBjb25kYWRvIGNvbG9yZWFkb3MgcG9yIGxhIHByb3BvcmNpw7NuIGRlIGxhIHBvYmxhY2nDs24gYmxhbmNhIGVuIGVzYSByZWdpw7NuLiANClBhcmEgcmVhbGl6YXIgZXN0YSBncsOhZmljYSBuZWNlc2l0YW1vcyBkZSBkb3MgYmlibGlvdGVjYXM6IGdncGxvdDIgeSB2aXJpZGlzTGl0ZQ0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KVW5hIHZleiBpbnN0YWxhZG9zIGVzdG9zIHBhcXVldGVzOg0KDQpgYGB7cn0NCmdyYWZpY28gPC0gZ2dwbG90KGFyY2hpdm8sIGFlcyh4PVJlZ2lvbk5hbWUsIHk9UHJvcG9yY2lvbiwgY29sb3I9V2hpdGUpKQ0KZ3JhZmljbyA8LSBncmFmaWNvICsgZ2VvbV9wb2ludCgpDQpwcmludChncmFmaWNvKQ0KYGBgDQoNCg0KIyMgTGEgZnVuY2nDs24gd2hpY2ggICANCg0KRXN0YSBmdW5jacOzbiBkZXZvbHZlcsOhIGVsIMOtbmRpY2UgZGVsIHZhbG9yIHF1ZSBzYXRpc2ZhY2UgY2llcnRhDQpjb25kaWNpw7NuLiBFbCBwYXLDoW1ldHJvIGRlIGVudHJhZGEgc2Vyw6EgdW4gdmVjdG9yIGzDs2dpY28uIA0KDQojIyMgRWplbXBsbw0KDQpVc2FuZG8gZWwgdmVjdG9yIHByZWRlZmluaWRvIGRlIGxldHJhcywgZW5jb250cmFyZW1vcyBlbCDDrW5kaWNlIGRlIGxhDQpsZXRyYSBSDQoNCmBgYHtyIEVqZW1wbG8gZGUgd2hpY2ggY29uIHVuIGVzY2FsYXJ9DQpsZXR0ZXJzDQp3aGljaChsZXR0ZXJzID09ICJyIikNCmBgYA0KDQpFbiBlbCBzaWd1aWVudGUgZWplbXBsbyBzZSBjcmVhIHVuIHZlY3RvciBjb24gdW5hIHNlY3VlbmNpYSBxdWUgdmEgZGUgMyBlbiAzLCBkZWwgMSBoYXN0YSBlbCAxNTAuIFBvc3Rlcmlvcm1lbnRlIHNlIGV4dHJhZW4gbG9zIMOtbmRpY2VzIGRlIGxvcyBuw7ptZXJvcyBtYXlvcmVzIGEgNTANCg0KYGBge3IgRWplbXBsbyBkZSB3aGljaCBjb24gdW4gdmVjdG9yfQ0KIyBDcmVhbmRvIHVuIHZlY3RvciBkZSBwcnVlYmENCmRmPC0gc2VxKGZyb209MSwgdG89MTUwLCBieT0zKQ0Kd2hpY2goZGYgPiA1MCkNCmBgYA0KDQojIyMgTWluaWVqZXJjaWNpbyBjb24gd2hpY2gNCg0Kwr9DdcOhbGVzIHNvbiBsb3Mgw61uZGljZXMgZGUgbG9zIG7Dum1lcm9zIHBhcmVzPw0KDQpgYGB7ciBNaW5pZWplcmNpY2lvIGRlIHdoaWNofQ0Kd2hpY2goKGRmICUlIDIgKT09IDApDQpkZlt3aGljaCgoZGYgJSUgMiApPT0gMCldDQpgYGANCg0KIyMgTGEgZnVuY2nDs24gc2FwcGx5DQoNCkxhIGZ1bmNpw7NuIHNhcHBseSBwZXJtaXRlIGl0ZXJhciBzb2JyZSB1bmEgbGlzdGEgbyB2ZWN0b3Igc2luIGxhIG5lY2VzaWRhZCBkZSB1c2FyIGVsIGNpY2xvIGZvciwgcXVlIGVzIGNvbm9jaWRvIHBvciBzZXIgbGVudG8gZW4gUi4NCg0KYGBge3J9DQpzYXBwbHkoMTo0LCBzcXJ0KQ0Kc2FwcGx5KDE6NixmdW5jdGlvbihpKSBpKioyKQ0KYGBgDQoNCkVuIGVsIGVqZW1wbG8gYW50ZXJpb3IgaGVtb3MgdmlzdG8gY29tbyB1c2FyIHNhcHBseSBzb2JyZSB2ZWN0b3JlcywgcG9kZW1vcyB2ZXIgc3UgdXNvIGVuIG1hdHJpY2VzIGRlbnRybyBkZSBsYSBzZWNjacOzbiAiW0hhY2llbmRvIHVuIHN1YmNvbmp1bnRvIGRlIGxhcyBjb2x1bW5hcyBudW3DqXJpY2FzXSINCg0KIyMgTGEgZnVuY2nDs24gbGFwcGx5DQoNCkxhIGZ1bmNpw7NuIGxhcHBseSBhcGxpY2EgdW5hIGZ1bmNpw7NuIGEgdW5hIGxpc3RhIG8gYSB1biB2ZWN0b3IgeSBkZXZ1ZWx2ZSB1bmEgbGlzdGEgZGUgbGEgbWlzbWEgbG9uZ2l0dWQgcXVlIGVsIG9iamV0byBkZSBlbnRyYWRhLg0KDQpgYGB7cn0NCmxhcHBseSgxOjQsIHNxcnQpDQpsYXBwbHkoMTo2LGZ1bmN0aW9uKGkpIGkqKjIpDQpgYGANCg0KIyMgTGEgZnVuY2nDs24gY2JpbmQNCg0KRXN0YSBmdW5jacOzbiAoY29sdW1uLWJpbmQpIHNlIHB1ZWRlIHVzYXIgcGFyYSBhZ3JlZ2FyIHZlY3RvcmVzLCBtYXRyaWNlcyB5IG1hcmNvcyBkZSBkYXRvcyBwb3IgY29sdW1uYSBhIHVuIG51ZXZvIG1hcmNvIGRlIGRhdG9zLg0KDQpgYGB7cn0NCmEgPC0gYygxLDYsOSw4LDMsNSw0KQ0KYiA8LSBjKDgsNiwyLDMsNyw2LDcpDQpjIDwtIGNiaW5kKGEsYikNCmMNCmQgPC0gYygzMiw0MSw3LDExLDM1LDY2LDQ0KQ0KZGYgPC0gZGF0YS5mcmFtZSgiQSIgPSBhLCAiQiIgPSBiLCAiRCIgPSBkKQ0KZGYNCmUgPC0gYygxLDUsOSw2LDQsMyw3OCkNCmRmMiA8LSBjYmluZChkZiwgZSkNCmRmMg0KYGBgDQoNCiMjIExhIGZ1bmNpw7NuIGRvLmNhbGwNCg0KRXN0YSBmdW5jacOzbiBwZXJtaXRlIGFwbGljYXIgdW5hIGNpZXJ0YSBmdW5jacOzbiBhIHVuYSBsaXN0YSBkZSBhcmd1bWVudG9zDQoNCmBgYHtyfQ0KbWlmdW5jaW9uIDwtIGZ1bmN0aW9uKGEsYil7DQogIGEgKyBiDQp9DQptaWxpc3RhIDwtIGxpc3QoYygxLDYsOCksIGMoOSw2LDcpKQ0KZG8uY2FsbChtaWZ1bmNpb24sIG1pbGlzdGEpDQpgYGANCg0KDQojIyBSZWZlcmVuY2lhcw0KDQoxLiBFamVtcGxvIHRyYWR1Y2lkbyB5IGFkYXB0YWRvIGRlbCBsaWJybyAqUiBQcm9ncmFtbWluZyBCeQ0KRXhhbXBsZSBkZSBPbWFyIFRyZWpvIHkgUGV0ZXIgQy4gRmlnbGlvenppKg0KMi4gVGhlIHdoaWNoIGZ1bmN0aW9uIGluIFIgcHJvZ3JhbW1pbmcsIFtKb3VybmFsIERldl0oaHR0cHM6Ly93d3cuam91cm5hbGRldi5jb20vNDUyNzQvd2hpY2gtZnVuY3Rpb24taW4tciM6fjp0ZXh0PVRoZSUyMHdoaWNoJTIwJTI4JTI5JTIwZnVuY3Rpb24lMjBpbiUyMFIlMjByZXR1cm5zJTIwdGhlLGxpa2UlMjByb3dzJTJDJTIwY29sdW1ucyUyMGFuZCUyMGV2ZW4lMjB2ZWN0b3IlMjBhcyUyMHdlbGwpLg0KMy4gTGEgZnVuY2nDs24gc2FwcGx5IGVuIFIsIFtSIENvZGVyXShodHRwczovL3ItY29kZXIuY29tL2Z1bmNpb24tc2FwcGx5LWVuLXIvKQ0KNC4gRnVuY2nDs24gbGFwcGx5IGVuIFIsIFtSIGNvZGVyXShodHRwczovL3ItY29kZXIuY29tL2xhcHBseS1lbi1yLykNCjUuIEhvdyB0byBVc2UgY2JpbmQgaW4gUiAoV2l0aCBFeGFtcGxlcyksIFtTdGF0b2xvZ3ldKGh0dHBzOi8vd3d3LnN0YXRvbG9neS5vcmcvY2JpbmQtaW4tci8pDQo2LiBEby5jYWxsOiBFeGVjdXRlIGEgZnVuY3Rpb24gY2FsbCwgW1JEb2N1bWVudGF0aW9uXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvQmlvY0dlbmVyaWNzL3ZlcnNpb25zLzAuMTguMC90b3BpY3MvZG8uY2FsbCkNCjcuIGdncGxvdDIsIFtnZ3Bsb3QyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pDQo4LiBXaGF0IGlzIHRoZSBkby5jYWxsKCkgZnVuY3Rpb24gaW4gUj8sIFtlZHVjYXRpdmVdKGh0dHBzOi8vd3d3LmVkdWNhdGl2ZS5pby9hbnN3ZXJzL3doYXQtaXMtdGhlLWRvY2FsbC1mdW5jdGlvbi1pbi1yKQ0K