Leer datos csv

Existen muchas funciones distintas para leer datos dependiendo del formato en el que están guardados. Para datos tabulares, la forma más útil es el formato csv, que es un archivo de texto plano con datos separados por coma.

Para importar datos hace falta escribir el código correspondiente pero también podés aprovechar el entorno gráfico de RStudio:

File → Import Dataset → From Text (readr)…

Esto nos va abrir una ventana donde podemos elegir el archivo que queremos importar (en este caso el archivo paises.csv que está dentro de la capeta datos del proyecto) y otros detalles.

Diálogo de importar datos

En la pantalla principal vas a poder previsualizar los datos y ver que pinta tienen. Abajo a la izquierda tenés varias opciones: el nombre que vas a usar para la variable (en este caso llamaremos paises), si la primera fila contiene los nombres de las columnas (First Row as Names), qué delimitador tienen los datos (en este caso comma, pero podría ser punto y coma u otro), etc…

Y abajo a la derecha es el código que vas a necesitar para efectivamente importar los datos. Podrías apretar el botón “Import” para leer los datos pero si bien es posible, al mismo tiempo esas líneas de código no se guardan en ningún lado y entonces nuestro trabajo luego no se puede reproducir desde un script. Por eso, te proponemos que copies ese código, cierres esa ventana con el botón “Cancel”, y pegues el código en el archivo donde estés trabajando. Cuando lo ejecutes, se va a generar la variable paises con los datos.

library(readr)
paises <- read_csv("datos/paises.csv")
## Parsed with column specification:
## cols(
##   pais = col_character(),
##   continente = col_character(),
##   anio = col_double(),
##   esperanza_de_vida = col_double(),
##   poblacion = col_double(),
##   pib_per_capita = col_double()
## )

Nota: Notar que en este caso el código para leer los datos consta de dos líneas. La primera carga el paquete readr y el segundo usa la función read_csv() (del paquete readr) para leer el archivo .csv. No es necesario cargar el paquete cada vez que vas a leer un archivo, pero asegurate de incluir esta línea al comienzo de tu archivo.

Nota: La interfaz de usuario de RStudio sirve para autogenerar el código que lee el archivo. Una vez que lo tenés, no necesitás abrirla de nuevo.

Todo ese texto naranja/rojo es intimidante pero no te preocupes, es sólo un mensaje que nos informa que los datos se leyeron y qué tipo de dato tiene cada columna. Podemos explorar la estructura de la variable paises usando la función str() (de structure en inglés).

str(paises)
## tibble [1,704 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ pais             : chr [1:1704] "Afganistán" "Afganistán" "Afganistán" "Afganistán" ...
##  $ continente       : chr [1:1704] "Asia" "Asia" "Asia" "Asia" ...
##  $ anio             : num [1:1704] 1952 1957 1962 1967 1972 ...
##  $ esperanza_de_vida: num [1:1704] 28.8 30.3 32 34 36.1 ...
##  $ poblacion        : num [1:1704] 8425333 9240934 10267083 11537966 13079460 ...
##  $ pib_per_capita   : num [1:1704] 779 821 853 836 740 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   pais = col_character(),
##   ..   continente = col_character(),
##   ..   anio = col_double(),
##   ..   esperanza_de_vida = col_double(),
##   ..   poblacion = col_double(),
##   ..   pib_per_capita = col_double()
##   .. )

Esto nos dice un montón. La primera línea dice que es una tibble, que es un caso especial de la estructura de datos tabular básica de R llamada data.frame. Tiene 1704 filas (las observaciones) y 6 columnas (o variables que describen las observaciones). Las siguientes líneas nos dicen los nombres de las columnas (“pais”, “continente”, “anio”, “esperanza_de_vida”, “poblacion” y “pbi_per_capita”), su tipo de dato (chr o num), la longitud ([1:1704]) y sus primeros elementos. Cada columna de la tabla paises es un vector. Dado que los vectores son como los “ladrillos” que hacen a los data.frames, merecen una atención especial.

Vectores

Un vector en R es esencialmente una lista ordenada de cosas, con la condición especial de que todos los elementos en un vector tienen que ser del mismo tipo de datos básico.

R tiene 5 tipos de datos básicos:

Existen otros pero son formas un poco más sofisticadas de estos tipos básicos. Por ejemplo, el formato POSIXct (de tiempo) se guarda como doble, representando la cantidad de segundos pasados desde el 01/01/1970.

Una forma conveniente de crear un vector es con la función c() (de combinar ).

x <- c(1, 2, 3, 4)
x
## [1] 1 2 3 4

x es una serie con 4 elementos. Si usamos la función str():

str(x)
##  num [1:4] 1 2 3 4

Esta salida nos está informando que x es de tipo numérico (un nombre para dobles y enteros). Los vectores sólo pueden ser de un solo tipo. Por lo tanto, si intentás combinar un caracter con números, R va a tratar de “coercer” u obligar todo los elementos a que sean de un tipo común:

x <- c("uno", 2)
str(x)
##  chr [1:2] "uno" "2"

Ahora el vector x es de tipo caracter y el número 2 se convirtió en el caracter “2”. Este tipo de conversión automática puede ser fuente de muchos dolores de cabeza! Por ejemplo, ¿qué creés que va a pasar si tratás de sumarle 1 a la variable x?

x + 1
## Error in x + 1: non-numeric argument to binary operator

¡Un error! ¿Cuál es el problema? El mensaje de error nos da una pista usamos un “argumento no numérico”. R puede hacer 2 + 1, pero "2" + 1 no significa nada!

Las reglas de coerción son: logical -> integer -> numeric -> complex -> character, donde -> se puede leer como se transforma en. Para forzar la coerción en contra de esta cadena podés usar las funciones as....

as.numeric(x)
## Warning: NAs introduced by coercion
## [1] NA  2

¡Pero no siempre funciona! Recordá que x tiene los valores “uno” y “2”. R pudo convertir “2” en 2, pero no sabe cómo transformar “uno” en un elemento numérico. Lo que devuelve es un vector que en el lugar de “2” tiene 2, pero en el lugar de “uno” tiene NA, que es un tipo de elemento especial que representa un valor faltante. Pero R avisa, así que no es traidor: el warning advierte que la coerción introdujo valores faltantes.

