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)
df <- data.frame(V1 = c(.2,.1,.11,.15,.4), V2 = c(30,21,43,65,29))
svar <- "V2"
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.
svar <- sym("V2")
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)
df <- data.frame(state = c("MN","CA","CA","MN","MN","CA"),
city = c("Deluth","San Diego","Los Angeles","St. Paul","Deluth","Oakland"))
ui <- basicPage(
selectInput("state", "Select State",
choices = unique(df$state)),
selectInput("city", "Select City",
choices = NULL)
)
server <- function(input, output, session) {
observeEvent(input$state, {
cities <- df$city[df$state == input$state]
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.
geodata::world(resolution = 5, level=0, path="data")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.
world_sf <- readRDS("data/gadm36_adm0_r5_pk.rds") %>%
terra::vect() %>%
sf::st_as_sf() %>%
sf::st_transform(crs = "+proj=longlat +datum=WGS84")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:
rates <- df %>%
select(iso3,rate)
world_sf <- left_join(world_sf, rates, by = c("GID_0" = "iso3"))Then just feed this joined data to leaflet. The result
is a beautiful choropleth map.
2022 Ingrid Lagos