jfulponi commited on
Commit
8bc9552
·
1 Parent(s): 683b647

Upload 45 files

Browse files
Files changed (46) hide show
  1. .gitattributes +1 -0
  2. Dockerfile +42 -0
  3. R/.DS_Store +0 -0
  4. R/.RData +3 -0
  5. R/.Rhistory +6 -0
  6. R/.Rproj.user/E43CA360/pcs/files-pane.pper +9 -0
  7. R/.Rproj.user/E43CA360/pcs/source-pane.pper +3 -0
  8. R/.Rproj.user/E43CA360/pcs/windowlayoutstate.pper +14 -0
  9. R/.Rproj.user/E43CA360/pcs/workbench-pane.pper +5 -0
  10. R/.Rproj.user/E43CA360/rmd-outputs +10 -0
  11. R/.Rproj.user/E43CA360/saved_source_markers +1 -0
  12. R/.Rproj.user/E43CA360/sources/per/t/21382A5A +26 -0
  13. R/.Rproj.user/E43CA360/sources/per/t/21382A5A-contents +199 -0
  14. R/.Rproj.user/E43CA360/sources/per/t/7215F060 +26 -0
  15. R/.Rproj.user/E43CA360/sources/per/t/7215F060-contents +42 -0
  16. R/.Rproj.user/E43CA360/sources/per/t/74244C1E +27 -0
  17. R/.Rproj.user/E43CA360/sources/per/t/74244C1E-contents +158 -0
  18. R/.Rproj.user/E43CA360/sources/per/t/EB610EE9 +26 -0
  19. R/.Rproj.user/E43CA360/sources/per/t/EB610EE9-contents +1 -0
  20. R/.Rproj.user/E43CA360/sources/per/t/FDAA923B +28 -0
  21. R/.Rproj.user/E43CA360/sources/per/t/FDAA923B-contents +523 -0
  22. R/.Rproj.user/E43CA360/sources/prop/79CB3A6C +7 -0
  23. R/.Rproj.user/E43CA360/sources/prop/7DEF3E17 +6 -0
  24. R/.Rproj.user/E43CA360/sources/prop/B3A3F0F6 +6 -0
  25. R/.Rproj.user/E43CA360/sources/prop/D2B7D434 +6 -0
  26. R/.Rproj.user/E43CA360/sources/prop/E88E6722 +8 -0
  27. R/.Rproj.user/E43CA360/sources/prop/INDEX +5 -0
  28. R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/E43CA360937CAF11/chunks.json +1 -0
  29. R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/s/chunks.json +1 -0
  30. R/.Rproj.user/shared/notebooks/AE375184-github/1/E43CA360937CAF11/chunks.json +1 -0
  31. R/.Rproj.user/shared/notebooks/AE375184-github/1/s/chunks.json +1 -0
  32. R/.Rproj.user/shared/notebooks/patch-chunk-names +0 -0
  33. R/.Rproj.user/shared/notebooks/paths +2 -0
  34. R/.data_def_v5.RData.icloud +0 -0
  35. R/Dash BID CAF.Rproj +13 -0
  36. R/bid.png +0 -0
  37. R/caf.png +0 -0
  38. R/dash_v2.Rmd +516 -0
  39. R/data/.DS_Store +0 -0
  40. R/data/.data_def_v5.RData.icloud +0 -0
  41. R/flujograma.png +0 -0
  42. R/github.Rmd +158 -0
  43. R/github.md +199 -0
  44. R/github_files/figure-gfm/pressure-1.png +0 -0
  45. R/global.R +1 -0
  46. R/omu.png +0 -0
.gitattributes CHANGED
@@ -32,3 +32,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ R/.RData filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM rocker/shiny:latest
2
+
3
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \
4
+ libgdal-dev \
5
+ libproj-dev \
6
+ libgeos-dev \
7
+ default-libmysqlclient-dev \
8
+ libmysqlclient-dev \
9
+ libudunits2-dev \
10
+ netcdf-bin \
11
+ libxml2-dev \
12
+ libcairo2-dev \
13
+ libsqlite3-dev \
14
+ # libmariadbd-dev \
15
+ libpq-dev \
16
+ libssh2-1-dev \
17
+ unixodbc-dev \
18
+ libcurl4-openssl-dev \
19
+ libssl-dev
20
+
21
+ RUN apt-get install pandoc
22
+
23
+ RUN apt-get update && \
24
+ apt-get upgrade -y && \
25
+ apt-get clean
26
+
27
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse','flexdashboard', 'highcharter', 'leaflet', 'sf', 'ggalluvial', 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
28
+
29
+ RUN R -e "install.packages(pkgs=c('devtools'), repos='https://cran.rstudio.com/')"
30
+
31
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
32
+
33
+
34
+ RUN mkdir /root/app
35
+
36
+ COPY R /root/shiny_save
37
+
38
+ EXPOSE 3838
39
+
40
+ # run app
41
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd', shiny_args=list(host='0.0.0.0', port=3838))"]
42
+
R/.DS_Store ADDED
Binary file (6.15 kB). View file
 