Vectorización

La mayoría de las funciones de R pueden trabajar sobre vectores y lo hacen elemento a elemento. Esto significa que si creás estos dos vectores:

x <- 1:4   # 1:4 crea un vector que empieza en 1 y termina en 4,
y <- 6:9
x
## [1] 1 2 3 4
y
## [1] 6 7 8 9

y los sumás

x + y
## [1]  7  9 11 13

el resultado es un vector de la misma longitud de x e y que en cada posición tiene la suma de cada elemento de x e y

x:  1  2  3  4
    +  +  +  +
y:  6  7  8  9
---------------
    7  9 11 13

Lo mismo sucede al multiplicar vectores

x*y
## [1]  6 14 24 36
x:  1   2   3   4
    *   *   *   *
y:  6   7   8   9
-----------------
    6  14  24  36

Si los vectores no tiene la misma longitud, los elementos del más corto se “reciclan”.

y <- c(6, 7, 8)
x * y
## Warning in x * y: longer object length is not a multiple of shorter object
## length
## [1]  6 14 24 24
x:  1   2   3   4
    *   *   *   *
y:  6   7   8   6
-----------------
    6  14  24  24

R emite una advertencia sólo si la longitud del vector largo no es múltiplo de la longitud del vector corto. Casi siempre el reciclado de vectores es señal de que algo salió mal. La única excepción es cuando el vector corto es de longitud 1. Por ejemplo, es totalmente razonable usar x * 2 para duplicar los elementos de x.

Leer datos de excel

Si tenés la vista avispada, habrás notado que en el menú de “Import Dataset” hay una opción para leer datos de Excel. En efecto, RStudio provee la misma ayuda para leer este tipo de datos:

File → Import Dataset → From Excel…

Notá que entre las opciones de abajo a la izquierda aparecen dos variables importantes. Podés seleccionar de qué hoja leer los datos y qué rango usar. Esto seguro que te va a ser muy útil para esos archivos de Excel con múltiples tablas en un archivo, o incluso múltiples tablas en cada hoja!

En este caso paises.xlsx es un Excel buena onda, y el código para leer los datos es muy simple:

library(readxl)
paises <- read_excel("datos/paises.xlsx")

Con la función str() podés confirmar que los datos leídos son los mismos que para el csv.

str(paises)
## tibble [1,704 × 6] (S3: tbl_df/tbl/data.frame)
##  $ pais             : chr [1:1704] "Afganistán" "Afganistán" "Afganistán" "Afganistán" ...
##  $ continente       : chr [1:1704] "Asia" "Asia" "Asia" "Asia" ...
##  $ anio             : num [1:1704] 1952 1957 1962 1967 1972 ...
##  $ esperanza_de_vida: num [1:1704] 28.8 30.3 32 34 36.1 ...
##  $ poblacion        : num [1:1704] 8425333 9240934 10267083 11537966 13079460 ...
##  $ pib_per_capita   : num [1:1704] 779 821 853 836 740 ...

Desafío: Lee un archivo

  1. Lee el archivo paises.xlsx, pero solo las primeras 30 lineas
  2. ¿Qué cambió en código que devuelve RStudio?
  3. Revisa la documentación de la función read_excel() para identificar otros argumentos que puedan resultarte útiles.

Formatos de tablas

Pensá un momento en la estructura de los datos de paises. Tiene seis columnas, pero sólo tres de ellas contienen valores observados (esperanza_de_vida, poblacion y pib_per_capita) y las otras tres son las “coordenadas”: pais, continente y anio son columnas que identifican la observación. De hecho, la columna pais tiene un montón de datos repetidos!

Esto es un ejemplo de formato “largo” (o “tidy” en inglés). La idea básica de datos “largos” es que:

La tabla paises es bastante larga (tiene 1704 filas y 6 columnas!) pero podría ser más larga aún

paises_largo <- read_csv("datos/paises_largo.csv")
str(paises_largo)
## tibble [5,112 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ pais      : chr [1:5112] "Afganistán" "Afganistán" "Afganistán" "Afganistán" ...
##  $ continente: chr [1:5112] "Asia" "Asia" "Asia" "Asia" ...
##  $ anio      : num [1:5112] 1952 1957 1962 1967 1972 ...
##  $ variable  : chr [1:5112] "esperanza_de_vida" "esperanza_de_vida" "esperanza_de_vida" "esperanza_de_vida" ...
##  $ valor     : num [1:5112] 28.8 30.3 32 34 36.1 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   pais = col_character(),
##   ..   continente = col_character(),
##   ..   anio = col_double(),
##   ..   variable = col_character(),
##   ..   valor = col_double()
##   .. )

La tabla paises_largo tiene ¡5112 filas! Tiene un valor para cada país, año y variable. Es decir, las tres columnas que contenían valores numéricos (esperanza_de_vida, poblacion y pib_per_capita), en esta tabla son una sola columna llamada “valor”.

Pero también pueden ser podría ser más “ancha”:

paises_ancho <- read_csv("datos/paises_ancho.csv")
str(paises_ancho)
## tibble [142 × 38] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ continente            : chr [1:142] "Africa" "Africa" "Africa" "Africa" ...
##  $ pais                  : chr [1:142] "Algeria" "Angola" "Benin" "Botswana" ...
##  $ pib_per_capita_1952   : num [1:142] 2449 3521 1063 851 543 ...
##  $ pib_per_capita_1957   : num [1:142] 3014 3828 960 918 617 ...
##  $ pib_per_capita_1962   : num [1:142] 2551 4269 949 984 723 ...
##  $ pib_per_capita_1967   : num [1:142] 3247 5523 1036 1215 795 ...
##  $ pib_per_capita_1972   : num [1:142] 4183 5473 1086 2264 855 ...
##  $ pib_per_capita_1977   : num [1:142] 4910 3009 1029 3215 743 ...
##  $ pib_per_capita_1982   : num [1:142] 5745 2757 1278 4551 807 ...
##  $ pib_per_capita_1987   : num [1:142] 5681 2430 1226 6206 912 ...
##  $ pib_per_capita_1992   : num [1:142] 5023 2628 1191 7954 932 ...
##  $ pib_per_capita_1997   : num [1:142] 4797 2277 1233 8647 946 ...
##  $ pib_per_capita_2002   : num [1:142] 5288 2773 1373 11004 1038 ...
##  $ pib_per_capita_2007   : num [1:142] 6223 4797 1441 12570 1217 ...
##  $ esperanza_de_vida_1952: num [1:142] 43.1 30 38.2 47.6 32 ...
##  $ esperanza_de_vida_1957: num [1:142] 45.7 32 40.4 49.6 34.9 ...
##  $ esperanza_de_vida_1962: num [1:142] 48.3 34 42.6 51.5 37.8 ...
##  $ esperanza_de_vida_1967: num [1:142] 51.4 36 44.9 53.3 40.7 ...
##  $ esperanza_de_vida_1972: num [1:142] 54.5 37.9 47 56 43.6 ...
##  $ esperanza_de_vida_1977: num [1:142] 58 39.5 49.2 59.3 46.1 ...
##  $ esperanza_de_vida_1982: num [1:142] 61.4 39.9 50.9 61.5 48.1 ...
##  $ esperanza_de_vida_1987: num [1:142] 65.8 39.9 52.3 63.6 49.6 ...
##  $ esperanza_de_vida_1992: num [1:142] 67.7 40.6 53.9 62.7 50.3 ...
##  $ esperanza_de_vida_1997: num [1:142] 69.2 41 54.8 52.6 50.3 ...
##  $ esperanza_de_vida_2002: num [1:142] 71 41 54.4 46.6 50.6 ...
##  $ esperanza_de_vida_2007: num [1:142] 72.3 42.7 56.7 50.7 52.3 ...
##  $ poblacion_1952        : num [1:142] 9279525 4232095 1738315 442308 4469979 ...
##  $ poblacion_1957        : num [1:142] 10270856 4561361 1925173 474639 4713416 ...
##  $ poblacion_1962        : num [1:142] 11000948 4826015 2151895 512764 4919632 ...
##  $ poblacion_1967        : num [1:142] 12760499 5247469 2427334 553541 5127935 ...
##  $ poblacion_1972        : num [1:142] 14760787 5894858 2761407 619351 5433886 ...
##  $ poblacion_1977        : num [1:142] 17152804 6162675 3168267 781472 5889574 ...
##  $ poblacion_1982        : num [1:142] 20033753 7016384 3641603 970347 6634596 ...
##  $ poblacion_1987        : num [1:142] 23254956 7874230 4243788 1151184 7586551 ...
##  $ poblacion_1992        : num [1:142] 26298373 8735988 4981671 1342614 8878303 ...
##  $ poblacion_1997        : num [1:142] 29072015 9875024 6066080 1536536 10352843 ...
##  $ poblacion_2002        : num [1:142] 31287142 10866106 7026113 1630347 12251209 ...
##  $ poblacion_2007        : num [1:142] 33333216 12420476 8078314 1639131 14326203 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   continente = col_character(),
##   ..   pais = col_character(),
##   ..   pib_per_capita_1952 = col_double(),
##   ..   pib_per_capita_1957 = col_double(),
##   ..   pib_per_capita_1962 = col_double(),
##   ..   pib_per_capita_1967 = col_double(),
##   ..   pib_per_capita_1972 = col_double(),
##   ..   pib_per_capita_1977 = col_double(),
##   ..   pib_per_capita_1982 = col_double(),
##   ..   pib_per_capita_1987 = col_double(),
##   ..   pib_per_capita_1992 = col_double(),
##   ..   pib_per_capita_1997 = col_double(),
##   ..   pib_per_capita_2002 = col_double(),
##   ..   pib_per_capita_2007 = col_double(),
##   ..   esperanza_de_vida_1952 = col_double(),
##   ..   esperanza_de_vida_1957 = col_double(),
##   ..   esperanza_de_vida_1962 = col_double(),
##   ..   esperanza_de_vida_1967 = col_double(),
##   ..   esperanza_de_vida_1972 = col_double(),
##   ..   esperanza_de_vida_1977 = col_double(),
##   ..   esperanza_de_vida_1982 = col_double(),
##   ..   esperanza_de_vida_1987 = col_double(),
##   ..   esperanza_de_vida_1992 = col_double(),
##   ..   esperanza_de_vida_1997 = col_double(),
##   ..   esperanza_de_vida_2002 = col_double(),
##   ..   esperanza_de_vida_2007 = col_double(),
##   ..   poblacion_1952 = col_double(),
##   ..   poblacion_1957 = col_double(),
##   ..   poblacion_1962 = col_double(),
##   ..   poblacion_1967 = col_double(),
##   ..   poblacion_1972 = col_double(),
##   ..   poblacion_1977 = col_double(),
##   ..   poblacion_1982 = col_double(),
##   ..   poblacion_1987 = col_double(),
##   ..   poblacion_1992 = col_double(),
##   ..   poblacion_1997 = col_double(),
##   ..   poblacion_2002 = col_double(),
##   ..   poblacion_2007 = col_double()
##   .. )

paises_ancho tiene una sola fila para cada país, y los datos de cada variable y año está en una columna propia.

Ninguna estructura de datos es “la correcta”; distintos análisis y distintas funciones se llevan mejor con los datos en distinto “grado de longitud”. Es muy normal empezar con datos largos, tener que pasarlos a anchos para hacer un análisis y luego volver a pasar a largos (tan normal que hay un paquete de R que encapsula ese proceso).

