Upload 45 files
Browse files- .gitattributes +1 -0
- Dockerfile +42 -0
- R/.DS_Store +0 -0
- R/.RData +3 -0
- R/.Rhistory +6 -0
- R/.Rproj.user/E43CA360/pcs/files-pane.pper +9 -0
- R/.Rproj.user/E43CA360/pcs/source-pane.pper +3 -0
- R/.Rproj.user/E43CA360/pcs/windowlayoutstate.pper +14 -0
- R/.Rproj.user/E43CA360/pcs/workbench-pane.pper +5 -0
- R/.Rproj.user/E43CA360/rmd-outputs +10 -0
- R/.Rproj.user/E43CA360/saved_source_markers +1 -0
- R/.Rproj.user/E43CA360/sources/per/t/21382A5A +26 -0
- R/.Rproj.user/E43CA360/sources/per/t/21382A5A-contents +199 -0
- R/.Rproj.user/E43CA360/sources/per/t/7215F060 +26 -0
- R/.Rproj.user/E43CA360/sources/per/t/7215F060-contents +42 -0
- R/.Rproj.user/E43CA360/sources/per/t/74244C1E +27 -0
- R/.Rproj.user/E43CA360/sources/per/t/74244C1E-contents +158 -0
- R/.Rproj.user/E43CA360/sources/per/t/EB610EE9 +26 -0
- R/.Rproj.user/E43CA360/sources/per/t/EB610EE9-contents +1 -0
- R/.Rproj.user/E43CA360/sources/per/t/FDAA923B +28 -0
- R/.Rproj.user/E43CA360/sources/per/t/FDAA923B-contents +523 -0
- R/.Rproj.user/E43CA360/sources/prop/79CB3A6C +7 -0
- R/.Rproj.user/E43CA360/sources/prop/7DEF3E17 +6 -0
- R/.Rproj.user/E43CA360/sources/prop/B3A3F0F6 +6 -0
- R/.Rproj.user/E43CA360/sources/prop/D2B7D434 +6 -0
- R/.Rproj.user/E43CA360/sources/prop/E88E6722 +8 -0
- R/.Rproj.user/E43CA360/sources/prop/INDEX +5 -0
- R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/E43CA360937CAF11/chunks.json +1 -0
- R/.Rproj.user/shared/notebooks/09ED8104-dash_v2/1/s/chunks.json +1 -0
- R/.Rproj.user/shared/notebooks/AE375184-github/1/E43CA360937CAF11/chunks.json +1 -0
- R/.Rproj.user/shared/notebooks/AE375184-github/1/s/chunks.json +1 -0
- R/.Rproj.user/shared/notebooks/patch-chunk-names +0 -0
- R/.Rproj.user/shared/notebooks/paths +2 -0
- R/.data_def_v5.RData.icloud +0 -0
- R/Dash BID CAF.Rproj +13 -0
- R/bid.png +0 -0
- R/caf.png +0 -0
- R/dash_v2.Rmd +516 -0
- R/data/.DS_Store +0 -0
- R/data/.data_def_v5.RData.icloud +0 -0
- R/flujograma.png +0 -0
- R/github.Rmd +158 -0
- R/github.md +199 -0
- R/github_files/figure-gfm/pressure-1.png +0 -0
- R/global.R +1 -0
- 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
|