R/.RData ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bd29f7629805f70aacee0888f94ddf67c8c41dd48158991ebe2e623e7239c45e
3
+ size 21106130
R/.Rhistory ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ load("dash_data_v17.RData")
2
+ distancias_medias["city"]
3
+ input$city_sel == FALSE)
4
+ input$city_sel == FALSE
5
+ remotes::install_github("JohnCoene/firebase")
6
+ devtools::install_github('rstudio/leaflet')
R/.Rproj.user/E43CA360/pcs/files-pane.pper ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "sortOrder": [
3
+ {
4
+ "columnIndex": 2,
5
+ "ascending": true
6
+ }
7
+ ],
8
+ "path": "~/Desktop/docker/R"
9
+ }
R/.Rproj.user/E43CA360/pcs/source-pane.pper ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "activeTab": 2
3
+ }
R/.Rproj.user/E43CA360/pcs/windowlayoutstate.pper ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "left": {
3
+ "splitterpos": 327,
4
+ "topwindowstate": "NORMAL",
5
+ "panelheight": 780,
6
+ "windowheight": 818
7
+ },
8
+ "right": {
9
+ "splitterpos": 490,
10
+ "topwindowstate": "NORMAL",
11
+ "panelheight": 780,
12
+ "windowheight": 818
13
+ }
14
+ }
R/.Rproj.user/E43CA360/pcs/workbench-pane.pper ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "TabSet1": 0,
3
+ "TabSet2": 0,
4
+ "TabZoom": {}
5
+ }
R/.Rproj.user/E43CA360/rmd-outputs ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /private/var/folders/nx/8wln4st11wn5q689p5s4lzwr0000gn/T/RtmpbRUrYS/preview-44f41ff77ab3.html
3
+ /private/var/folders/nx/8wln4st11wn5q689p5s4lzwr0000gn/T/RtmpbRUrYS/preview-43f61d793f60.html
4
+ /private/var/folders/nx/8wln4st11wn5q689p5s4lzwr0000gn/T/RtmpbRUrYS/preview-441519b1c75b.html
5
+ /private/var/folders/nx/8wln4st11wn5q689p5s4lzwr0000gn/T/RtmpbRUrYS/preview-4486538c10bc.html
6
+
7
+
8
+
9
+
10
+
R/.Rproj.user/E43CA360/saved_source_markers ADDED
@@ -0,0 +1 @@
 
 
1
+ {"active_set":"","sets":[]}
R/.Rproj.user/E43CA360/sources/per/t/21382A5A ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "21382A5A",
3
+ "path": "~/Desktop/docker/R/github.md",
4
+ "project_path": "github.md",
5
+ "type": "markdown",
6
+ "hash": "2534523926",
7
+ "contents": "",
8
+ "dirty": false,
9
+ "created": 1654631295448.0,
10
+ "source_on_save": false,
11
+ "relative_order": 5,
12
+ "properties": {
13
+ "source_window_id": "",
14
+ "Source": "Source",
15
+ "cursorPosition": "199,0",
16
+ "scrollLine": "183"
17
+ },
18
+ "folds": "",
19
+ "lastKnownWriteTime": 1654631250,
20
+ "encoding": "UTF-8",
21
+ "collab_server": "",
22
+ "source_window": "",
23
+ "last_content_update": 1654631250,
24
+ "read_only": false,
25
+ "read_only_alternatives": []
26
+ }
R/.Rproj.user/E43CA360/sources/per/t/21382A5A-contents ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Dockerizando Shiny
2
+ ================
3
+
4
+ ## Introducción
5
+
6
+ Muchas veces, la versión estándar de los servidores gratuitos de Shiny
7
+ no alcanzan para nuestras hermosas creaciones en R. La limitación que
8
+ más me molestaba era la de las 5 instancias activas simultáneamente.
9
+ Para entornos de test quizá no estaba mal, pero una vez que la
10
+ aplicación llega a ámbitos más profesionales donde probablemente más
11
+ personas utilicen la app al mismo tiempo es más difícil.
12
+
13
+ En este breve tutorial llevaremos una apliación de Shiny a un
14
+ repositorio Docker y de allí mismo haremos un deploy a un servicio de
15
+ Google Cloud con más RAM y CPUs disponibles.
16
+
17
+ ## Ingredientes
18
+
19
+ - Una aplicación Shiny, .Rmd, flexdashboard (como en este ejemplo),
20
+ etc.
21
+ - Docker y una cuenta Docker para subir la imagen a un repositorio.
22
+ - Cuenta en Google Cloud y las APIs básicas activas (el proceso es
23
+ bastante intuitivo).
24
+
25
+ ## Instalando Docker
26
+
27
+ Para instalar Docker, se obtiene el archivo desde la página oficial y se
28
+ instala normalmente. En OSX, como es mi caso, después de descargar el
29
+ .dmg se instala de la siguiente manera desde la terminal:
30
+
31
+ ``` bash
32
+ sudo hdiutil attach Docker.dmg
33
+ sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
34
+ sudo hdiutil detach /Volumes/Docker
35
+ ```
36
+
37
+ ## Creando el Dockerfile
38
+
39
+ El archivo Dockerfile es un conjunto de líneas de texto que configuran
40
+ los paquetes que Docker va a utilizar para generar el entorno de nuestra
41
+ aplicación y es donde también le daremos “órdenes” para ejecutar la app.
42
+
43
+ En mi caso, creé una carpeta que se llama “Docker” con dos elementos:
44
+ una carpeta llamada “R” donde está todo lo relacionado a la aplicación
45
+ Shiny y el propio archivo “Dockerfile” (así, sin puntos, ni extensiones,
46
+ ni cosas raras).
47
+
48
+ En mi caso, el archivo Dockerfile contiene las siguientes líneas,
49
+ comentadas punto a punto para entender mejor:
50
+
51
+ ``` bash
52
+ FROM rocker/shiny:latest # Se instala el entorno Shiny y R
53
+
54
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \ # Se instalan librerías necesarias
55
+ libgdal-dev \ # Ojo que probablemente necesites más o menos
56
+ libproj-dev \ # depende de los paquetes que uses
57
+ libgeos-dev \
58
+ default-libmysqlclient-dev \
59
+ libmysqlclient-dev \
60
+ libudunits2-dev \
61
+ netcdf-bin \
62
+ libxml2-dev \
63
+ libcairo2-dev \
64
+ libsqlite3-dev \
65
+ libpq-dev \
66
+ libssh2-1-dev \
67
+ unixodbc-dev \
68
+ libcurl4-openssl-dev \
69
+ libssl-dev
70
+
71
+ RUN apt-get install pandoc # Tenía problemas con Pandoc e instalarlo por separado lo solucionó
72
+
73
+ RUN apt-get update && \ # Actualizar paquetes y limpiar
74
+ apt-get upgrade -y && \
75
+ apt-get clean
76
+
77
+ # Se instalan los paquetes necesarios
78
+ # notar que ya estamos ejecutando COMANDOS DE R dentro del Docker
79
+
80
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse',
81
+ 'flexdashboard', 'leaflet', 'sf', 'ggalluvial',
82
+ 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
83
+
84
+ RUN R -e "install.packages(pkgs=c('devtools'),
85
+ repos='https://cran.rstudio.com/')"
86
+
87
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
88
+
89
+ RUN mkdir /root/app # Creamos la carpeta de la app
90
+
91
+ COPY R /root/shiny_save # Copia la carpeta "R" a la carpeta shiny_save
92
+
93
+ EXPOSE 3838 #Importantísimo: asignarle un puerto de comunicación a la app.
94
+ #Después no vamos a poder hacer nada si no se expone el puerto
95
+
96
+ # Se corre la app (en este caso, al ser un flexdashboard
97
+ # tengo que ejecutarlo con rmarkdown, si no sería shiny::runApp(...)).
98
+ # Como argumento extra le paso el puerto abierto.
99
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd',
100
+ shiny_args=list(host='0.0.0.0', port=3838))"]
101
+ ```
102
+
103
+ ## Construcción de la imagen
104
+
105
+ Una vez que tengamos el dockerfile y la carpeta con todos los archivos
106
+ que necesita la app para correr, pasamos a construir la imagen de Docker
107
+ propiamente dicha. Para esto, abrimos la consola y ejecutamos:
108
+
109
+ ``` bash
110
+ docker login
111
+ ```
112
+
113
+ Nos pedirá el usuario y la contraseña de Docker. La escribimos y luego:
114
+
115
+ ``` bash
116
+ docker build -t nombreapp .
117
+ ```
118
+
119
+ Ese comando construirá una imagen llamada “nombreapp” con los parámetros
120
+ del dockerfile (para eso el punto al final). Esta imagen contiene el
121
+ entorno R y todo el código que hemos usado, dentro de un sistema
122
+ Linux.
123
+ Es por esto que probablemente tarde bastante en ejecutarse la primera
124
+ vez (a mí me lleva aproximadamente 3 horas), obviamente dependiendo la
125
+ complejidad de la app. Si hacemos modificaciones al código de R de la
126
+ aplicación y pusheamos los cambios (ejecutando los mismos parámetros),
127
+ la construcción tarda muchísimo menos ya que la mayoría de las capas del
128
+ entorno son las mismas y no se modifican. Esto es algo que me encanta de
129
+ Docker. El trabajo se divide en “capas”, por lo que si corregís una
130
+ parte pequeña del código, el build y el push de la app tardan segundos.
131
+
132
+ ``` bash
133
+ docker tag nombreapp usuario/nombreapp:latest
134
+ ```
135
+
136
+ “Tagueamos” la app dentro del repositorio público que estamos por crear.
137
+ Es como decir que lo estamos “seleccionando” para subirlo. El
138
+ repositorio se llamará usuario/nombreapp. La última parte “:latest” es
139
+ un nombre que podemos poner para identificar a la última versión de la
140
+ app. Lo podemos ir modificando para cada versión.
141
+
142
+ ``` bash
143
+ docker push usuario/nombreapp:latest
144
+ ```
145
+
146
+ Se pushea la imagen al repositorio, tal como si fuera GitHub.
147
+
148
+ ## Deploy en Google Cloud
149
+
150
+ Una vez creado el proyecto, procedemos a abrir la consola de comandos
151
+ dentro de la misma Web de Google. Lo primero que debemos hacer es
152
+ loguearnos con nuestras credenciales de Docker.
153
+
154
+ ``` bash
155
+ docker login
156
+ ```
157
+
158
+ Posteriormente, “traemos” la imagen del Docker a nuestro proyecto de
159
+ Google Cloud con los últimos cambios.
160
+
161
+ ``` bash
162
+ docker pull usuario/nombreapp:latest
163
+ ```
164
+
165
+ Luego, análogamente a lo que hacíamos en Docker, se “taguea” la imagen a
166
+ pushear, lo único es que esta vez no pusheamos al repo de Docker sino al
167
+ container de imágenes de Google Cloud. Reemplazá PROJECT_ID por el ID de
168
+ tu proyecto.
169
+
170
+ ``` bash
171
+ docker tag usuario/nombreapp:latest gcr.io/PROJECT_ID/usuario/nombreapp:latest
172
+ ```
173
+
174
+ Por último, pusheamos la imagen.
175
+
176
+ ``` bash
177
+ docker push gcr.io/PROJECT_ID/usuario/nombreapp:latest
178
+ ```
179
+
180
+ ## Creación de la instancia del servicio
181
+
182
+ Ya casi terminamos. Luego de pusheada la imagen, en la pestaña de Google
183
+ Cloud Run clickeás “Crear Servicio”. Esto nos habilitará una máquina
184
+ virtual para hacer el deploy y que podamos compartir la app a un montón
185
+ de gente.
186
+
187
+ Donde dice “Url de la imagen” poné seleccionar y se van a desplegar las
188
+ opciones de imagen. Nos va a aparecer la imagen “nombreapp” con la
189
+ versión “latest”. Usamos esa y completamos los parámetros con lo que
190
+ necesite el proyecto.
191
+
192
+ **IMPORTANTE:** En la parte de más abajo donde dice “CONTENEDOR”,
193
+ completá dentro del campo “Puerto del contenedor” el puerto 3838 o el
194
+ que hayas elegido en la construcción del Dockerfile, ya que sin esto
195
+ Google va a enrutar mal el puerto generando errores.
196
+
197
+ Esperás unos segundos a que Google cree la instancia y listo! Ya está la
198
+ app deployada en Google Cloud Run. Cualquiera va a poder acceder a ella
199
+ a través de la URL que te muestra arriba del panel.
R/.Rproj.user/E43CA360/sources/per/t/7215F060 ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "7215F060",
3
+ "path": "~/Desktop/docker/Dockerfile",
4
+ "project_path": null,
5
+ "type": "dockerfile",
6
+ "hash": "393467562",
7
+ "contents": "",
8
+ "dirty": false,
9
+ "created": 1654610279179.0,
10
+ "source_on_save": false,
11
+ "relative_order": 3,
12
+ "properties": {
13
+ "source_window_id": "",
14
+ "Source": "Source",
15
+ "cursorPosition": "0,0",
16
+ "scrollLine": "0"
17
+ },
18
+ "folds": "",
19
+ "lastKnownWriteTime": 1654610286,
20
+ "encoding": "UTF-8",
21
+ "collab_server": "",
22
+ "source_window": "",
23
+ "last_content_update": 1654610286,
24
+ "read_only": false,
25
+ "read_only_alternatives": []
26
+ }
R/.Rproj.user/E43CA360/sources/per/t/7215F060-contents ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM rocker/shiny:latest
2
+
3
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \
4
+ libgdal-dev \
5
+ libproj-dev \
6
+ libgeos-dev \
7
+ default-libmysqlclient-dev \
8
+ libmysqlclient-dev \
9
+ libudunits2-dev \
10
+ netcdf-bin \
11
+ libxml2-dev \
12
+ libcairo2-dev \
13
+ libsqlite3-dev \
14
+ # libmariadbd-dev \
15
+ libpq-dev \
16
+ libssh2-1-dev \
17
+ unixodbc-dev \
18
+ libcurl4-openssl-dev \
19
+ libssl-dev
20
+
21
+ RUN apt-get install pandoc
22
+
23
+ RUN apt-get update && \
24
+ apt-get upgrade -y && \
25
+ apt-get clean
26
+
27
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse','flexdashboard', 'leaflet', 'sf', 'ggalluvial', 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
28
+
29
+ RUN R -e "install.packages(pkgs=c('devtools'), repos='https://cran.rstudio.com/')"
30
+
31
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
32
+
33
+
34
+ RUN mkdir /root/app
35
+
36
+ COPY R /root/shiny_save
37
+
38
+ EXPOSE 3838
39
+
40
+ # run app
41
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd', shiny_args=list(host='0.0.0.0', port=3838))"]
42
+
R/.Rproj.user/E43CA360/sources/per/t/74244C1E ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "74244C1E",
3
+ "path": "~/Desktop/docker/R/github.Rmd",
4
+ "project_path": "github.Rmd",
5
+ "type": "r_markdown",
6
+ "hash": "1314225375",
7
+ "contents": "",
8
+ "dirty": false,
9
+ "created": 1654628306321.0,
10
+ "source_on_save": false,
11
+ "relative_order": 4,
12
+ "properties": {
13
+ "tempName": "Untitled1",
14
+ "source_window_id": "",
15
+ "Source": "Source",
16
+ "cursorPosition": "143,22",
17
+ "scrollLine": "119"
18
+ },
19
+ "folds": "",
20
+ "lastKnownWriteTime": 1654631249,
21
+ "encoding": "UTF-8",
22
+ "collab_server": "",
23
+ "source_window": "",
24
+ "last_content_update": 1654631249388,
25
+ "read_only": false,
26
+ "read_only_alternatives": []
27
+ }
R/.Rproj.user/E43CA360/sources/per/t/74244C1E-contents ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Dockerizando Shiny"
3
+ output: github_document
4
+ ---
5
+
6
+ ```{r setup, include=FALSE}
7
+ knitr::opts_chunk$set(echo = TRUE)
8
+ ```
9
+
10
+ ## Introducción
11
+
12
+ Muchas veces, la versión estándar de los servidores gratuitos de Shiny no alcanzan para nuestras hermosas creaciones en R. La limitación que más me molestaba era la de las 5 instancias activas simultáneamente. Para entornos de test quizá no estaba mal, pero una vez que la aplicación llega a ámbitos más profesionales donde probablemente más personas utilicen la app al mismo tiempo es más difícil.
13
+
14
+ En este breve tutorial llevaremos una apliación de Shiny a un repositorio Docker y de allí mismo haremos un deploy a un servicio de Google Cloud con más RAM y CPUs disponibles.
15
+
16
+ ## Ingredientes
17
+
18
+ - Una aplicación Shiny, .Rmd, flexdashboard (como en este ejemplo), etc.
19
+ - Docker y una cuenta Docker para subir la imagen a un repositorio.
20
+ - Cuenta en Google Cloud y las APIs básicas activas (el proceso es bastante intuitivo).
21
+
22
+ ## Instalando Docker
23
+
24
+ Para instalar Docker, se obtiene el archivo desde la página oficial y se instala normalmente. En OSX, como es mi caso, después de descargar el .dmg se instala de la siguiente manera desde la terminal:
25
+
26
+ ```{bash, eval = FALSE}
27
+ sudo hdiutil attach Docker.dmg
28
+ sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
29
+ sudo hdiutil detach /Volumes/Docker
30
+ ```
31
+
32
+ ## Creando el Dockerfile
33
+
34
+ El archivo Dockerfile es un conjunto de líneas de texto que configuran los paquetes que Docker va a utilizar para generar el entorno de nuestra aplicación y es donde también le daremos "órdenes" para ejecutar la app.
35
+
36
+ En mi caso, creé una carpeta que se llama "Docker" con dos elementos: una carpeta llamada "R" donde está todo lo relacionado a la aplicación Shiny y el propio archivo "Dockerfile" (así, sin puntos, ni extensiones, ni cosas raras).
37
+
38
+ En mi caso, el archivo Dockerfile contiene las siguientes líneas, comentadas punto a punto para entender mejor:
39
+
40
+ ```{bash, eval = FALSE}
41
+ FROM rocker/shiny:latest # Se instala el entorno Shiny y R
42
+
43
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \ # Se instalan librerías necesarias
44
+ libgdal-dev \ # Ojo que probablemente necesites más o menos
45
+ libproj-dev \ # depende de los paquetes que uses
46
+ libgeos-dev \
47
+ default-libmysqlclient-dev \
48
+ libmysqlclient-dev \
49
+ libudunits2-dev \
50
+ netcdf-bin \
51
+ libxml2-dev \
52
+ libcairo2-dev \
53
+ libsqlite3-dev \
54
+ libpq-dev \
55
+ libssh2-1-dev \
56
+ unixodbc-dev \
57
+ libcurl4-openssl-dev \
58
+ libssl-dev
59
+
60
+ RUN apt-get install pandoc # Tenía problemas con Pandoc e instalarlo por separado lo solucionó
61
+
62
+ RUN apt-get update && \ # Actualizar paquetes y limpiar
63
+ apt-get upgrade -y && \
64
+ apt-get clean
65
+
66
+ # Se instalan los paquetes necesarios
67
+ # notar que ya estamos ejecutando COMANDOS DE R dentro del Docker
68
+
69
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse',
70
+ 'flexdashboard', 'leaflet', 'sf', 'ggalluvial',
71
+ 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
72
+
73
+ RUN R -e "install.packages(pkgs=c('devtools'),
74
+ repos='https://cran.rstudio.com/')"
75
+
76
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
77
+
78
+ RUN mkdir /root/app # Creamos la carpeta de la app
79
+
80
+ COPY R /root/shiny_save # Copia la carpeta "R" a la carpeta shiny_save
81
+
82
+ EXPOSE 3838 #Importantísimo: asignarle un puerto de comunicación a la app.
83
+ #Después no vamos a poder hacer nada si no se expone el puerto
84
+
85
+ # Se corre la app (en este caso, al ser un flexdashboard
86
+ # tengo que ejecutarlo con rmarkdown, si no sería shiny::runApp(...)).
87
+ # Como argumento extra le paso el puerto abierto.
88
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd',
89
+ shiny_args=list(host='0.0.0.0', port=3838))"]
90
+
91
+ ```
92
+
93
+ ## Construcción de la imagen
94
+
95
+ Una vez que tengamos el dockerfile y la carpeta con todos los archivos que necesita la app para correr, pasamos a construir la imagen de Docker propiamente dicha. Para esto, abrimos la consola y ejecutamos:
96
+
97
+ ```{bash, eval = FALSE}
98
+ docker login
99
+ ```
100
+ Nos pedirá el usuario y la contraseña de Docker. La escribimos y luego:
101
+
102
+ ```{bash, eval = FALSE}
103
+ docker build -t nombreapp .
104
+ ```
105
+
106
+ Ese comando construirá una imagen llamada "nombreapp" con los parámetros del dockerfile (para eso el punto al final). Esta imagen contiene el entorno R y todo el código que hemos usado, dentro de un sistema Linux.
107
+ Es por esto que probablemente tarde bastante en ejecutarse la primera vez (a mí me lleva aproximadamente 3 horas), obviamente dependiendo la complejidad de la app. Si hacemos modificaciones al código de R de la aplicación y pusheamos los cambios (ejecutando los mismos parámetros), la construcción tarda muchísimo menos ya que la mayoría de las capas del entorno son las mismas y no se modifican. Esto es algo que me encanta de Docker. El trabajo se divide en "capas", por lo que si corregís una parte pequeña del código, el build y el push de la app tardan segundos.
108
+
109
+
110
+ ```{bash, eval = FALSE}
111
+ docker tag nombreapp usuario/nombreapp:latest
112
+ ```
113
+
114
+ "Tagueamos" la app dentro del repositorio público que estamos por crear. Es como decir que lo estamos "seleccionando" para subirlo. El repositorio se llamará usuario/nombreapp. La última parte ":latest" es un nombre que podemos poner para identificar a la última versión de la app. Lo podemos ir modificando para cada versión.
115
+
116
+ ```{bash, eval = FALSE}
117
+ docker push usuario/nombreapp:latest
118
+ ```
119
+
120
+ Se pushea la imagen al repositorio, tal como si fuera GitHub.
121
+
122
+ ## Deploy en Google Cloud
123
+
124
+ Una vez creado el proyecto, procedemos a abrir la consola de comandos dentro de la misma Web de Google.
125
+ Lo primero que debemos hacer es loguearnos con nuestras credenciales de Docker.
126
+
127
+ ```{bash, eval = FALSE}
128
+ docker login
129
+ ```
130
+
131
+ Posteriormente, "traemos" la imagen del Docker a nuestro proyecto de Google Cloud con los últimos cambios.
132
+
133
+ ```{bash, eval = FALSE}
134
+ docker pull usuario/nombreapp:latest
135
+ ```
136
+ Luego, análogamente a lo que hacíamos en Docker, se "taguea" la imagen a pushear, lo único es que esta vez no pusheamos al repo de Docker sino al container de imágenes de Google Cloud. Reemplazá PROJECT_ID por el ID de tu proyecto.
137
+
138
+ ```{bash, eval = FALSE}
139
+ docker tag usuario/nombreapp:latest gcr.io/PROJECT_ID/usuario/nombreapp:latest
140
+ ```
141
+
142
+ Por último, pusheamos la imagen.
143
+
144
+ ```{bash, eval = FALSE}
145
+ docker push gcr.io/PROJECT_ID/usuario/nombreapp:latest
146
+ ```
147
+
148
+ ## Creación de la instancia del servicio
149
+
150
+ Ya casi terminamos. Luego de pusheada la imagen, en la pestaña de Google Cloud Run clickeás "Crear Servicio". Esto nos habilitará una máquina virtual para hacer el deploy y que podamos compartir la app a un montón de gente.
151
+
152
+ Donde dice "Url de la imagen" poné seleccionar y se van a desplegar las opciones de imagen. Nos va a aparecer la imagen "nombreapp" con la versión "latest". Usamos esa y completamos los parámetros con lo que necesite el proyecto.
153
+
154
+ **IMPORTANTE:** En la parte de más abajo donde dice "CONTENEDOR", completá dentro del campo "Puerto del contenedor" el puerto 3838 o el que hayas elegido en la construcción del Dockerfile, ya que sin esto Google va a enrutar mal el puerto generando errores.
155
+
156
+ Esperás unos segundos a que Google cree la instancia y listo! Ya está la app deployada en Google Cloud Run. Cualquiera va a poder acceder a ella a través de la URL que te muestra arriba del panel.
157
+
158
+
R/.Rproj.user/E43CA360/sources/per/t/EB610EE9 ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "EB610EE9",
3
+ "path": "~/Desktop/docker/R/global.R",
4
+ "project_path": "global.R",
5
+ "type": "r_source",
6
+ "hash": "3877315401",
7
+ "contents": "",
8
+ "dirty": false,
9
+ "created": 1654609389102.0,
10
+ "source_on_save": false,
11
+ "relative_order": 2,
12
+ "properties": {
13
+ "source_window_id": "",
14
+ "Source": "Source",
15
+ "cursorPosition": "0,1",
16
+ "scrollLine": "0"
17
+ },
18
+ "folds": "",
19
+ "lastKnownWriteTime": 1654609392,
20
+ "encoding": "UTF-8",
21
+ "collab_server": "",
22
+ "source_window": "",
23
+ "last_content_update": 1654609392337,
24
+ "read_only": false,
25
+ "read_only_alternatives": []
26
+ }
R/.Rproj.user/E43CA360/sources/per/t/EB610EE9-contents ADDED
@@ -0,0 +1 @@
 
 
1
+ #load("/root/shiny_save/data_def_v5.RData")
R/.Rproj.user/E43CA360/sources/per/t/FDAA923B ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "FDAA923B",
3
+ "path": "~/Desktop/docker/R/dash_v2.Rmd",
4
+ "project_path": "dash_v2.Rmd",
5
+ "type": "r_markdown",
6
+ "hash": "916050268",
7
+ "contents": "",
8
+ "dirty": false,
9
+ "created": 1654609377835.0,
10
+ "source_on_save": false,
11
+ "relative_order": 1,
12
+ "properties": {
13
+ "source_window_id": "",
14
+ "Source": "Source",
15
+ "cursorPosition": "72,91",
16
+ "scrollLine": "61",
17
+ "docOutlineVisible": "1",
18
+ "rmdVisualCollapsedChunks": ""
19
+ },
20
+ "folds": "",
21
+ "lastKnownWriteTime": 1654621409,
22
+ "encoding": "UTF-8",
23
+ "collab_server": "",
24
+ "source_window": "",
25
+ "last_content_update": 1654621409523,
26
+ "read_only": false,
27
+ "read_only_alternatives": []
28
+ }
R/.Rproj.user/E43CA360/sources/per/t/FDAA923B-contents ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Dashboard Patrones de Movilidad"
3
+ output:
4
+ flexdashboard::flex_dashboard:
5
+ orientation: rows
6
+ #logo: "omu.png"
7
+ runtime: shiny
8
+ ---
9
+
10
+ ```{r setup, include=FALSE}
11
+ library(flexdashboard)
12
+ library(shiny)
13
+ library(leaflet)
14
+ library(tidyverse)
15
+ library(sf)
16
+ library(plotly)
17
+ library(ggalluvial)
18
+ library(scales)
19
+
20
+ load("data_def_v5.RData")
21
+
22
+ ```
23
+
24
+ Sidebar {.sidebar}
25
+ =====================================
26
+
27
+ ```{r}
28
+
29
+ city_sel <- reactive(input$city_sel)
30
+ fecha_sel <- reactive(input$date1)
31
+
32
+ tags$style(HTML(".js-irs-0 .irs-single, .js-irs-0 .irs-bar-edge, .js-irs-0 .irs-bar {background: black}"))
33
+ tags$style(HTML(".js-irs-1 .irs-single, .js-irs-1 .irs-bar-edge, .js-irs-1 .irs-bar {background: black}"))
34
+ tags$style(HTML(".js-irs-2 .irs-single, .js-irs-2 .irs-bar-edge, .js-irs-2 .irs-bar {background: black}"))
35
+
36
+ selectInput("city_sel", label = "Ciudad seleccionada:",
37
+ choices = list(
38
+ Argentina = c("Buenos Aires", "Rosario"),
39
+ Brasil = c("Belo Horizonte",
40
+ "Brasilia",
41
+ "Florianopolis",
42
+ "Porto Alegre",
43
+ "Rio de Janeiro",
44
+ "Sao Paulo"),
45
+ Colombia = c("Barranquilla", "Bogota", "Cali", "Medellín"),
46
+ Otros = c("Santiago","Mexico", "Montevideo", "Lima","Panama")
47
+ ),
48
+ selected = "Buenos Aires")
49
+ dateInput("date1", "Fecha:", value = "2022-04-18", min = "2022-04-18",
50
+ max = "2022-05-15")
51
+ checkboxInput("tiles", label = "Cambiar de formato distrito a grilla", value = FALSE)
52
+ sliderInput("alpha", label = "Opacidad de las capas", min = 0,
53
+ max = 1, value = 0.85)
54
+ h4("Histogramas y dist. medias")
55
+ sliderInput("top_n", label = "Cantidad de distritos", min = 1,
56
+ max = 30, value = 8)
57
+ h4("Matriz Origen y Destino")
58
+ sliderInput("tolerance", label = "Tolerancia",
59
+ min = 100, max=60000, value = 4000)
60
+ checkboxInput("mismodest", label = "No incluir flujos intradistrito", value = TRUE)
61
+
62
+ #, value = fecha_inicio,
63
+ # min = fecha_inicio, max = fecha_fin)
64
+
65
+ # observe({
66
+ # We'll use the input$controller variable multiple times, so save it as x
67
+ # for convenience.
68
+ # x <- input$city_sel
69
+ # y <- input$date1
70
+ # if(isTRUE(y %in% as.Date(distancias_medias[which(distancias_medias$city == x),2]))){
71
+ # updateDateInput(session, "date1",
72
+ # min = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
73
+ # max = as.Date(distancias_medias[max(which(distancias_medias$city == x)), 2]))}else{
74
+ # updateDateInput(session, "date1",
75
+ # value = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
76
+ # min = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
77
+ # max = as.Date(distancias_medias[max(which(distancias_medias$city == x)), 2]))
78
+ # }
79
+ #
80
+ #
81
+ #
82
+ # })
83
+
84
+ ```
85
+
86
+
87
+ <!-- Información recolectada desde los datasets de **Facebook Disaster Maps** y del Mapa de Población de Alta Definición de Facebook. También se utiliza información de **Open Street Maps** y **GADM**. -->
88
+
89
+ <!-- Para más información, consultar la solapa "Metodología". -->
90
+
91
+ <div>
92
+
93
+ ```{r picture omu, echo = F, out.width = '95%'}
94
+ knitr::include_graphics("omu.png")
95
+ ```
96
+ </div>
97
+
98
+ <div>
99
+
100
+ ```{r picture caf, echo = F, out.width = '100%'}
101
+ knitr::include_graphics("caf.png")
102
+ ```
103
+
104
+ </div>
105
+
106
+ <div>
107
+
108
+ ```{r picture bid, echo = F, out.width = '95%'}
109
+ knitr::include_graphics("bid.png")
110
+ ```
111
+
112
+ </div>
113
+
114
+ <!-- __Juan Ignacio Fulponi__, _2022_. -->
115
+
116
+ Mapas
117
+ =====================================
118
+
119
+ ## Row
120
+
121
+ ### Distancia media {.value-box}
122
+
123
+ ```{r}
124
+
125
+ # Emit the download rate
126
+ renderValueBox({
127
+ histo_data <- viajes %>%
128
+ filter(city == input$city_sel, date == input$date1) %>%
129
+ rename(Partido=start_polygon_name)
130
+ mean_dist <- weighted.mean(histo_data$length_km, histo_data$viajes)
131
+ valueBox(value = paste0(round(mean(mean_dist), 2), " km."), icon = "fa-road")
132
+ })
133
+ ```
134
+
135
+ ### Distancia mediana {.value-box}
136
+
137
+ ```{r}
138
+
139
+ renderValueBox({
140
+ histo_data <- viajes %>%
141
+ filter(city == input$city_sel, date == input$date1) %>%
142
+ rename(Partido=start_polygon_name)
143
+ median_dist <- median(histo_data$length_km)
144
+ valueBox(value = paste0(round(mean(median_dist), 2), " km."), icon = "fa-bus", color = "darkgreen")
145
+ })
146
+ ```
147
+
148
+ ### Cantidad de pers. que viajan {.value-box}
149
+
150
+ ```{r}
151
+ # Emit the download count
152
+ renderValueBox({
153
+ count <- viajes %>%
154
+ dplyr::filter(city == input$city_sel, date == input$date1)
155
+ valueBox(value = paste0(round(sum(count$viajes, na.rm = T)/1000000,2), " mill."), icon = "fa-users", color = "purple")
156
+ })
157
+ ```
158
+
159
+ ## Column {data-width="650"}
160
+
161
+ ### Análisis geoespacial
162
+
163
+ ```{r}
164
+ city_sel <- reactive(input$city_sel)
165
+ fecha_sel <- reactive(input$date1)
166
+
167
+ renderLeaflet({
168
+ if (input$tiles){
169
+ grafico <- tiles %>%
170
+ dplyr::filter(city == input$city_sel,
171
+ date == input$date1)
172
+ if (input$city_sel == "Sao Paulo"){
173
+ grafico <- st_crop(grafico, xmin = -51.307504, ymin = -25.675336,
174
+ xmax= -44.528940, ymax=-21.671866)}
175
+
176
+ data_points <- grafico %>% st_centroid()
177
+
178
+ grafico$colors <- as.numeric(cut(grafico$mean_distance, breaks=c(0,3,4,5,6,7,8,9,10,11,12, max(grafico$mean_distance))))
179
+
180
+ data_points$rescaled_val <- scales::rescale(data_points$n_crisis, to = c(100,1400))
181
+
182
+
183
+ pal <- colorNumeric("viridis", grafico$mean_distance)
184
+ pal2 <- colorNumeric("PuRd", grafico$n_crisis, reverse = F)
185
+
186
+ leaflet(grafico) %>%
187
+ addProviderTiles(providers$Stamen.Toner) %>%
188
+ addPolygons(color = pal(grafico$mean_distance), stroke = NA,
189
+ fillOpacity = input$alpha, smoothFactor = 0.4,
190
+ label = paste0(round(grafico$mean_distance, 2), " km."),
191
+ group = "Distancias Medias") %>%
192
+ addLegend(pal = pal, values = ~grafico$mean_distance, title = "Km.",
193
+ group = "Distancias Medias") %>%
194
+ addCircles(data = data_points, radius = ~rescaled_val, color = pal(grafico$mean_distance), stroke = NA, fillOpacity = input$alpha,
195
+ label = ~paste0(start_polygon_name, " - Personas que viajan: ", n_crisis*12, " - Distancia media: ", round(mean_distance, 2), " km."),
196
+ group = "Combinado en puntos") %>%
197
+ addPolygons(color = pal2(grafico$n_crisis), stroke = NA,
198
+ fillOpacity = input$alpha, smoothFactor = 0.4,
199
+ label = paste0(round(grafico$n_crisis*12)),
200
+ group= "Generación de Pers. que viajan") %>%
201
+ addLegend(pal = pal2, values = ~grafico$n_crisis, title = "Cant.",
202
+ group = "Generación de Pers. que viajan",
203
+ position = c("topleft")) %>%
204
+ addLayersControl(
205
+ baseGroups=c("Combinado en puntos", "Distancias Medias", "Generación de Pers. que viajan"),
206
+ position = "bottomleft",
207
+ options = layersControlOptions(collapsed = FALSE))
208
+ }
209
+ else {
210
+ grafico <- distancias_medias %>%
211
+ dplyr::filter(city == input$city_sel,
212
+ date == input$date1) %>% left_join(
213
+ viajes %>% group_by(start_polygon_name, date, city) %>%
214
+ summarise(viajes = sum(viajes))
215
+ )
216
+
217
+ grafico$colors2 <- as.numeric(cut(grafico$distancia_media, breaks=c(0,3,4,5,6,7,8,9,10,11,12, max(grafico$distancia_media))))
218
+
219
+ pal_2 <- colorNumeric("viridis", grafico$distancia_media)
220
+ pal_r <- colorNumeric("PuRd", grafico$viajes, reverse = F)
221
+ grafico %>%
222
+ left_join(geo) %>%
223
+ st_as_sf() %>%
224
+ leaflet() %>%
225
+ addProviderTiles(providers$Stamen.Toner) %>%
226
+ addPolygons(color = ~pal_2(distancia_media), stroke = NA,
227
+ fillOpacity = input$alpha, smoothFactor = 0.4,
228
+ label = paste0(grafico$start_polygon_name, " - ",
229
+ round(grafico$distancia_media, 2), " km."), group = "Distancias Medias") %>%
230
+ addLegend(pal = pal_2, values = ~distancia_media, title = "Km.", group = "Distancias Medias") %>%
231
+ addPolygons(color = pal_r(grafico$viajes), stroke = NA,
232
+ fillOpacity = input$alpha, smoothFactor = 0.4,
233
+ label = paste0(grafico$start_polygon_name, " - ",
234
+ round(grafico$viajes), " viajes"),
235
+ group = "Generación de Pers. que viajan") %>%
236
+ addLegend(pal = pal_r, values = ~grafico$viajes, title = "Cant.",
237
+ group = "Generación de Pers. que viajan",
238
+ position = c("topleft")) %>%
239
+ addLayersControl(
240
+ baseGroups=c("Distancias Medias", "Generación de Pers. que viajan"),
241
+ position = "bottomleft",
242
+ options = layersControlOptions(collapsed = FALSE))
243
+ }
244
+ })
245
+
246
+ ```
247
+
248
+ # Análisis estadístico
249
+
250
+ ## Column {data-width="650"}
251
+
252
+ ### Distancias medias (en km.) de los distritos con más pers. que viajan
253
+
254
+ ```{r}
255
+ city_sel <- reactive(input$city_sel)
256
+ fecha_sel <- reactive(input$date1)
257
+ renderPlotly({
258
+
259
+ munis <- viajes %>%
260
+ dplyr::filter(city == input$city_sel) %>%
261
+ group_by(start_polygon_name) %>%
262
+ summarise(viajes=sum(viajes, na.rm=T)) %>%
263
+ top_n(n=input$top_n) %>% dplyr::select(start_polygon_name)
264
+
265
+ plot_ly(distancias_medias %>% right_join(munis) %>% filter(distancia_media>0), x = ~date, y = ~distancia_media, color = ~start_polygon_name) %>% add_lines() %>%
266
+ layout(xaxis = list(zerolinecolor = '#ffff',
267
+ gridcolor = 'ffff',
268
+ autotick = T, title = ""),
269
+ yaxis = list(zerolinecolor = '#ffff',
270
+ gridcolor = 'ffff', title = "Km."),
271
+ plot_bgcolor='#e5ecf6') %>%
272
+ layout(legend = list(orientation = "h")) # put legend in center of x-axis
273
+
274
+ })
275
+
276
+ ```
277
+
278
+ ### Histograma de pers. que viajan
279
+
280
+ ```{r}
281
+ city_sel <- reactive(input$city_sel)
282
+ fecha_sel <- reactive(input$date1)
283
+ renderPlotly({
284
+
285
+ Salientes <- viajes %>%
286
+ dplyr::filter(city == input$city_sel, date == input$date1,
287
+ start_polygon_name!=end_polygon_name) %>%
288
+ filter(start_polygon_name!=end_polygon_name) %>%
289
+ group_by(start_polygon_name) %>%
290
+ summarise(Salientes=sum(viajes, na.rm=T)) %>%
291
+ dplyr::top_n(n=input$top_n) %>%
292
+ # filter(salientes>1000) %>%
293
+ filter(start_polygon_name!="Dagua")
294
+
295
+ Entrantes <- viajes %>%
296
+ dplyr::filter(city == input$city_sel, date == input$date1,
297
+ start_polygon_name!=end_polygon_name) %>%
298
+ filter(end_polygon_name %in% Salientes$start_polygon_name) %>%
299
+ group_by(end_polygon_name) %>%
300
+ summarise(Entrantes=sum(viajes, na.rm=T)) %>%
301
+ #filter(entrantes>1000) %>%
302
+ filter(end_polygon_name!="Dagua")
303
+
304
+ Salientes <- Salientes %>%
305
+ filter(start_polygon_name %in% Entrantes$end_polygon_name)
306
+
307
+ bind_cols(Entrantes, Salientes) %>% dplyr::select(-end_polygon_name) %>%
308
+ pivot_longer(cols = c(Entrantes, Salientes)) %>%
309
+ plot_ly(
310
+ x = ~start_polygon_name,
311
+ y = ~value, color = ~name,
312
+ type = "bar") %>%
313
+ layout(xaxis = list(zerolinecolor = '#ffff',
314
+ gridcolor = 'ffff',
315
+ categoryorder = "total descending", title = ""),
316
+ yaxis = list(zerolinecolor = '#ffff',
317
+ gridcolor = 'ffff', title = "Cantidad de pers. que viajan"
318
+ ), barmode = 'stack',
319
+ plot_bgcolor='#e5ecf6',
320
+ showlegend = T, legend = list(orientation = 'h',
321
+ xanchor = "center",
322
+ x = 0.5, y=-0.4))
323
+ })
324
+
325
+ ```
326
+
327
+ ## Column {data-width="650"}
328
+
329
+ ### Histograma de distancias medias entre los distritos
330
+
331
+ ```{r corrplot}
332
+ city_sel <- reactive(input$city_sel)
333
+ fecha_sel <- reactive(input$date1)
334
+ renderPlotly({
335
+
336
+ histo_data <- viajes %>%
337
+ filter(city == input$city_sel, date == input$date1) %>%
338
+ rename(Distrito=start_polygon_name)
339
+ mean_dist <- weighted.mean(histo_data$length_km, histo_data$viajes)
340
+ median_dist <- median(histo_data$length_km, na.rm = T)
341
+
342
+ ggplotly(ggplot(histo_data) +
343
+ geom_histogram(aes(as.numeric(length_km),
344
+ #y = ..density..#,
345
+ #weight = viajes
346
+ #fill = Partido
347
+ ), bins = 100) +
348
+ geom_vline(xintercept=mean_dist, size=1, color="red")+
349
+ geom_vline(xintercept=median_dist,
350
+ size=1, color="blue", linetype = 2)+
351
+ xlim(c(0,50))+
352
+ theme(legend.position = "none") +
353
+ labs(x = "Distancias medias, en km.", y = "Ocurrencia de distritos", title = paste0("Distancia Media: ", round(mean_dist, 2), " km. - Distancia Mediana: ", round(median_dist, 2), " km.")), tooltip="Distrito"
354
+ )%>%
355
+ layout(xaxis = list(autorange = TRUE),
356
+ yaxis = list(autorange = TRUE))
357
+
358
+ })
359
+
360
+
361
+ ```
362
+
363
+ ### Flujos Inter e Intradistrito
364
+
365
+ ```{r}
366
+ city_sel <- reactive(input$city_sel)
367
+ fecha_sel <- reactive(input$date1)
368
+
369
+ renderPlotly({
370
+ plotly_viajes <- viajes %>% filter(city == input$city_sel) %>%
371
+ group_by(date, start_polygon_name, end_polygon_name) %>%
372
+ summarise(viajes = sum(viajes)) %>%
373
+ mutate(intrap = if_else(start_polygon_name==end_polygon_name,
374
+ "Flujo Intradistrito",
375
+ "Flujo Interdistrito")) %>%
376
+ group_by(date, intrap) %>%
377
+ summarise(viajes = sum(viajes))
378
+
379
+ ggplotly(ggplot(plotly_viajes) +
380
+ geom_line(aes(date, viajes/1000, color = intrap)) +
381
+ labs(color = NULL, x = NULL, y = "Pers. que viajan (en miles)"),
382
+ tooltip="viajes/1000")})
383
+ ```
384
+
385
+
386
+ # Matriz Origen y Destino
387
+
388
+ ## Column {data-width="350"}
389
+
390
+ ### Matriz origen y destino
391
+
392
+ ```{r}
393
+
394
+ city_sel <- reactive(input$city_sel)
395
+ fecha_sel <- reactive(input$date1)
396
+
397
+ renderPlot({
398
+ if (input$mismodest){
399
+ plot <- viajes %>%
400
+ dplyr::filter(city == input$city_sel, date == input$date1) %>%
401
+ dplyr::filter(start_polygon_name != end_polygon_name) %>%
402
+ group_by(start_polygon_name, end_polygon_name) %>%
403
+ summarize(viajes = sum(viajes, na.rm=T)) %>%
404
+ filter(viajes > input$tolerance)
405
+ }else{
406
+ plot <- viajes %>%
407
+ dplyr::filter(city == input$city_sel, date == input$date1) %>%
408
+ group_by(start_polygon_name, end_polygon_name) %>%
409
+ summarize(viajes = sum(viajes, na.rm=T)) %>%
410
+ filter(viajes > input$tolerance)
411
+ }
412
+
413
+ ggplot(plot, aes(y = viajes/1000000, axis1=start_polygon_name, axis2=end_polygon_name))+
414
+ geom_alluvium(aes(fill=start_polygon_name))+
415
+ geom_stratum() +
416
+ geom_text(stat = "stratum",
417
+ aes(label = after_stat(stratum))) +
418
+ scale_x_discrete(limits = c("Origen", "Destino"),
419
+ expand = c(0.15, 0.05)) +
420
+ labs(fill = "", y = "Oers. que viajan (en mill.)") +
421
+ theme(legend.position = "none")
422
+
423
+ })
424
+ ```
425
+
426
+
427
+ ### Pares origen y destino
428
+
429
+ ```{r}
430
+
431
+ city_sel <- reactive(input$city_sel)
432
+ fecha_sel <- reactive(input$date1)
433
+
434
+ renderPlotly({
435
+ arrows <- viajes %>% filter(!is.na(geometry),
436
+ city == input$city_sel,
437
+ date == input$date1,
438
+ start_polygon_name != end_polygon_name,
439
+ viajes > input$tolerance) %>%
440
+ mutate(geometry = gsub("LINESTRING \\(", "", geometry),
441
+ geometry = gsub(",", "", geometry),
442
+ geometry = gsub(")", "", geometry),
443
+ lonlats = strsplit(geometry, "\\s+"))
444
+
445
+ arrows$xstart <- sapply(arrows$lonlats,"[[",1) %>% as.numeric
446
+ arrows$ystart <- sapply(arrows$lonlats,"[[",2) %>% as.numeric
447
+ arrows$xend <- sapply(arrows$lonlats,"[[",3) %>% as.numeric
448
+ arrows$yend <- sapply(arrows$lonlats,"[[",4) %>% as.numeric
449
+
450
+ geo_sf <- geo[which(geo$city==input$city_sel),]
451
+ bbox <- st_bbox(geo_sf)
452
+
453
+ p <- ggplot(arrows)+
454
+ geom_sf(data = geo_sf, fill = NA, size = .3) +
455
+ geom_segment(aes(x=xstart, y=ystart, xend=xend, yend=yend, size = viajes, text = paste0(start_polygon_name, " - ", end_polygon_name, " - ", round(viajes), " viajes.")), color = NA) +
456
+ scale_size(range=c(.5,1.5)) +
457
+ xlim(bbox$xmin, bbox$xmax) +
458
+ ylim(bbox$ymin, bbox$ymax) +
459
+ theme_void() + theme(legend.position = "none",
460
+ axis.line = element_blank())
461
+
462
+ mypal <- colorRampPalette(c("white", "darkblue"))(30)
463
+ map2color<-function(x,pal,limits=NULL){
464
+ if(is.null(limits)) limits=range(x)
465
+ pal[findInterval(x,seq(limits[1],limits[2],length.out=length(pal)+1), all.inside=TRUE)]
466
+ }
467
+
468
+
469
+ g <- ggplotly(p, tooltip = c("text")) %>%
470
+ add_annotations( x = ~arrows$xend,
471
+ y = ~arrows$yend,
472
+ xref = "x", yref = "y",
473
+ axref = "x", ayref = "y",
474
+ text = "",
475
+ showarrow = T,
476
+ ax = ~arrows$xstart,
477
+ ay = ~arrows$ystart,
478
+ arrowcolor = ~map2color(log(arrows$viajes), mypal),
479
+ arrowwidth = ~rescale(arrows$viajes, 2, 20),
480
+ borderwidth = ~rescale(arrows$viajes, 0, 10))
481
+
482
+ g
483
+ })
484
+
485
+
486
+ ```
487
+
488
+ # Metodología
489
+
490
+ ## Column {data-width="650"}
491
+
492
+ ### Metodología aplicada en el presente trabajo
493
+
494
+ Toda la información presentada es recolectada desde los datasets de **Facebook Disaster Maps** y del Mapa de Población de Alta Definición de Facebook. También se utiliza información de **Open Street Maps** y **GADM**.
495
+
496
+
497
+ Cuando las personas usan la aplicación de Facebook con los servicios de ubicación habilitados, sus latitudes y longitudes se reciben en intervalos regulares. La información de ubicación se usa de varias maneras, como ofrecer una función desde la aplicación o contenido que sea más relevante para las personas. Los __Facebook disaster maps__ brindan información sobre dónde se encuentran las personas y cómo se mueven. La información se presenta en dos niveles: por tile (cuadrados) o por región administrativa (como los censos poblacionales).
498
+
499
+ La región se divide en una grilla (tiles) de 2 km. Facebook detecta las coordenadas de las personas y las clasifica según el tile en el que ha estado más tiempo en las ocho horas anteriores al tiempo de la toma de datos. Por ejemplo, si el conjunto de datos es referido a las 8 a. m. y la persona estaba en su casa, Facebook clasificará a esa persona en ese tile y le asignará "origen". Si la persona pasó la mayor parte de las próximas ocho horas en otro Tile, esa persona aparecerá en los datos agregados del segundo cuadrado como "destino". Si aparece en el mismo tile, el destino será ese mismo tile.
500
+
501
+ __Sesgo de población__
502
+
503
+ Desde el punto de vista de las regiones administrativas, es posible que existan diferencias en cuanto al uso de las aplicaciones de Facebook y/o la aceptación del uso de la localización geoespacial. Por lo tanto, tratamos de eliminar ese sesgo tomando sumas de todos los viajes de cada uno de los orígenes y comparándolos geoespacialmente con la población correspondiente de cada región provieniente del dataset de High Resolution Density Maps.
504
+
505
+ De allí se extraen factores de expansión o pesos que aplicamos a los viajes de cada región administrativa para tener una noción más “real” de los flujos de personas entre partidos o municipios.
506
+
507
+ __Viajes cortos o nulos__
508
+
509
+ También se quitan del análisis, para tener un dato más cercano a la realidad de las personas que "no se movieron", aquellas personas que viajan que comienzan y terminan su viaje en el mismo tile. Para esto, se toman las personas que tienen como origen y como destino la misma región administrativa pero distinto tile. Luego, se aplican dos factores de expansión: el primero de acuerdo a la cantidad de personas que viajan intrapartido que figura en el dataset de Movement Between Administrative Regions y el segundo según la población (como en el caso anterior), para luego sumarse a la cantidad de personas que viajan interpartido entre regiones administrativas
510
+
511
+ El flujo de trabajo se puede representar de la siguiente manera, para cada uno de los partidos o municipios (más técnicamente llamados regiones administrativas), pertenecientes a la zona de las ciudades analizadas.
512
+
513
+
514
+ <div>
515
+
516
+ ```{r picture3, echo = F, fig.cap = "Flujograma", out.width = '100%', fig.pos="H"}
517
+ knitr::include_graphics("flujograma.png")
518
+ ```
519
+
520
+ </div>
521
+
522
+
523
+
R/.Rproj.user/E43CA360/sources/prop/79CB3A6C ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "tempName": "Untitled1",
3
+ "source_window_id": "",
4
+ "Source": "Source",
5
+ "cursorPosition": "143,22",
6
+ "scrollLine": "119"
7
+ }
R/.Rproj.user/E43CA360/sources/prop/7DEF3E17 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "source_window_id": "",
3
+ "Source": "Source",
4
+ "cursorPosition": "0,0",
5
+ "scrollLine": "0"
6
+ }
R/.Rproj.user/E43CA360/sources/prop/B3A3F0F6 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "source_window_id": "",
3
+ "Source": "Source",
4
+ "cursorPosition": "0,1",
5
+ "scrollLine": "0"
6
+ }
R/.Rproj.user/E43CA360/sources/prop/D2B7D434 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "source_window_id": "",
3
+ "Source": "Source",
4
+ "cursorPosition": "199,0",
5
+ "scrollLine": "183"
6
+ }
R/.Rproj.user/E43CA360/sources/prop/E88E6722 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_window_id": "",
3
+ "Source": "Source",
4
+ "cursorPosition": "72,91",
5
+ "scrollLine": "61",
6
+ "docOutlineVisible": "1",
7
+ "rmdVisualCollapsedChunks": ""
8
+ }
R/.Rproj.user/E43CA360/sources/prop/INDEX ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ ~%2FDesktop%2Fdocker%2FDockerfile="7DEF3E17"
2
+ ~%2FDesktop%2Fdocker%2FR%2Fdash_v2.Rmd="E88E6722"
3
+ ~%2FDesktop%2Fdocker%2FR%2Fgithub.Rmd="79CB3A6C"
4
+ ~%2FDesktop%2Fdocker%2FR%2Fgithub.md="D2B7D434"
5
+ ~%2FDesktop%2Fdocker%2FR%2Fglobal.R="B3A3F0F6"
R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/E43CA360937CAF11/chunks.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"chunk_definitions":[],"doc_write_time":1654609442}
R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/s/chunks.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"chunk_definitions":[],"doc_write_time":1654609442}
R/.Rproj.user/shared/notebooks/AE375184-github/1/E43CA360937CAF11/chunks.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"chunk_definitions":[],"doc_write_time":1654628376}
R/.Rproj.user/shared/notebooks/AE375184-github/1/s/chunks.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"chunk_definitions":[],"doc_write_time":1654628376}
R/.Rproj.user/shared/notebooks/patch-chunk-names ADDED
File without changes
R/.Rproj.user/shared/notebooks/paths ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ /Users/juanignaciofulponi/Desktop/docker/R/dash_v2.Rmd="09ED8104"
2
+ /Users/juanignaciofulponi/Desktop/docker/R/github.Rmd="AE375184"
R/.data_def_v5.RData.icloud ADDED
Binary file (169 Bytes). View file
 