LS0tCnRpdGxlOiAiTGVjdHVyYSBkZSBkYXRvcyBvcmRlbmFkb3MiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiBmYWxzZQogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIAotLS0KCgojIyBMZWVyIGRhdG9zIGNzdgoKRXhpc3RlbiBtdWNoYXMgZnVuY2lvbmVzIGRpc3RpbnRhcyBwYXJhIGxlZXIgZGF0b3MgZGVwZW5kaWVuZG8gZGVsIGZvcm1hdG8gZW4gZWwgcXVlIGVzdMOhbiBndWFyZGFkb3MuIFBhcmEgZGF0b3MgdGFidWxhcmVzLCBsYSBmb3JtYSBtw6FzIMO6dGlsIGVzIGVsIGZvcm1hdG8gY3N2LCBxdWUgZXMgdW4gYXJjaGl2byBkZSB0ZXh0byBwbGFubyBjb24gZGF0b3Mgc2VwYXJhZG9zIHBvciBjb21hLiAKClBhcmEgaW1wb3J0YXIgZGF0b3MgaGFjZSBmYWx0YSBlc2NyaWJpciBlbCBjw7NkaWdvIGNvcnJlc3BvbmRpZW50ZSBwZXJvIHRhbWJpw6luIHBvZMOpcyBhcHJvdmVjaGFyIGVsIGVudG9ybm8gZ3LDoWZpY28gZGUgUlN0dWRpbzoKCjo6OiB7LmFsZXJ0IC5hbGVydC1zZWNvbmRhcnl9CkZpbGUg4oaSIEltcG9ydCBEYXRhc2V0IOKGkiBGcm9tIFRleHQgKHJlYWRyKS4uLgo6OjoKCkVzdG8gbm9zIHZhIGFicmlyIHVuYSB2ZW50YW5hIGRvbmRlIHBvZGVtb3MgZWxlZ2lyIGVsIGFyY2hpdm8gcXVlIHF1ZXJlbW9zIGltcG9ydGFyIChlbiBlc3RlIGNhc28gZWwgYXJjaGl2byBgcGFpc2VzLmNzdmAgcXVlIGVzdMOhIGRlbnRybyBkZSBsYSBjYXBldGEgYGRhdG9zYCBkZWwgcHJveWVjdG8pIHkgb3Ryb3MgZGV0YWxsZXMuCgohW0Rpw6Fsb2dvIGRlIGltcG9ydGFyIGRhdG9zXShpbWcvaW1wb3J0YXItcGFpc2VzLnBuZykKCkVuIGxhIHBhbnRhbGxhIHByaW5jaXBhbCB2YXMgYSBwb2RlciBwcmV2aXN1YWxpemFyIGxvcyBkYXRvcyB5IHZlciBxdWUgcGludGEgdGllbmVuLiBBYmFqbyBhIGxhIGl6cXVpZXJkYSB0ZW7DqXMgdmFyaWFzIG9wY2lvbmVzOiBlbCBub21icmUgcXVlIHZhcyBhIHVzYXIgcGFyYSBsYSB2YXJpYWJsZSAoZW4gZXN0ZSBjYXNvIGxsYW1hcmVtb3MgYHBhaXNlc2ApLCBzaSBsYSBwcmltZXJhIGZpbGEgY29udGllbmUgbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIChgRmlyc3QgUm93IGFzIE5hbWVzYCksIHF1w6kgZGVsaW1pdGFkb3IgdGllbmVuIGxvcyBkYXRvcyAoZW4gZXN0ZSBjYXNvIGBjb21tYWAsIHBlcm8gcG9kcsOtYSBzZXIgcHVudG8geSBjb21hIHUgb3RybyksIGV0Yy4uLiAKClkgYWJham8gYSBsYSBkZXJlY2hhIGVzIGVsIGPDs2RpZ28gcXVlIHZhcyBhIG5lY2VzaXRhciBwYXJhIGVmZWN0aXZhbWVudGUgaW1wb3J0YXIgbG9zIGRhdG9zLiBQb2Ryw61hcyBhcHJldGFyIGVsIGJvdMOzbiAiSW1wb3J0IiBwYXJhIGxlZXIgbG9zIGRhdG9zIHBlcm8gc2kgYmllbiBlcyBwb3NpYmxlLCBhbCBtaXNtbyB0aWVtcG8gZXNhcyBsw61uZWFzIGRlIGPDs2RpZ28gbm8gc2UgZ3VhcmRhbiBlbiBuaW5nw7puIGxhZG8geSBlbnRvbmNlcyBudWVzdHJvIHRyYWJham8gbHVlZ28gbm8gc2UgcHVlZGUgcmVwcm9kdWNpciBkZXNkZSB1biBzY3JpcHQuIFBvciBlc28sIHRlIHByb3BvbmVtb3MgcXVlIGNvcGllcyBlc2UgY8OzZGlnbywgY2llcnJlcyBlc2EgdmVudGFuYSBjb24gZWwgYm90w7NuICJDYW5jZWwiLCB5IHBlZ3VlcyBlbCBjw7NkaWdvIGVuIGVsIGFyY2hpdm8gZG9uZGUgZXN0w6lzIHRyYWJhamFuZG8uIEN1YW5kbyBsbyBlamVjdXRlcywgc2UgdmEgYSBnZW5lcmFyIGxhIHZhcmlhYmxlIGBwYWlzZXNgIGNvbiBsb3MgZGF0b3MuICAKCgpgYGB7cn0KbGlicmFyeShyZWFkcikKcGFpc2VzIDwtIHJlYWRfY3N2KCJkYXRvcy9wYWlzZXMuY3N2IikKYGBgCgo6Ojogey5hbGVydCAuYWxlcnQtc3VjY2Vzc30KKipOb3RhKio6IE5vdGFyIHF1ZSBlbiBlc3RlIGNhc28gZWwgY8OzZGlnbyBwYXJhIGxlZXIgbG9zIGRhdG9zIGNvbnN0YSBkZSBkb3MgbMOtbmVhcy4gTGEgcHJpbWVyYSBjYXJnYSBlbCBwYXF1ZXRlIGByZWFkcmAgeSBlbCBzZWd1bmRvIHVzYSBsYSBmdW5jacOzbiBgcmVhZF9jc3YoKWAgKGRlbCBwYXF1ZXRlIHJlYWRyKSBwYXJhIGxlZXIgZWwgYXJjaGl2byAuY3N2LgpObyBlcyBuZWNlc2FyaW8gY2FyZ2FyIGVsIHBhcXVldGUgY2FkYSB2ZXogcXVlIHZhcyBhIGxlZXIgdW4gYXJjaGl2bywgcGVybyBhc2VndXJhdGUgZGUgaW5jbHVpciBlc3RhIGzDrW5lYSBhbCBjb21pZW56byBkZSB0dSBhcmNoaXZvLgo6OjoKCjo6OiB7LmFsZXJ0IC5hbGVydC1zdWNjZXNzfQoqKk5vdGEqKjogTGEgaW50ZXJmYXogZGUgdXN1YXJpbyBkZSBSU3R1ZGlvIHNpcnZlIHBhcmEgYXV0b2dlbmVyYXIgZWwgY8OzZGlnbyBxdWUgbGVlIGVsIGFyY2hpdm8uIFVuYSB2ZXogcXVlIGxvIHRlbsOpcywgbm8gbmVjZXNpdMOhcyBhYnJpcmxhIGRlIG51ZXZvLiAKOjo6CgpUb2RvIGVzZSB0ZXh0byBuYXJhbmphL3Jvam8gZXMgaW50aW1pZGFudGUgcGVybyBubyB0ZSBwcmVvY3VwZXMsIGVzIHPDs2xvIHVuIG1lbnNhamUgcXVlIG5vcyBpbmZvcm1hIHF1ZSBsb3MgZGF0b3Mgc2UgbGV5ZXJvbiB5IHF1w6kgdGlwbyBkZSBkYXRvIHRpZW5lIGNhZGEgY29sdW1uYS4gUG9kZW1vcyBleHBsb3JhciBsYSBlc3RydWN0dXJhIGRlIGxhIHZhcmlhYmxlIGBwYWlzZXNgIHVzYW5kbyBsYSBmdW5jacOzbiBgc3RyKClgIChkZSAqc3RydWN0dXJlKiBlbiBpbmdsw6lzKS4gCgpgYGB7cn0Kc3RyKHBhaXNlcykKYGBgCgpFc3RvIG5vcyBkaWNlIHVuIG1vbnTDs24uIExhIHByaW1lcmEgbMOtbmVhIGRpY2UgcXVlIGVzIHVuYSBgdGliYmxlYCwgcXVlIGVzIHVuIGNhc28gZXNwZWNpYWwgZGUgbGEgZXN0cnVjdHVyYSBkZSBkYXRvcyB0YWJ1bGFyIGLDoXNpY2EgZGUgUiBsbGFtYWRhIGBkYXRhLmZyYW1lYC4gVGllbmUgMTcwNCBmaWxhcyAobGFzICoqb2JzZXJ2YWNpb25lcyoqKSB5IDYgY29sdW1uYXMgKG8gKip2YXJpYWJsZXMqKiBxdWUgZGVzY3JpYmVuIGxhcyBvYnNlcnZhY2lvbmVzKS4gTGFzIHNpZ3VpZW50ZXMgbMOtbmVhcyBub3MgZGljZW4gbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzICgicGFpcyIsICJjb250aW5lbnRlIiwgImFuaW8iLCAiZXNwZXJhbnphX2RlX3ZpZGEiLCAicG9ibGFjaW9uIiB5ICJwYmlfcGVyX2NhcGl0YSIpLCBzdSB0aXBvIGRlIGRhdG8gKGBjaHJgIG8gYG51bWApLCBsYSBsb25naXR1ZCAoYFsxOjE3MDRdYCkgeSBzdXMgcHJpbWVyb3MgZWxlbWVudG9zLiBDYWRhIGNvbHVtbmEgZGUgbGEgdGFibGEgYHBhaXNlc2AgZXMgdW4gKip2ZWN0b3IqKi4gRGFkbyBxdWUgbG9zIHZlY3RvcmVzIHNvbiBjb21vIGxvcyAibGFkcmlsbG9zIiBxdWUgaGFjZW4gYSBsb3MgZGF0YS5mcmFtZXMsIG1lcmVjZW4gdW5hIGF0ZW5jacOzbiBlc3BlY2lhbC4KCgojIyBWZWN0b3JlcwoKVW4gdmVjdG9yIGVuIFIgZXMgZXNlbmNpYWxtZW50ZSB1bmEgbGlzdGEgb3JkZW5hZGEgZGUgY29zYXMsIGNvbiBsYSBjb25kaWNpw7NuIGVzcGVjaWFsIGRlIHF1ZSB0b2RvcyBsb3MgZWxlbWVudG9zIGVuIHVuIHZlY3RvciB0aWVuZW4gcXVlIHNlciBkZWwgbWlzbW8gdGlwbyBkZSBkYXRvcyBiw6FzaWNvLgoKUiB0aWVuZSA1IHRpcG9zIGRlIGRhdG9zIGLDoXNpY29zOiAKCiogYGRvdWJsZWAgKGRvYmxlKTogbsO6bWVyb3MgcmVhbGVzLCBwb3IgZWplbXBsbyAzLjE0MTU5IG8gMS41CiogYGludGVnZXJgIChlbnRlcm8pOiBuw7ptZXJvcyBlbnRlcm9zLCBjb21vIDEgbyA1CiogYGNvbXBsZXhgIChjb21wbGVqbyk6IG7Dum1lcm9zIGNvbXBsZWpvcywgcG9yIGVqZW1wbG8gaSArIDIKKiBgbG9naWNhbGAgKGzDs2dpY28pOiB2ZXJkYWRlcm8gYFRSVUVgIG8gYEZBTFNFYAoqIGBjaGFyYWN0ZXJgIChjYXJhY3Rlcik6IGNhZGVuYSBkZSBjYXJhY3RlcmVzLCBjb21vICJob2xhIFIhIgoKRXhpc3RlbiBvdHJvcyBwZXJvIHNvbiBmb3JtYXMgdW4gcG9jbyBtw6FzIHNvZmlzdGljYWRhcyBkZSBlc3RvcyB0aXBvcyBiw6FzaWNvcy4gUG9yIGVqZW1wbG8sIGVsIGZvcm1hdG8gUE9TSVhjdCAoZGUgdGllbXBvKSBzZSBndWFyZGEgY29tbyBkb2JsZSwgcmVwcmVzZW50YW5kbyBsYSBjYW50aWRhZCBkZSBzZWd1bmRvcyBwYXNhZG9zIGRlc2RlIGVsIDAxLzAxLzE5NzAuIAoKVW5hIGZvcm1hIGNvbnZlbmllbnRlIGRlIGNyZWFyIHVuIHZlY3RvciBlcyBjb24gbGEgZnVuY2nDs24gYGMoKWAgKGRlICpjb21iaW5hciAqKS4KCmBgYHtyfQp4IDwtIGMoMSwgMiwgMywgNCkKeApgYGAKCmB4YCBlcyB1bmEgc2VyaWUgY29uIDQgZWxlbWVudG9zLiBTaSB1c2Ftb3MgbGEgZnVuY2nDs24gYHN0cigpYDoKCmBgYHtyfQpzdHIoeCkKYGBgCgpFc3RhIHNhbGlkYSBub3MgZXN0w6EgaW5mb3JtYW5kbyBxdWUgYHhgIGVzIGRlIHRpcG8gbnVtw6lyaWNvICh1biBub21icmUgcGFyYSBkb2JsZXMgeSBlbnRlcm9zKS4gTG9zIHZlY3RvcmVzIHPDs2xvIHB1ZWRlbiBzZXIgZGUgdW4gc29sbyB0aXBvLiBQb3IgbG8gdGFudG8sIHNpIGludGVudMOhcyBjb21iaW5hciB1biBjYXJhY3RlciBjb24gbsO6bWVyb3MsIFIgdmEgYSB0cmF0YXIgZGUgImNvZXJjZXIiIHUgb2JsaWdhciB0b2RvIGxvcyBlbGVtZW50b3MgYSBxdWUgc2VhbiBkZSB1biB0aXBvIGNvbcO6bjoKCmBgYHtyfQp4IDwtIGMoInVubyIsIDIpCnN0cih4KQpgYGAKCkFob3JhIGVsIHZlY3RvciBgeGAgZXMgZGUgdGlwbyBjYXJhY3RlciB5IGVsIG7Dum1lcm8gMiBzZSBjb252aXJ0acOzIGVuIGVsIGNhcmFjdGVyICIyIi4gRXN0ZSB0aXBvIGRlIGNvbnZlcnNpw7NuIGF1dG9tw6F0aWNhIHB1ZWRlIHNlciBmdWVudGUgZGUgbXVjaG9zIGRvbG9yZXMgZGUgY2FiZXphISBQb3IgZWplbXBsbywgwr9xdcOpIGNyZcOpcyBxdWUgdmEgYSBwYXNhciBzaSB0cmF0w6FzIGRlIHN1bWFybGUgMSBhIGxhIHZhcmlhYmxlIHg/CgpgYGB7ciwgZXJyb3IgPSBUUlVFfQp4ICsgMQpgYGAKCsKhVW4gZXJyb3IhIMK/Q3XDoWwgZXMgZWwgcHJvYmxlbWE/IEVsIG1lbnNhamUgZGUgZXJyb3Igbm9zIGRhIHVuYSBwaXN0YSB1c2Ftb3MgdW4gImFyZ3VtZW50byBubyBudW3DqXJpY28iLiBSIHB1ZWRlIGhhY2VyIGAyICsgMWAsIHBlcm8gYCIyIiArIDFgIG5vIHNpZ25pZmljYSBuYWRhISAKCkxhcyByZWdsYXMgZGUgY29lcmNpw7NuIHNvbjogbG9naWNhbCAtPiBpbnRlZ2VyIC0+IG51bWVyaWMgLT4gY29tcGxleCAtPiBjaGFyYWN0ZXIsIGRvbmRlIC0+IHNlIHB1ZWRlIGxlZXIgY29tbyBzZSB0cmFuc2Zvcm1hIGVuLiBQYXJhIGZvcnphciBsYSBjb2VyY2nDs24gZW4gY29udHJhIGRlIGVzdGEgY2FkZW5hIHBvZMOpcyB1c2FyIGxhcyBmdW5jaW9uZXMgYGFzLi4uYC4gCgpgYGB7cn0KYXMubnVtZXJpYyh4KQpgYGAKCsKhUGVybyBubyBzaWVtcHJlIGZ1bmNpb25hISBSZWNvcmTDoSBxdWUgYHhgIHRpZW5lIGxvcyB2YWxvcmVzICJ1bm8iIHkgIjIiLiBSIHB1ZG8gY29udmVydGlyICIyIiBlbiAyLCBwZXJvIG5vIHNhYmUgY8OzbW8gdHJhbnNmb3JtYXIgInVubyIgZW4gdW4gZWxlbWVudG8gbnVtw6lyaWNvLiBMbyBxdWUgZGV2dWVsdmUgZXMgdW4gdmVjdG9yIHF1ZSBlbiBlbCBsdWdhciBkZSAiMiIgdGllbmUgMiwgcGVybyBlbiBlbCBsdWdhciBkZSAidW5vIiB0aWVuZSBgTkFgLCBxdWUgZXMgdW4gdGlwbyBkZSBlbGVtZW50byBlc3BlY2lhbCBxdWUgcmVwcmVzZW50YSB1biB2YWxvciBmYWx0YW50ZS4gUGVybyBSIGF2aXNhLCBhc8OtIHF1ZSBubyBlcyB0cmFpZG9yOiBlbCB3YXJuaW5nIGFkdmllcnRlIHF1ZSBsYSBjb2VyY2nDs24gaW50cm9kdWpvIHZhbG9yZXMgZmFsdGFudGVzLgoKCiMjIyBWZWN0b3JpemFjacOzbgoKTGEgbWF5b3LDrWEgZGUgbGFzIGZ1bmNpb25lcyBkZSBSIHB1ZWRlbiB0cmFiYWphciBzb2JyZSB2ZWN0b3JlcyB5IGxvIGhhY2VuICoqZWxlbWVudG8gYSBlbGVtZW50byoqLiBFc3RvIHNpZ25pZmljYSBxdWUgc2kgY3Jlw6FzIGVzdG9zIGRvcyB2ZWN0b3JlczoKCmBgYHtyfQp4IDwtIDE6NCAgICMgMTo0IGNyZWEgdW4gdmVjdG9yIHF1ZSBlbXBpZXphIGVuIDEgeSB0ZXJtaW5hIGVuIDQsCnkgPC0gNjo5CmBgYAoKYGBge3J9CngKYGBgCmBgYHtyfQp5CmBgYAoKeSBsb3Mgc3Vtw6FzCgpgYGB7cn0KeCArIHkKYGBgCgplbCByZXN1bHRhZG8gZXMgdW4gdmVjdG9yIGRlIGxhIG1pc21hIGxvbmdpdHVkIGRlIGB4YCBlIGB5YCBxdWUgZW4gY2FkYSBwb3NpY2nDs24gdGllbmUgbGEgc3VtYSBkZSBjYWRhIGVsZW1lbnRvIGRlIGB4YCBlIGB5YAoKYGBgCng6ICAxICAyICAzICA0CiAgICArICArICArICArCnk6ICA2ICA3ICA4ICA5Ci0tLS0tLS0tLS0tLS0tLQogICAgNyAgOSAxMSAxMwpgYGAKCkxvIG1pc21vIHN1Y2VkZSBhbCBtdWx0aXBsaWNhciB2ZWN0b3JlcwoKYGBge3J9CngqeQpgYGAKYGBgCng6ICAxICAgMiAgIDMgICA0CiAgICAqICAgKiAgICogICAqCnk6ICA2ICAgNyAgIDggICA5Ci0tLS0tLS0tLS0tLS0tLS0tCiAgICA2ICAxNCAgMjQgIDM2CmBgYAoKU2kgbG9zIHZlY3RvcmVzIG5vIHRpZW5lIGxhIG1pc21hIGxvbmdpdHVkLCBsb3MgZWxlbWVudG9zIGRlbCBtw6FzIGNvcnRvIHNlICJyZWNpY2xhbiIuCgpgYGB7cn0KeSA8LSBjKDYsIDcsIDgpCnggKiB5CmBgYApgYGAKeDogIDEgICAyICAgMyAgIDQKICAgICogICAqICAgKiAgICoKeTogIDYgICA3ICAgOCAgIDYKLS0tLS0tLS0tLS0tLS0tLS0KICAgIDYgIDE0ICAyNCAgMjQKYGBgCgpSIGVtaXRlIHVuYSBhZHZlcnRlbmNpYSAqKnPDs2xvIHNpIGxhIGxvbmdpdHVkIGRlbCB2ZWN0b3IgbGFyZ28gbm8gZXMgbcO6bHRpcGxvIGRlIGxhIGxvbmdpdHVkIGRlbCB2ZWN0b3IgY29ydG8qKi4gQ2FzaSBzaWVtcHJlIGVsIHJlY2ljbGFkbyBkZSB2ZWN0b3JlcyBlcyBzZcOxYWwgZGUgcXVlIGFsZ28gc2FsacOzIG1hbC4gTGEgw7puaWNhIGV4Y2VwY2nDs24gZXMgY3VhbmRvIGVsIHZlY3RvciBjb3J0byBlcyBkZSBsb25naXR1ZCAxLiBQb3IgZWplbXBsbywgZXMgdG90YWxtZW50ZSByYXpvbmFibGUgdXNhciBgeCAqIDJgIHBhcmEgZHVwbGljYXIgbG9zIGVsZW1lbnRvcyBkZSB4LiAKCgojIyBMZWVyIGRhdG9zIGRlIGV4Y2VsCgpTaSB0ZW7DqXMgbGEgdmlzdGEgYXZpc3BhZGEsIGhhYnLDoXMgbm90YWRvIHF1ZSBlbiBlbCBtZW7DuiBkZSAiSW1wb3J0IERhdGFzZXQiIGhheSB1bmEgb3BjacOzbiBwYXJhIGxlZXIgZGF0b3MgZGUgRXhjZWwuIEVuIGVmZWN0bywgUlN0dWRpbyBwcm92ZWUgbGEgbWlzbWEgYXl1ZGEgcGFyYSBsZWVyIGVzdGUgdGlwbyBkZSBkYXRvczoKCjo6OiB7LmFsZXJ0IC5hbGVydC1zZWNvbmRhcnl9CkZpbGUg4oaSIEltcG9ydCBEYXRhc2V0IOKGkiBGcm9tIEV4Y2VsLi4uCjo6OgoKIVtdKGltZy9pbXBvcnRhci1wYWlzZXMteGwucG5nKQoKTm90w6EgcXVlIGVudHJlIGxhcyBvcGNpb25lcyBkZSBhYmFqbyBhIGxhIGl6cXVpZXJkYSBhcGFyZWNlbiBkb3MgdmFyaWFibGVzIGltcG9ydGFudGVzLiBQb2TDqXMgc2VsZWNjaW9uYXIgZGUgcXXDqSBob2phIGxlZXIgbG9zIGRhdG9zIHkgcXXDqSByYW5nbyB1c2FyLiBFc3RvIHNlZ3VybyBxdWUgdGUgdmEgYSBzZXIgbXV5IMO6dGlsIHBhcmEgZXNvcyBhcmNoaXZvcyBkZSBFeGNlbCBjb24gbcO6bHRpcGxlcyB0YWJsYXMgZW4gdW4gYXJjaGl2bywgbyBpbmNsdXNvIG3Dumx0aXBsZXMgdGFibGFzIGVuIGNhZGEgaG9qYSEKCkVuIGVzdGUgY2FzbyBwYWlzZXMueGxzeCBlcyB1biBFeGNlbCBidWVuYSBvbmRhLCB5IGVsIGPDs2RpZ28gcGFyYSBsZWVyIGxvcyBkYXRvcyBlcyBtdXkgc2ltcGxlOgoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQpwYWlzZXMgPC0gcmVhZF9leGNlbCgiZGF0b3MvcGFpc2VzLnhsc3giKQpgYGAKCkNvbiBsYSBmdW5jacOzbiBgc3RyKClgIHBvZMOpcyBjb25maXJtYXIgcXVlIGxvcyBkYXRvcyBsZcOtZG9zIHNvbiBsb3MgbWlzbW9zIHF1ZSBwYXJhIGVsIGNzdi4KCmBgYHtyfQpzdHIocGFpc2VzKQpgYGAKCjo6OiB7LmFsZXJ0IC5hbGVydC1pbmZvfQoqKkRlc2Fmw61vOiBMZWUgdW4gYXJjaGl2byoqCgoxLiBMZWUgZWwgYXJjaGl2byBgcGFpc2VzLnhsc3hgLCBwZXJvIHNvbG8gbGFzIHByaW1lcmFzIDMwIGxpbmVhcwoyLiDCv1F1w6kgY2FtYmnDsyBlbiBjw7NkaWdvIHF1ZSBkZXZ1ZWx2ZSBSU3R1ZGlvPyAKMy4gUmV2aXNhIGxhIGRvY3VtZW50YWNpw7NuIGRlIGxhIGZ1bmNpw7NuIGByZWFkX2V4Y2VsKClgIHBhcmEgaWRlbnRpZmljYXIgb3Ryb3MgYXJndW1lbnRvcyBxdWUgcHVlZGFuIHJlc3VsdGFydGUgw7p0aWxlcy4KOjo6CgoKIyMgRm9ybWF0b3MgZGUgdGFibGFzCgpQZW5zw6EgdW4gbW9tZW50byBlbiBsYSBlc3RydWN0dXJhIGRlIGxvcyBkYXRvcyBkZSBgcGFpc2VzYC4gVGllbmUgc2VpcyBjb2x1bW5hcywgcGVybyBzw7NsbyB0cmVzIGRlIGVsbGFzIGNvbnRpZW5lbiB2YWxvcmVzIG9ic2VydmFkb3MgKGBlc3BlcmFuemFfZGVfdmlkYWAsIGBwb2JsYWNpb25gIHkgYHBpYl9wZXJfY2FwaXRhYCkgeSBsYXMgb3RyYXMgdHJlcyBzb24gbGFzICJjb29yZGVuYWRhcyI6IGBwYWlzYCwgYGNvbnRpbmVudGVgIHkgYGFuaW9gIHNvbiBjb2x1bW5hcyBxdWUgaWRlbnRpZmljYW4gbGEgb2JzZXJ2YWNpw7NuLiBEZSBoZWNobywgbGEgY29sdW1uYSBgcGFpc2AgdGllbmUgdW4gbW9udMOzbiBkZSBkYXRvcyByZXBldGlkb3MhIAoKRXN0byBlcyB1biBlamVtcGxvIGRlIGZvcm1hdG8gImxhcmdvIiAobyAidGlkeSIgZW4gaW5nbMOpcykuIExhIGlkZWEgYsOhc2ljYSBkZSBkYXRvcyAibGFyZ29zIiBlcyBxdWU6IAoKKiBjYWRhIGZpbGEgZXMgdW5hIG9ic2VydmFjacOzbgoqIGNhZGEgY29sdW1uYSBlcyB1bmEgdmFyaWFibGUKCkxhIHRhYmxhIGBwYWlzZXNgIGVzIGJhc3RhbnRlIGxhcmdhICh0aWVuZSAxNzA0IGZpbGFzIHkgNiBjb2x1bW5hcyEpIHBlcm8gcG9kcsOtYSBzZXIgbcOhcyBsYXJnYSBhw7puCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnBhaXNlc19sYXJnbyA8LSByZWFkX2NzdigiZGF0b3MvcGFpc2VzX2xhcmdvLmNzdiIpCnN0cihwYWlzZXNfbGFyZ28pCmBgYAoKTGEgdGFibGEgYHBhaXNlc19sYXJnb2AgdGllbmUgwqE1MTEyIGZpbGFzISBUaWVuZSB1biB2YWxvciBwYXJhIGNhZGEgcGHDrXMsIGHDsW8geSB2YXJpYWJsZS4gRXMgZGVjaXIsIGxhcyB0cmVzIGNvbHVtbmFzIHF1ZSBjb250ZW7DrWFuIHZhbG9yZXMgbnVtw6lyaWNvcyAoYGVzcGVyYW56YV9kZV92aWRhYCwgYHBvYmxhY2lvbmAgeSBgcGliX3Blcl9jYXBpdGFgKSwgZW4gZXN0YSB0YWJsYSBzb24gdW5hIHNvbGEgY29sdW1uYSBsbGFtYWRhICJ2YWxvciIuIAoKUGVybyB0YW1iacOpbiBwdWVkZW4gc2VyIHBvZHLDrWEgc2VyIG3DoXMgImFuY2hhIjoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpwYWlzZXNfYW5jaG8gPC0gcmVhZF9jc3YoImRhdG9zL3BhaXNlc19hbmNoby5jc3YiKQpzdHIocGFpc2VzX2FuY2hvKQpgYGAKCmBwYWlzZXNfYW5jaG9gIHRpZW5lIHVuYSBzb2xhIGZpbGEgcGFyYSBjYWRhIHBhw61zLCB5IGxvcyBkYXRvcyBkZSBjYWRhIHZhcmlhYmxlIHkgYcOxbyBlc3TDoSBlbiB1bmEgY29sdW1uYSBwcm9waWEuIAoKTmluZ3VuYSBlc3RydWN0dXJhIGRlIGRhdG9zIGVzICJsYSBjb3JyZWN0YSI7IGRpc3RpbnRvcyBhbsOhbGlzaXMgeSBkaXN0aW50YXMgZnVuY2lvbmVzIHNlIGxsZXZhbiBtZWpvciBjb24gbG9zIGRhdG9zIGVuIGRpc3RpbnRvICJncmFkbyBkZSBsb25naXR1ZCIuIEVzIG11eSBub3JtYWwgZW1wZXphciBjb24gZGF0b3MgbGFyZ29zLCB0ZW5lciBxdWUgcGFzYXJsb3MgYSBhbmNob3MgcGFyYSBoYWNlciB1biBhbsOhbGlzaXMgeSBsdWVnbyB2b2x2ZXIgYSBwYXNhciBhIGxhcmdvcyAodGFuIG5vcm1hbCBxdWUgaGF5IFt1biBwYXF1ZXRlIGRlIFIgcXVlIGVuY2Fwc3VsYSBlc2UgcHJvY2Vzb10oaHR0cHM6Ly9naXRodWIuY29tL2RncnR3by93aWR5cikpLiAKCgo8ZGl2IGNsYXNzPSJidG4tZ3JvdXAiIHJvbGU9Imdyb3VwIiBhcmlhLWxhYmVsPSJOYXZlZ2FjacOzbiI+CiAgPGEgaHJlZj0gIjAzLXJlcG9ydGVzLUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5BbnRlcmlvcjwvYT4KICA8YSBocmVmPSAiMDUtZHBseXItSS5odG1sIiBjbGFzcyA9ICJidG4gYnRuLXByaW1hcnkiPlNpZ3VpZW50ZTwvYT4KPC9kaXY+