I built this
app as an exercise to practice R, R markdown, and Shiny. It was an
exercise in joining WHO
homicide, UN
population and UN
GDP data, and in getting a lightweight world polygon geodata file
with iso alpha3 country codes to use with leaflet
.
I learned the dplyr
grammar has some quirks, like having
to use .data[[var]]
or {{var}}
when supplying
a variable through a character vector or function argument. It’s
documented here.
The following code outputs an empty frame without an error as if the
condition was not met.
library(dplyr)
<- data.frame(V1 = c(.2,.1,.11,.15,.4), V2 = c(30,21,43,65,29))
df <- "V2"
svar
%>%
df filter(svar < 30)
## [1] V1 V2
## <0 rows> (or 0-length row.names)
It seems the problem stems from drawing an equivalence between the
data-variable df$V2
and the env-variable svar
.
They are not equivalent. Finding the data-variable in the current data
frame solves the problem.
%>%
df filter(.data[[svar]] < 30)
## V1 V2
## 1 0.1 21
## 2 0.4 29
The following also works, but it seems bang-bang
precedes .data
. Not sure.
%>%
df filter(!!sym(svar) < 30)
## V1 V2
## 1 0.1 21
## 2 0.4 29
This also works and it saves some typing if you have to call the variable a few times.
<- sym("V2")
svar %>%
df filter(!!svar < 30)
## V1 V2
## 1 0.1 21
## 2 0.4 29
I was confused since other dplyr
functions do not need
such wrapping.
%>%
df select(svar)
## V2
## 1 30
## 2 21
## 3 43
## 4 65
## 5 29
This works as is. Seems inconsistent to me.
I learned there are many ways to build contextual dropdown menus in Shiny. After trying the “update” and “dynamic UI” options, I chose the update method. Here is a simple example.
library(shiny)
<- data.frame(state = c("MN","CA","CA","MN","MN","CA"),
df city = c("Deluth","San Diego","Los Angeles","St. Paul","Deluth","Oakland"))
<- basicPage(
ui selectInput("state", "Select State",
choices = unique(df$state)),
selectInput("city", "Select City",
choices = NULL)
)
<- function(input, output, session) {
server observeEvent(input$state, {
<- df$city[df$state == input$state]
cities updateSelectInput(session, "city", choices = unique(cities))
})
}
shinyApp(ui, server)
Note that session
must be added to the server and update
functions.
For leaflet choropleths world maps you need a data object that has the polygon information of each country. There are many ways to get this. I settled for using the geodata package to get an RSD file from GADM.
::world(resolution = 5, level=0, path="data") geodata
This function is meant to be run once. It saves a file in the path
you indicate. I don’t include this in the app. In the app, I read the
file, transform it into a SpatVector
object, then into an
sf
object. The object is then ready to be fed to
leaflet.
<- readRDS("data/gadm36_adm0_r5_pk.rds") %>%
world_sf ::vect() %>%
terra::st_as_sf() %>%
sf::st_transform(crs = "+proj=longlat +datum=WGS84") sf
In this example, the world_sf
object has two variables,
GID_0
the ISO3 alpha country codes, and NAME_0
the country name. You can use the ISO3 country codes to easily merge
data that has ISO3 codes.
Suppose you have a dataframe with a variable rate
that
has the homicide rates, and a variable iso3
that has the
ISO3 country codes. You can join the data like this:
<- df %>%
rates select(iso3,rate)
<- left_join(world_sf, rates, by = c("GID_0" = "iso3")) world_sf
Then just feed this joined data to leaflet
. The result
is a beautiful choropleth map.
2022 Ingrid Lagos