R/Dash BID CAF.Rproj ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Version: 1.0
2
+
3
+ RestoreWorkspace: Default
4
+ SaveWorkspace: Default
5
+ AlwaysSaveHistory: Default
6
+
7
+ EnableCodeIndexing: Yes
8
+ UseSpacesForTab: Yes
9
+ NumSpacesForTab: 2
10
+ Encoding: UTF-8
11
+
12
+ RnwWeave: Sweave
13
+ LaTeX: pdfLaTeX
R/bid.png ADDED
R/caf.png ADDED
R/dash_v2.Rmd ADDED
@@ -0,0 +1,516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Dashboard Patrones de Movilidad"
3
+ output:
4
+ flexdashboard::flex_dashboard:
5
+ orientation: rows
6
+ #logo: "omu.png"
7
+ runtime: shiny
8
+ ---
9
+
10
+ ```{r setup, include=FALSE}
11
+ library(flexdashboard)
12
+ library(shiny)
13
+ library(leaflet)
14
+ library(tidyverse)
15
+ library(sf)
16
+ library(plotly)
17
+ library(ggalluvial)
18
+ library(scales)
19
+ library(highcharter)
20
+
21
+ load("data_def_v5.RData")
22
+
23
+ ```
24
+
25
+ Sidebar {.sidebar}
26
+ =====================================
27
+
28
+ ```{r}
29
+
30
+ city_sel <- reactive(input$city_sel)
31
+ fecha_sel <- reactive(input$date1)
32
+
33
+ tags$style(HTML(".js-irs-0 .irs-single, .js-irs-0 .irs-bar-edge, .js-irs-0 .irs-bar {background: black}"))
34
+ tags$style(HTML(".js-irs-1 .irs-single, .js-irs-1 .irs-bar-edge, .js-irs-1 .irs-bar {background: black}"))
35
+ tags$style(HTML(".js-irs-2 .irs-single, .js-irs-2 .irs-bar-edge, .js-irs-2 .irs-bar {background: black}"))
36
+
37
+ selectInput("city_sel", label = "Ciudad seleccionada:",
38
+ choices = list(
39
+ Argentina = c("Buenos Aires", "Rosario"),
40
+ Brasil = c("Belo Horizonte",
41
+ "Brasilia",
42
+ "Florianopolis",
43
+ "Porto Alegre",
44
+ "Rio de Janeiro",
45
+ "Sao Paulo"),
46
+ Colombia = c("Barranquilla", "Bogota", "Cali", "Medellín"),
47
+ Otros = c("Santiago","Mexico", "Montevideo", "Lima","Panama")
48
+ ),
49
+ selected = "Buenos Aires")
50
+ dateInput("date1", "Fecha:", value = "2022-04-18", min = "2022-04-18",
51
+ max = "2022-05-15")
52
+ checkboxInput("tiles", label = "Cambiar de formato distrito a grilla", value = FALSE)
53
+ sliderInput("alpha", label = "Opacidad de las capas", min = 0,
54
+ max = 1, value = 0.85)
55
+ h4("Histogramas y dist. medias")
56
+ sliderInput("top_n", label = "Cantidad de distritos", min = 1,
57
+ max = 30, value = 8)
58
+ h4("Matriz Origen y Destino")
59
+ sliderInput("tolerance", label = "Tolerancia",
60
+ min = 100, max=60000, value = 4000)
61
+ checkboxInput("mismodest", label = "No incluir flujos intradistrito", value = TRUE)
62
+
63
+ #, value = fecha_inicio,
64
+ # min = fecha_inicio, max = fecha_fin)
65
+
66
+ # observe({
67
+ # We'll use the input$controller variable multiple times, so save it as x
68
+ # for convenience.
69
+ # x <- input$city_sel
70
+ # y <- input$date1
71
+ # if(isTRUE(y %in% as.Date(distancias_medias[which(distancias_medias$city == x),2]))){
72
+ # updateDateInput(session, "date1",
73
+ # min = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
74
+ # max = as.Date(distancias_medias[max(which(distancias_medias$city == x)), 2]))}else{
75
+ # updateDateInput(session, "date1",
76
+ # value = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
77
+ # min = as.Date(distancias_medias[min(which(distancias_medias$city == x)), 2]),
78
+ # max = as.Date(distancias_medias[max(which(distancias_medias$city == x)), 2]))
79
+ # }
80
+ #
81
+ #
82
+ #
83
+ # })
84
+
85
+ ```
86
+
87
+
88
+ <!-- Información recolectada desde los datasets de **Facebook Disaster Maps** y del Mapa de Población de Alta Definición de Facebook. También se utiliza información de **Open Street Maps** y **GADM**. -->
89
+
90
+ <!-- Para más información, consultar la solapa "Metodología". -->
91
+
92
+ <div>
93
+
94
+ ```{r picture omu, echo = F, out.width = '95%'}
95
+ knitr::include_graphics("omu.png")
96
+ ```
97
+ </div>
98
+
99
+ <div>
100
+
101
+ ```{r picture caf, echo = F, out.width = '100%'}
102
+ knitr::include_graphics("caf.png")
103
+ ```
104
+
105
+ </div>
106
+
107
+ <div>
108
+
109
+ ```{r picture bid, echo = F, out.width = '95%'}
110
+ knitr::include_graphics("bid.png")
111
+ ```
112
+
113
+ </div>
114
+
115
+ <!-- __Juan Ignacio Fulponi__, _2022_. -->
116
+
117
+ Mapas
118
+ =====================================
119
+
120
+ ## Row
121
+
122
+ ### Distancia media {.value-box}
123
+
124
+ ```{r}
125
+
126
+ # Emit the download rate
127
+ renderValueBox({
128
+ histo_data <- viajes %>%
129
+ filter(city == input$city_sel, date == input$date1) %>%
130
+ rename(Partido=start_polygon_name)
131
+ mean_dist <- weighted.mean(histo_data$length_km, histo_data$viajes)
132
+ valueBox(value = paste0(round(mean(mean_dist), 2), " km."), icon = "fa-road")
133
+ })
134
+ ```
135
+
136
+ ### Distancia mediana {.value-box}
137
+
138
+ ```{r}
139
+
140
+ renderValueBox({
141
+ histo_data <- viajes %>%
142
+ filter(city == input$city_sel, date == input$date1) %>%
143
+ rename(Partido=start_polygon_name)
144
+ median_dist <- median(histo_data$length_km)
145
+ valueBox(value = paste0(round(mean(median_dist), 2), " km."), icon = "fa-bus", color = "darkgreen")
146
+ })
147
+ ```
148
+
149
+ ### Cantidad de pers. que viajan {.value-box}
150
+
151
+ ```{r}
152
+ # Emit the download count
153
+ renderValueBox({
154
+ count <- viajes %>%
155
+ dplyr::filter(city == input$city_sel, date == input$date1)
156
+ valueBox(value = paste0(round(sum(count$viajes, na.rm = T)/1000000,2), " mill."), icon = "fa-users", color = "purple")
157
+ })
158
+ ```
159
+
160
+ ## Column {data-width="650"}
161
+
162
+ ### Análisis geoespacial
163
+
164
+ ```{r}
165
+ city_sel <- reactive(input$city_sel)
166
+ fecha_sel <- reactive(input$date1)
167
+
168
+ renderLeaflet({
169
+ if (input$tiles){
170
+ grafico <- tiles %>%
171
+ dplyr::filter(city == input$city_sel,
172
+ date == input$date1)
173
+ if (input$city_sel == "Sao Paulo"){
174
+ grafico <- st_crop(grafico, xmin = -51.307504, ymin = -25.675336,
175
+ xmax= -44.528940, ymax=-21.671866)}
176
+
177
+ data_points <- grafico %>% st_centroid()
178
+
179
+ grafico$colors <- as.numeric(cut(grafico$mean_distance, breaks=c(0,3,4,5,6,7,8,9,10,11,12, max(grafico$mean_distance))))
180
+
181
+ data_points$rescaled_val <- scales::rescale(data_points$n_crisis, to = c(100,1400))
182
+
183
+
184
+ pal <- colorNumeric("viridis", grafico$mean_distance)
185
+ pal2 <- colorNumeric("PuRd", grafico$n_crisis, reverse = F)
186
+
187
+ leaflet(grafico) %>%
188
+ addProviderTiles(providers$Stamen.Toner) %>%
189
+ addPolygons(color = pal(grafico$mean_distance), stroke = NA,
190
+ fillOpacity = input$alpha, smoothFactor = 0.4,
191
+ label = paste0(round(grafico$mean_distance, 2), " km."),
192
+ group = "Distancias Medias") %>%
193
+ addLegend(pal = pal, values = ~grafico$mean_distance, title = "Km.",
194
+ group = "Distancias Medias") %>%
195
+ addCircles(data = data_points, radius = ~rescaled_val, color = pal(grafico$mean_distance), stroke = NA, fillOpacity = input$alpha,
196
+ label = ~paste0(start_polygon_name, " - Personas que viajan: ", n_crisis*12, " - Distancia media: ", round(mean_distance, 2), " km."),
197
+ group = "Combinado en puntos") %>%
198
+ addPolygons(color = pal2(grafico$n_crisis), stroke = NA,
199
+ fillOpacity = input$alpha, smoothFactor = 0.4,
200
+ label = paste0(round(grafico$n_crisis*12)),
201
+ group= "Generación de Pers. que viajan") %>%
202
+ addLegend(pal = pal2, values = ~grafico$n_crisis, title = "Cant.",
203
+ group = "Generación de Pers. que viajan",
204
+ position = c("topleft")) %>%
205
+ addLayersControl(
206
+ baseGroups=c("Combinado en puntos", "Distancias Medias", "Generación de Pers. que viajan"),
207
+ position = "bottomleft",
208
+ options = layersControlOptions(collapsed = FALSE))
209
+ }
210
+ else {
211
+ grafico <- distancias_medias %>%
212
+ dplyr::filter(city == input$city_sel,
213
+ date == input$date1) %>% left_join(
214
+ viajes %>% group_by(start_polygon_name, date, city) %>%
215
+ summarise(viajes = sum(viajes))
216
+ )
217
+
218
+ grafico$colors2 <- as.numeric(cut(grafico$distancia_media, breaks=c(0,3,4,5,6,7,8,9,10,11,12, max(grafico$distancia_media))))
219
+
220
+ pal_2 <- colorNumeric("viridis", grafico$distancia_media)
221
+ pal_r <- colorNumeric("PuRd", grafico$viajes, reverse = F)
222
+ grafico %>%
223
+ left_join(geo) %>%
224
+ st_as_sf() %>%
225
+ leaflet() %>%
226
+ addProviderTiles(providers$Stamen.Toner) %>%
227
+ addPolygons(color = ~pal_2(distancia_media), stroke = NA,
228
+ fillOpacity = input$alpha, smoothFactor = 0.4,
229
+ label = paste0(grafico$start_polygon_name, " - ",
230
+ round(grafico$distancia_media, 2), " km."), group = "Distancias Medias") %>%
231
+ addLegend(pal = pal_2, values = ~distancia_media, title = "Km.", group = "Distancias Medias") %>%
232
+ addPolygons(color = pal_r(grafico$viajes), stroke = NA,
233
+ fillOpacity = input$alpha, smoothFactor = 0.4,
234
+ label = paste0(grafico$start_polygon_name, " - ",
235
+ round(grafico$viajes), " viajes"),
236
+ group = "Generación de Pers. que viajan") %>%
237
+ addLegend(pal = pal_r, values = ~grafico$viajes, title = "Cant.",
238
+ group = "Generación de Pers. que viajan",
239
+ position = c("topleft")) %>%
240
+ addLayersControl(
241
+ baseGroups=c("Distancias Medias", "Generación de Pers. que viajan"),
242
+ position = "bottomleft",
243
+ options = layersControlOptions(collapsed = FALSE))
244
+ }
245
+ })
246
+
247
+ ```
248
+
249
+ # Análisis estadístico
250
+
251
+ ## Column {data-width="650"}
252
+
253
+ ### Distancias medias (en km.) de los distritos con más pers. que viajan
254
+
255
+ ```{r}
256
+ city_sel <- reactive(input$city_sel)
257
+ fecha_sel <- reactive(input$date1)
258
+ renderPlotly({
259
+
260
+ munis <- viajes %>%
261
+ dplyr::filter(city == input$city_sel) %>%
262
+ group_by(start_polygon_name) %>%
263
+ summarise(viajes=sum(viajes, na.rm=T)) %>%
264
+ top_n(n=input$top_n) %>% dplyr::select(start_polygon_name)
265
+
266
+ plot_ly(distancias_medias %>% right_join(munis) %>% filter(distancia_media>0), x = ~date, y = ~distancia_media, color = ~start_polygon_name) %>% add_lines() %>%
267
+ layout(xaxis = list(zerolinecolor = '#ffff',
268
+ gridcolor = 'ffff',
269
+ autotick = T, title = ""),
270
+ yaxis = list(zerolinecolor = '#ffff',
271
+ gridcolor = 'ffff', title = "Km."),
272
+ plot_bgcolor='#e5ecf6') %>%
273
+ layout(legend = list(orientation = "h")) # put legend in center of x-axis
274
+
275
+ })
276
+
277
+ ```
278
+
279
+ ### Histograma de pers. que viajan
280
+
281
+ ```{r}
282
+ city_sel <- reactive(input$city_sel)
283
+ fecha_sel <- reactive(input$date1)
284
+ renderPlotly({
285
+
286
+ Salientes <- viajes %>%
287
+ dplyr::filter(city == input$city_sel, date == input$date1,
288
+ start_polygon_name!=end_polygon_name) %>%
289
+ filter(start_polygon_name!=end_polygon_name) %>%
290
+ group_by(start_polygon_name) %>%
291
+ summarise(Salientes=sum(viajes, na.rm=T)) %>%
292
+ dplyr::top_n(n=input$top_n) %>%
293
+ # filter(salientes>1000) %>%
294
+ filter(start_polygon_name!="Dagua")
295
+
296
+ Entrantes <- viajes %>%
297
+ dplyr::filter(city == input$city_sel, date == input$date1,
298
+ start_polygon_name!=end_polygon_name) %>%
299
+ filter(end_polygon_name %in% Salientes$start_polygon_name) %>%
300
+ group_by(end_polygon_name) %>%
301
+ summarise(Entrantes=sum(viajes, na.rm=T)) %>%
302
+ #filter(entrantes>1000) %>%
303
+ filter(end_polygon_name!="Dagua")
304
+
305
+ Salientes <- Salientes %>%
306
+ filter(start_polygon_name %in% Entrantes$end_polygon_name)
307
+
308
+ bind_cols(Entrantes, Salientes) %>% dplyr::select(-end_polygon_name) %>%
309
+ pivot_longer(cols = c(Entrantes, Salientes)) %>%
310
+ plot_ly(
311
+ x = ~start_polygon_name,
312
+ y = ~value, color = ~name,
313
+ type = "bar") %>%
314
+ layout(xaxis = list(zerolinecolor = '#ffff',
315
+ gridcolor = 'ffff',
316
+ categoryorder = "total descending", title = ""),
317
+ yaxis = list(zerolinecolor = '#ffff',
318
+ gridcolor = 'ffff', title = "Cantidad de pers. que viajan"
319
+ ), barmode = 'stack',
320
+ plot_bgcolor='#e5ecf6',
321
+ showlegend = T, legend = list(orientation = 'h',
322
+ xanchor = "center",
323
+ x = 0.5, y=-0.4))
324
+ })
325
+
326
+ ```
327
+
328
+ ## Column {data-width="650"}
329
+
330
+ ### Histograma de distancias medias entre los distritos
331
+
332
+ ```{r corrplot}
333
+ city_sel <- reactive(input$city_sel)
334
+ fecha_sel <- reactive(input$date1)
335
+ renderPlotly({
336
+
337
+ histo_data <- viajes %>%
338
+ filter(city == input$city_sel, date == input$date1) %>%
339
+ rename(Distrito=start_polygon_name)
340
+ mean_dist <- weighted.mean(histo_data$length_km, histo_data$viajes)
341
+ median_dist <- median(histo_data$length_km, na.rm = T)
342
+
343
+ ggplotly(ggplot(histo_data) +
344
+ geom_histogram(aes(as.numeric(length_km),
345
+ #y = ..density..#,
346
+ #weight = viajes
347
+ #fill = Partido
348
+ ), bins = 100) +
349
+ geom_vline(xintercept=mean_dist, size=1, color="red")+
350
+ geom_vline(xintercept=median_dist,
351
+ size=1, color="blue", linetype = 2)+
352
+ xlim(c(0,50))+
353
+ theme(legend.position = "none") +
354
+ labs(x = "Distancias medias, en km.", y = "Ocurrencia de distritos", title = paste0("Distancia Media: ", round(mean_dist, 2), " km. - Distancia Mediana: ", round(median_dist, 2), " km.")), tooltip="Distrito"
355
+ )%>%
356
+ layout(xaxis = list(autorange = TRUE),
357
+ yaxis = list(autorange = TRUE))
358
+
359
+ })
360
+
361
+
362
+ ```
363
+
364
+ ### Flujos Inter e Intradistrito
365
+
366
+ ```{r}
367
+ city_sel <- reactive(input$city_sel)
368
+ fecha_sel <- reactive(input$date1)
369
+
370
+ renderPlotly({
371
+ plotly_viajes <- viajes %>% filter(city == input$city_sel) %>%
372
+ group_by(date, start_polygon_name, end_polygon_name) %>%
373
+ summarise(viajes = sum(viajes)) %>%
374
+ mutate(intrap = if_else(start_polygon_name==end_polygon_name,
375
+ "Flujo Intradistrito",
376
+ "Flujo Interdistrito")) %>%
377
+ group_by(date, intrap) %>%
378
+ summarise(viajes = sum(viajes))
379
+
380
+ ggplotly(ggplot(plotly_viajes) +
381
+ geom_line(aes(date, viajes/1000, color = intrap)) +
382
+ labs(color = NULL, x = NULL, y = "Pers. que viajan (en miles)"),
383
+ tooltip="viajes/1000")})
384
+ ```
385
+
386
+
387
+ # Matriz Origen y Destino
388
+
389
+ ## Column {data-width="350"}
390
+
391
+ ### Matriz origen y destino
392
+
393
+ ```{r}
394
+
395
+ city_sel <- reactive(input$city_sel)
396
+ fecha_sel <- reactive(input$date1)
397
+
398
+ highcharter::renderHighchart({
399
+ if (input$mismodest){
400
+ plot <- viajes %>%
401
+ dplyr::filter(city == input$city_sel, date == input$date1) %>%
402
+ dplyr::filter(start_polygon_name != end_polygon_name) %>%
403
+ group_by(start_polygon_name, end_polygon_name) %>%
404
+ summarize(viajes = sum(viajes, na.rm=T)) %>%
405
+ filter(viajes > input$tolerance)
406
+ }else{
407
+ plot <- viajes %>%
408
+ dplyr::filter(city == input$city_sel, date == input$date1) %>%
409
+ group_by(start_polygon_name, end_polygon_name) %>%
410
+ summarize(viajes = sum(viajes, na.rm=T)) %>%
411
+ filter(viajes > input$tolerance)
412
+ }
413
+
414
+ plot %>% mutate(viajes = round(viajes)) %>% uncount(viajes) %>% data_to_sankey() %>% mutate(to = paste(" ", to)) %>% hchart(type = "sankey")
415
+ })
416
+
417
+ ```
418
+
419
+
420
+ ### Pares origen y destino
421
+
422
+ ```{r}
423
+
424
+ city_sel <- reactive(input$city_sel)
425
+ fecha_sel <- reactive(input$date1)
426
+
427
+ renderPlotly({
428
+ arrows <- viajes %>% filter(!is.na(geometry),
429
+ city == input$city_sel,
430
+ date == input$date1,
431
+ start_polygon_name != end_polygon_name,
432
+ viajes > input$tolerance) %>%
433
+ mutate(geometry = gsub("LINESTRING \\(", "", geometry),
434
+ geometry = gsub(",", "", geometry),
435
+ geometry = gsub(")", "", geometry),
436
+ lonlats = strsplit(geometry, "\\s+"))
437
+
438
+ arrows$xstart <- sapply(arrows$lonlats,"[[",1) %>% as.numeric
439
+ arrows$ystart <- sapply(arrows$lonlats,"[[",2) %>% as.numeric
440
+ arrows$xend <- sapply(arrows$lonlats,"[[",3) %>% as.numeric
441
+ arrows$yend <- sapply(arrows$lonlats,"[[",4) %>% as.numeric
442
+
443
+ geo_sf <- geo[which(geo$city==input$city_sel),]
444
+ bbox <- st_bbox(geo_sf)
445
+
446
+ p <- ggplot(arrows)+
447
+ geom_sf(data = geo_sf, fill = NA, size = .3) +
448
+ geom_segment(aes(x=xstart, y=ystart, xend=xend, yend=yend, size = viajes, text = paste0(start_polygon_name, " - ", end_polygon_name, " - ", round(viajes), " viajes.")), color = NA) +
449
+ scale_size(range=c(.5,1.5)) +
450
+ xlim(bbox$xmin, bbox$xmax) +
451
+ ylim(bbox$ymin, bbox$ymax) +
452
+ theme_void() + theme(legend.position = "none",
453
+ axis.line = element_blank())
454
+
455
+ mypal <- colorRampPalette(c("white", "darkblue"))(30)
456
+ map2color<-function(x,pal,limits=NULL){
457
+ if(is.null(limits)) limits=range(x)
458
+ pal[findInterval(x,seq(limits[1],limits[2],length.out=length(pal)+1), all.inside=TRUE)]
459
+ }
460
+
461
+
462
+ g <- ggplotly(p, tooltip = c("text")) %>%
463
+ add_annotations( x = ~arrows$xend,
464
+ y = ~arrows$yend,
465
+ xref = "x", yref = "y",
466
+ axref = "x", ayref = "y",
467
+ text = "",
468
+ showarrow = T,
469
+ ax = ~arrows$xstart,
470
+ ay = ~arrows$ystart,
471
+ arrowcolor = ~map2color(log(arrows$viajes), mypal),
472
+ arrowwidth = ~rescale(arrows$viajes, 2, 20),
473
+ borderwidth = ~rescale(arrows$viajes, 0, 10))
474
+
475
+ g
476
+ })
477
+
478
+
479
+ ```
480
+
481
+ # Metodología
482
+
483
+ ## Column {data-width="650"}
484
+
485
+ ### Metodología aplicada en el presente trabajo
486
+
487
+ Toda la información presentada es recolectada desde los datasets de **Facebook Disaster Maps** y del Mapa de Población de Alta Definición de Facebook. También se utiliza información de **Open Street Maps** y **GADM**.
488
+
489
+
490
+ Cuando las personas usan la aplicación de Facebook con los servicios de ubicación habilitados, sus latitudes y longitudes se reciben en intervalos regulares. La información de ubicación se usa de varias maneras, como ofrecer una función desde la aplicación o contenido que sea más relevante para las personas. Los __Facebook disaster maps__ brindan información sobre dónde se encuentran las personas y cómo se mueven. La información se presenta en dos niveles: por tile (cuadrados) o por región administrativa (como los censos poblacionales).
491
+
492
+ La región se divide en una grilla (tiles) de 2 km. Facebook detecta las coordenadas de las personas y las clasifica según el tile en el que ha estado más tiempo en las ocho horas anteriores al tiempo de la toma de datos. Por ejemplo, si el conjunto de datos es referido a las 8 a. m. y la persona estaba en su casa, Facebook clasificará a esa persona en ese tile y le asignará "origen". Si la persona pasó la mayor parte de las próximas ocho horas en otro Tile, esa persona aparecerá en los datos agregados del segundo cuadrado como "destino". Si aparece en el mismo tile, el destino será ese mismo tile.
493
+
494
+ __Sesgo de población__
495
+
496
+ Desde el punto de vista de las regiones administrativas, es posible que existan diferencias en cuanto al uso de las aplicaciones de Facebook y/o la aceptación del uso de la localización geoespacial. Por lo tanto, tratamos de eliminar ese sesgo tomando sumas de todos los viajes de cada uno de los orígenes y comparándolos geoespacialmente con la población correspondiente de cada región provieniente del dataset de High Resolution Density Maps.
497
+
498
+ De allí se extraen factores de expansión o pesos que aplicamos a los viajes de cada región administrativa para tener una noción más “real” de los flujos de personas entre partidos o municipios.
499
+
500
+ __Viajes cortos o nulos__
501
+
502
+ También se quitan del análisis, para tener un dato más cercano a la realidad de las personas que "no se movieron", aquellas personas que viajan que comienzan y terminan su viaje en el mismo tile. Para esto, se toman las personas que tienen como origen y como destino la misma región administrativa pero distinto tile. Luego, se aplican dos factores de expansión: el primero de acuerdo a la cantidad de personas que viajan intrapartido que figura en el dataset de Movement Between Administrative Regions y el segundo según la población (como en el caso anterior), para luego sumarse a la cantidad de personas que viajan interpartido entre regiones administrativas
503
+
504
+ El flujo de trabajo se puede representar de la siguiente manera, para cada uno de los partidos o municipios (más técnicamente llamados regiones administrativas), pertenecientes a la zona de las ciudades analizadas.
505
+
506
+
507
+ <div>
508
+
509
+ ```{r picture3, echo = F, fig.cap = "Flujograma", out.width = '100%', fig.pos="H"}
510
+ knitr::include_graphics("flujograma.png")
511
+ ```
512
+
513
+ </div>
514
+
515
+
516
+
R/data/.DS_Store ADDED
Binary file (6.15 kB). View file
 
R/data/.data_def_v5.RData.icloud ADDED
Binary file (169 Bytes). View file
 
R/flujograma.png ADDED
R/github.Rmd ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Dockerizando Shiny"
3
+ output: github_document
4
+ ---
5
+
6
+ ```{r setup, include=FALSE}
7
+ knitr::opts_chunk$set(echo = TRUE)
8
+ ```
9
+
10
+ ## Introducción
11
+
12
+ Muchas veces, la versión estándar de los servidores gratuitos de Shiny no alcanzan para nuestras hermosas creaciones en R. La limitación que más me molestaba era la de las 5 instancias activas simultáneamente. Para entornos de test quizá no estaba mal, pero una vez que la aplicación llega a ámbitos más profesionales donde probablemente más personas utilicen la app al mismo tiempo es más difícil.
13
+
14
+ En este breve tutorial llevaremos una apliación de Shiny a un repositorio Docker y de allí mismo haremos un deploy a un servicio de Google Cloud con más RAM y CPUs disponibles.
15
+
16
+ ## Ingredientes
17
+
18
+ - Una aplicación Shiny, .Rmd, flexdashboard (como en este ejemplo), etc.
19
+ - Docker y una cuenta Docker para subir la imagen a un repositorio.
20
+ - Cuenta en Google Cloud y las APIs básicas activas (el proceso es bastante intuitivo).
21
+
22
+ ## Instalando Docker
23
+
24
+ Para instalar Docker, se obtiene el archivo desde la página oficial y se instala normalmente. En OSX, como es mi caso, después de descargar el .dmg se instala de la siguiente manera desde la terminal:
25
+
26
+ ```{bash, eval = FALSE}
27
+ sudo hdiutil attach Docker.dmg
28
+ sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
29
+ sudo hdiutil detach /Volumes/Docker
30
+ ```
31
+
32
+ ## Creando el Dockerfile
33
+
34
+ El archivo Dockerfile es un conjunto de líneas de texto que configuran los paquetes que Docker va a utilizar para generar el entorno de nuestra aplicación y es donde también le daremos "órdenes" para ejecutar la app.
35
+
36
+ En mi caso, creé una carpeta que se llama "Docker" con dos elementos: una carpeta llamada "R" donde está todo lo relacionado a la aplicación Shiny y el propio archivo "Dockerfile" (así, sin puntos, ni extensiones, ni cosas raras).
37
+
38
+ En mi caso, el archivo Dockerfile contiene las siguientes líneas, comentadas punto a punto para entender mejor:
39
+
40
+ ```{bash, eval = FALSE}
41
+ FROM rocker/shiny:latest # Se instala el entorno Shiny y R
42
+
43
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \ # Se instalan librerías necesarias
44
+ libgdal-dev \ # Ojo que probablemente necesites más o menos
45
+ libproj-dev \ # depende de los paquetes que uses
46
+ libgeos-dev \
47
+ default-libmysqlclient-dev \
48
+ libmysqlclient-dev \
49
+ libudunits2-dev \
50
+ netcdf-bin \
51
+ libxml2-dev \
52
+ libcairo2-dev \
53
+ libsqlite3-dev \
54
+ libpq-dev \
55
+ libssh2-1-dev \
56
+ unixodbc-dev \
57
+ libcurl4-openssl-dev \
58
+ libssl-dev
59
+
60
+ RUN apt-get install pandoc # Tenía problemas con Pandoc e instalarlo por separado lo solucionó
61
+
62
+ RUN apt-get update && \ # Actualizar paquetes y limpiar
63
+ apt-get upgrade -y && \
64
+ apt-get clean
65
+
66
+ # Se instalan los paquetes necesarios
67
+ # notar que ya estamos ejecutando COMANDOS DE R dentro del Docker
68
+
69
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse',
70
+ 'flexdashboard', 'leaflet', 'sf', 'ggalluvial',
71
+ 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
72
+
73
+ RUN R -e "install.packages(pkgs=c('devtools'),
74
+ repos='https://cran.rstudio.com/')"
75
+
76
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
77
+
78
+ RUN mkdir /root/app # Creamos la carpeta de la app
79
+
80
+ COPY R /root/shiny_save # Copia la carpeta "R" a la carpeta shiny_save
81
+
82
+ EXPOSE 3838 #Importantísimo: asignarle un puerto de comunicación a la app.
83
+ #Después no vamos a poder hacer nada si no se expone el puerto
84
+
85
+ # Se corre la app (en este caso, al ser un flexdashboard
86
+ # tengo que ejecutarlo con rmarkdown, si no sería shiny::runApp(...)).
87
+ # Como argumento extra le paso el puerto abierto.
88
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd',
89
+ shiny_args=list(host='0.0.0.0', port=3838))"]
90
+
91
+ ```
92
+
93
+ ## Construcción de la imagen
94
+
95
+ Una vez que tengamos el dockerfile y la carpeta con todos los archivos que necesita la app para correr, pasamos a construir la imagen de Docker propiamente dicha. Para esto, abrimos la consola y ejecutamos:
96
+
97
+ ```{bash, eval = FALSE}
98
+ docker login
99
+ ```
100
+ Nos pedirá el usuario y la contraseña de Docker. La escribimos y luego:
101
+
102
+ ```{bash, eval = FALSE}
103
+ docker build -t nombreapp .
104
+ ```
105
+
106
+ Ese comando construirá una imagen llamada "nombreapp" con los parámetros del dockerfile (para eso el punto al final). Esta imagen contiene el entorno R y todo el código que hemos usado, dentro de un sistema Linux.
107
+ Es por esto que probablemente tarde bastante en ejecutarse la primera vez (a mí me lleva aproximadamente 3 horas), obviamente dependiendo la complejidad de la app. Si hacemos modificaciones al código de R de la aplicación y pusheamos los cambios (ejecutando los mismos parámetros), la construcción tarda muchísimo menos ya que la mayoría de las capas del entorno son las mismas y no se modifican. Esto es algo que me encanta de Docker. El trabajo se divide en "capas", por lo que si corregís una parte pequeña del código, el build y el push de la app tardan segundos.
108
+
109
+
110
+ ```{bash, eval = FALSE}
111
+ docker tag nombreapp usuario/nombreapp:latest
112
+ ```
113
+
114
+ "Tagueamos" la app dentro del repositorio público que estamos por crear. Es como decir que lo estamos "seleccionando" para subirlo. El repositorio se llamará usuario/nombreapp. La última parte ":latest" es un nombre que podemos poner para identificar a la última versión de la app. Lo podemos ir modificando para cada versión.
115
+
116
+ ```{bash, eval = FALSE}
117
+ docker push usuario/nombreapp:latest
118
+ ```
119
+
120
+ Se pushea la imagen al repositorio, tal como si fuera GitHub.
121
+
122
+ ## Deploy en Google Cloud
123
+
124
+ Una vez creado el proyecto, procedemos a abrir la consola de comandos dentro de la misma Web de Google.
125
+ Lo primero que debemos hacer es loguearnos con nuestras credenciales de Docker.
126
+
127
+ ```{bash, eval = FALSE}
128
+ docker login
129
+ ```
130
+
131
+ Posteriormente, "traemos" la imagen del Docker a nuestro proyecto de Google Cloud con los últimos cambios.
132
+
133
+ ```{bash, eval = FALSE}
134
+ docker pull usuario/nombreapp:latest
135
+ ```
136
+ Luego, análogamente a lo que hacíamos en Docker, se "taguea" la imagen a pushear, lo único es que esta vez no pusheamos al repo de Docker sino al container de imágenes de Google Cloud. Reemplazá PROJECT_ID por el ID de tu proyecto.
137
+
138
+ ```{bash, eval = FALSE}
139
+ docker tag usuario/nombreapp:latest gcr.io/PROJECT_ID/usuario/nombreapp:latest
140
+ ```
141
+
142
+ Por último, pusheamos la imagen.
143
+
144
+ ```{bash, eval = FALSE}
145
+ docker push gcr.io/PROJECT_ID/usuario/nombreapp:latest
146
+ ```
147
+
148
+ ## Creación de la instancia del servicio
149
+
150
+ Ya casi terminamos. Luego de pusheada la imagen, en la pestaña de Google Cloud Run clickeás "Crear Servicio". Esto nos habilitará una máquina virtual para hacer el deploy y que podamos compartir la app a un montón de gente.
151
+
152
+ Donde dice "Url de la imagen" poné seleccionar y se van a desplegar las opciones de imagen. Nos va a aparecer la imagen "nombreapp" con la versión "latest". Usamos esa y completamos los parámetros con lo que necesite el proyecto.
153
+
154
+ **IMPORTANTE:** En la parte de más abajo donde dice "CONTENEDOR", completá dentro del campo "Puerto del contenedor" el puerto 3838 o el que hayas elegido en la construcción del Dockerfile, ya que sin esto Google va a enrutar mal el puerto generando errores.
155
+
156
+ Esperás unos segundos a que Google cree la instancia y listo! Ya está la app deployada en Google Cloud Run. Cualquiera va a poder acceder a ella a través de la URL que te muestra arriba del panel.
157
+
158
+
R/github.md ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Dockerizando Shiny
2
+ ================
3
+
4
+ ## Introducción
5
+
6
+ Muchas veces, la versión estándar de los servidores gratuitos de Shiny
7
+ no alcanzan para nuestras hermosas creaciones en R. La limitación que
8
+ más me molestaba era la de las 5 instancias activas simultáneamente.
9
+ Para entornos de test quizá no estaba mal, pero una vez que la
10
+ aplicación llega a ámbitos más profesionales donde probablemente más
11
+ personas utilicen la app al mismo tiempo es más difícil.
12
+
13
+ En este breve tutorial llevaremos una apliación de Shiny a un
14
+ repositorio Docker y de allí mismo haremos un deploy a un servicio de
15
+ Google Cloud con más RAM y CPUs disponibles.
16
+
17
+ ## Ingredientes
18
+
19
+ - Una aplicación Shiny, .Rmd, flexdashboard (como en este ejemplo),
20
+ etc.
21
+ - Docker y una cuenta Docker para subir la imagen a un repositorio.
22
+ - Cuenta en Google Cloud y las APIs básicas activas (el proceso es
23
+ bastante intuitivo).
24
+
25
+ ## Instalando Docker
26
+
27
+ Para instalar Docker, se obtiene el archivo desde la página oficial y se
28
+ instala normalmente. En OSX, como es mi caso, después de descargar el
29
+ .dmg se instala de la siguiente manera desde la terminal:
30
+
31
+ ``` bash
32
+ sudo hdiutil attach Docker.dmg
33
+ sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
34
+ sudo hdiutil detach /Volumes/Docker
35
+ ```
36
+
37
+ ## Creando el Dockerfile
38
+
39
+ El archivo Dockerfile es un conjunto de líneas de texto que configuran
40
+ los paquetes que Docker va a utilizar para generar el entorno de nuestra
41
+ aplicación y es donde también le daremos “órdenes” para ejecutar la app.
42
+
43
+ En mi caso, creé una carpeta que se llama “Docker” con dos elementos:
44
+ una carpeta llamada “R” donde está todo lo relacionado a la aplicación
45
+ Shiny y el propio archivo “Dockerfile” (así, sin puntos, ni extensiones,
46
+ ni cosas raras).
47
+
48
+ En mi caso, el archivo Dockerfile contiene las siguientes líneas,
49
+ comentadas punto a punto para entender mejor:
50
+
51
+ ``` bash
52
+ FROM rocker/shiny:latest # Se instala el entorno Shiny y R
53
+
54
+ RUN apt-get update -qq && apt-get -y --no-install-recommends install \ # Se instalan librerías necesarias
55
+ libgdal-dev \ # Ojo que probablemente necesites más o menos
56
+ libproj-dev \ # depende de los paquetes que uses
57
+ libgeos-dev \
58
+ default-libmysqlclient-dev \
59
+ libmysqlclient-dev \
60
+ libudunits2-dev \
61
+ netcdf-bin \
62
+ libxml2-dev \
63
+ libcairo2-dev \
64
+ libsqlite3-dev \
65
+ libpq-dev \
66
+ libssh2-1-dev \
67
+ unixodbc-dev \
68
+ libcurl4-openssl-dev \
69
+ libssl-dev
70
+
71
+ RUN apt-get install pandoc # Tenía problemas con Pandoc e instalarlo por separado lo solucionó
72
+
73
+ RUN apt-get update && \ # Actualizar paquetes y limpiar
74
+ apt-get upgrade -y && \
75
+ apt-get clean
76
+
77
+ # Se instalan los paquetes necesarios
78
+ # notar que ya estamos ejecutando COMANDOS DE R dentro del Docker
79
+
80
+ RUN R -e "install.packages(pkgs=c('shiny','tidyverse',
81
+ 'flexdashboard', 'leaflet', 'sf', 'ggalluvial',
82
+ 'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
83
+
84
+ RUN R -e "install.packages(pkgs=c('devtools'),
85
+ repos='https://cran.rstudio.com/')"
86
+
87
+ RUN R -e "devtools::install_github('rstudio/leaflet')"
88
+
89
+ RUN mkdir /root/app # Creamos la carpeta de la app
90
+
91
+ COPY R /root/shiny_save # Copia la carpeta "R" a la carpeta shiny_save
92
+
93
+ EXPOSE 3838 #Importantísimo: asignarle un puerto de comunicación a la app.
94
+ #Después no vamos a poder hacer nada si no se expone el puerto
95
+
96
+ # Se corre la app (en este caso, al ser un flexdashboard
97
+ # tengo que ejecutarlo con rmarkdown, si no sería shiny::runApp(...)).
98
+ # Como argumento extra le paso el puerto abierto.
99
+ CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd',
100
+ shiny_args=list(host='0.0.0.0', port=3838))"]
101
+ ```
102
+
103
+ ## Construcción de la imagen
104
+
105
+ Una vez que tengamos el dockerfile y la carpeta con todos los archivos
106
+ que necesita la app para correr, pasamos a construir la imagen de Docker
107
+ propiamente dicha. Para esto, abrimos la consola y ejecutamos:
108
+
109
+ ``` bash
110
+ docker login
111
+ ```
112
+
113
+ Nos pedirá el usuario y la contraseña de Docker. La escribimos y luego:
114
+
115
+ ``` bash
116
+ docker build -t nombreapp .
117
+ ```
118
+
119
+ Ese comando construirá una imagen llamada “nombreapp” con los parámetros
120
+ del dockerfile (para eso el punto al final). Esta imagen contiene el
121
+ entorno R y todo el código que hemos usado, dentro de un sistema
122
+ Linux.
123
+ Es por esto que probablemente tarde bastante en ejecutarse la primera
124
+ vez (a mí me lleva aproximadamente 3 horas), obviamente dependiendo la
125
+ complejidad de la app. Si hacemos modificaciones al código de R de la
126
+ aplicación y pusheamos los cambios (ejecutando los mismos parámetros),
127
+ la construcción tarda muchísimo menos ya que la mayoría de las capas del
128
+ entorno son las mismas y no se modifican. Esto es algo que me encanta de
129
+ Docker. El trabajo se divide en “capas”, por lo que si corregís una
130
+ parte pequeña del código, el build y el push de la app tardan segundos.
131
+
132
+ ``` bash
133
+ docker tag nombreapp usuario/nombreapp:latest
134
+ ```
135
+
136
+ “Tagueamos” la app dentro del repositorio público que estamos por crear.
137
+ Es como decir que lo estamos “seleccionando” para subirlo. El
138
+ repositorio se llamará usuario/nombreapp. La última parte “:latest” es
139
+ un nombre que podemos poner para identificar a la última versión de la
140
+ app. Lo podemos ir modificando para cada versión.
141
+
142
+ ``` bash
143
+ docker push usuario/nombreapp:latest
144
+ ```
145
+
146
+ Se pushea la imagen al repositorio, tal como si fuera GitHub.
147
+
148
+ ## Deploy en Google Cloud
149
+
150
+ Una vez creado el proyecto, procedemos a abrir la consola de comandos
151
+ dentro de la misma Web de Google. Lo primero que debemos hacer es
152
+ loguearnos con nuestras credenciales de Docker.
153
+
154
+ ``` bash
155
+ docker login
156
+ ```
157
+
158
+ Posteriormente, “traemos” la imagen del Docker a nuestro proyecto de
159
+ Google Cloud con los últimos cambios.
160
+
161
+ ``` bash
162
+ docker pull usuario/nombreapp:latest
163
+ ```
164
+
165
+ Luego, análogamente a lo que hacíamos en Docker, se “taguea” la imagen a
166
+ pushear, lo único es que esta vez no pusheamos al repo de Docker sino al
167
+ container de imágenes de Google Cloud. Reemplazá PROJECT_ID por el ID de
168
+ tu proyecto.
169
+
170
+ ``` bash
171
+ docker tag usuario/nombreapp:latest gcr.io/PROJECT_ID/usuario/nombreapp:latest
172
+ ```
173
+
174
+ Por último, pusheamos la imagen.
175
+
176
+ ``` bash
177
+ docker push gcr.io/PROJECT_ID/usuario/nombreapp:latest
178
+ ```
179
+
180
+ ## Creación de la instancia del servicio
181
+
182
+ Ya casi terminamos. Luego de pusheada la imagen, en la pestaña de Google
183
+ Cloud Run clickeás “Crear Servicio”. Esto nos habilitará una máquina
184
+ virtual para hacer el deploy y que podamos compartir la app a un montón
185
+ de gente.
186
+
187
+ Donde dice “Url de la imagen” poné seleccionar y se van a desplegar las
188
+ opciones de imagen. Nos va a aparecer la imagen “nombreapp” con la
189
+ versión “latest”. Usamos esa y completamos los parámetros con lo que
190
+ necesite el proyecto.
191
+
192
+ **IMPORTANTE:** En la parte de más abajo donde dice “CONTENEDOR”,
193
+ completá dentro del campo “Puerto del contenedor” el puerto 3838 o el
194
+ que hayas elegido en la construcción del Dockerfile, ya que sin esto
195
+ Google va a enrutar mal el puerto generando errores.
196
+
197
+ Esperás unos segundos a que Google cree la instancia y listo! Ya está la
198
+ app deployada en Google Cloud Run. Cualquiera va a poder acceder a ella
199
+ a través de la URL que te muestra arriba del panel.
R/github_files/figure-gfm/pressure-1.png ADDED
R/global.R ADDED
@@ -0,0 +1 @@
 
 
1
+ #load("/root/shiny_save/data_def_v5.RData")
R/omu.png ADDED