7 Interactive communication

STATUS: Under construction.

Required reading

Required viewing

Recommended reading

Key concepts/skills/etc

  • Building a website using within the R environment using (in order of ease): postcards, distill, and blogdown.
  • Thinking of about how we can take advantage of interaction in maps, and broadening the data that we make available via interactive maps, while still telling a clear story.

Key libraries

  • blogdown
  • distill
  • leaflet
  • mapdeck
  • postcards
  • tidyverse
  • usethis

Key functions/etc

7.1 Making a website

7.1.1 Introduction

A website is a critical part of communication. For instance, it is a place to bring together everything that you’ve done, and it allows you some control of your online presence. You need a website.

One way to make a website is to use the blogdown package (Xie, Dervieux, and Hill 2021). blogdown is a package that allows you to make websites (not just blogs, notwithstanding its name) largely within R Studio. It builds on Hugo, which is a popular tool for making websites. blogdown lets you freely and quickly get a website up-and-running. It is easy to add content from time-to-time. It integrates with R Markdown which lets you easily share your work. And the separation of content and styling allows you to relatively quickly change your website’s design.

However, blogdown is brittle. Because it is so dependent on Hugo, features that work today may not work tomorrow. Also, owners of Hugo templates can update them at any time, without thought to existing users. blogdown is great if you know what you’re doing and have a specific use-case, or style, in mind. However, recently there are two alternatives that are better starting points.

The first is distill (Allaire et al. 2021). Again, this is an R package that wraps around another framework, in this case Distill. However, in contrast to Hugo, Distill is more focused on common needs in data science, and is also only maintained by one group, so it can be a more stable choice. That said, the default distill site is fairly unremarkable. As such, here we recommend a third option.

The third option, and the one that we’ll start with, is postcards (Kross 2021). This is a tailored solution that creates simple biographical websites that look great. If you followed the earlier chapter and set-up GitHub, then you should literally be able to get a postcards website online in five minutes.

7.1.2 Postcards

To get started with postcards, we first need to install the packages.

install.packages('postcards')

You will want to create a new project for your website, so ‘File -> New Project -> New Directory -> Postcards Website.’ You’ll then get a pick a name and location for the project, and you can select a postcards theme. In this case I’ll choose ‘trestles,’ and you probably want to tick ‘Open in new session.’

That will open a new file and you should now click ‘Knit’ to build the site. The result will be a fairly great one-page website (Figure 7.1)!

Example of default Trestles website made with the `postcards` package

Figure 7.1: Example of default Trestles website made with the postcards package

At this point, we should update the basic content to match our own. For instance, here is the same website, but with my details (Figure 7.2).

Example of Trestles website with my own details

Figure 7.2: Example of Trestles website with my own details

When you’ve got the site how you’d like it, then you should add it to GitHub. GitHub will try to build a site, which was don’t want so you need to first add a hidden file by running this in the console:

file.create('.nojekyll')

Then the easiest way (assuming that you set everything up in earlier chapters) is to use the usethis package (Wickham and Bryan 2020).

usethis::use_git()
usethis::use_github()

The project will then be on your GitHub repo and you can use GitHub pages to host it: ‘Settings -> Pages’ and then change the source to ‘main’ or ‘master,’ depending on your settings.

7.1.3 Distill

To get started with distill (Allaire et al. 2021), we are going to build a framework around our postcards site, following Presmanes Hill (2021a) fairly closely (please go to Alison’s blogpost for the details). After that we’ll explore some of the aspects of distill that make it a nice choice, and mention some of the trade-offs that you make if you choose this option. First, we need to install distill.

install.packages('distill')

Again, create a new project for your website, so ‘File -> New Project -> New Directory -> Distill Blog’ (there’s not really much difference between the website and blog options).

You’ll then get a pick a name and location for the project, and you can set a title. Select ‘Configure for GitHub Pages’ and also ‘Open in a new session’ (if you forget to do any of this or change your mind then it’s not a big deal - these can always be changed ex post or you can just delete the directory and start again). It should look something like Figure 7.3.

Example settings for setting up Distill.

Figure 7.3: Example settings for setting up Distill.

At this point you can click ‘Build Website’ in the Build tab, and you’ll see the default website, which should look something like Figure 7.4.

Example of default Distill website.

Figure 7.4: Example of default Distill website.

Again, now we need to do the work to update things. The default for the ‘Distill Blog’ setting is that the blog is the homepage. We can change that. I really liked the bio page from earlier, so we could use that approach.

First change the name of the ‘index.Rmd’ file to ‘blog.Rmd.’ Then create a new ‘trestles’ page:

postcards::create_postcard(file = "index.Rmd", template = "trestles")

The trestles page that you just created will open, and you need to add the following line in the yaml.

site: distill::distill_website

In Figure 7.5 I added it to line 16 and then rebuilt the website.

Updating the yaml to change the homepage.

Figure 7.5: Updating the yaml to change the homepage.

We can make the same changes to the default content as earlier, updating the links, image, and bio. The advantage of using Distill is that we now have additional pages, not just a one-page website, and we also have a blog. By default, we have an ‘about’ page, but some other pages that may be useful, depending on your particular use-case, could include: ‘research,’ ‘teaching,’ ‘talks,’ ‘projects,’ ‘software,’ ‘datasets.’ For now, I’ll talk through adding and editing a page called ‘software.’

We can use the following function:

distill::create_article(file = 'software')

That will create and open an R Markdown document. To add it to the website, open ’_site.yml’ and then add a line to the ‘navbar’ (Figure 7.6(. After this is done then re-building the site will result in that software page having been added.

Adding another page to the website.

Figure 7.6: Adding another page to the website.

Continue with this process until you’re happy with your site. For instance, we may want to add our blog back. To do this follow the same pattern as before, but with ‘blog’ instance of ‘software.’

When you’re ready, you can get your website online in the same way as we did with the postcards site (i.e. push to GitHub and then use GitHub Pages).

Using the distill is a great option if you want a multi-page website, but still want a fairly controlled environment. There are a lot of options that you can change and the best place to start with that is to see Alison Hill’s blog post (Presmanes Hill 2021a), but the distill package homepage is also useful.

That said, distill is very opinionated. Until recently they didn’t even allow a different citation style! While it is a great option (and what I use for my own website), if you want something that is more flexible, then blogdown might be a better option.

7.1.4 Blogdown

Using blogdown (Xie, Dervieux, and Hill 2021) is more work than Google sites or Squarespace. It requires a little more knowledge than using a basic Wordpress site. And if you want to customise absolutely every aspect of your website, or need everything to be ‘just so’ then blogdown may not be for you. Further, blogdown is still under active development and various aspects may break in future releases. However, blogdown allows a variety and level of expression that is not possible with distill.

This post is a simplified version of Presmanes Hill (2021b) and Xie, Thomas, and Presmanes Hill (2021). It sticks to the basics and doesn’t require much decision-making. The purpose is to allow someone without much experience to use blogdown to get a website up-and-running. Head to those two resources once you’ve got a website working and want to dive a bit deeper.

We’ll need to install blogdown.

install.packages("blogdown")

Again, create a new project for your website, so ‘File -> New Project -> New Directory -> Website using blogdown.’ At this point you can set a name and location, and also select ‘Open in a new session.’ It should look something like Figure 7.7.

Example settings for setting up blogdown

Figure 7.7: Example settings for setting up blogdown

You can again click ‘Build Website’ from the ‘Build’ pane, but then an extra step is needed, of serving the site:

blogdown:::serve_site()

The site will show in the ‘Viewer’ pane (Figure 7.8).

Serving default blogdown site.

Figure 7.8: Serving default blogdown site.

At this point, the default website is being ‘served’ locally. This means that changes you make will be reflected in the website that you see in your Viewer pane. To see the website in a web browser, click ‘Show in new window’ button on the top left of the Viewer. That will open the website using the address that the R Studio also tells you.

You probably want to update the ‘About’ section. To do that go to ‘content -> about.md’ and add your own content. One nice aspect of blogdown is that it will automatically re-load the content when you save, so you should see your changes immediately show up.

You may also like to change the logo. You could do this by adding a square image to ‘public/images/’ and then changing the call to ‘logo.png’ in ‘config.yaml.’

When you’re happy with it, you can make your website public in the same way that is described for postcards.

This all said, the biggest advantage of using blogdown is that it allows us to use Hugo templates. This provides a large number of beautifully crafted websites. To pick a theme you can go to the Hugo themes page: https://themes.gohugo.io. There are hundreds of different themes. In general, most of them can be made to work with blogdown, but sometimes it can be a bit of a hassle to get them working.

One that I particularly like is Apéro: https://hugo-apero-docs.netlify.app. If you like that too, then you could use that theme by calling it when you create a new site. As a reminder, ‘File -> New Project -> New Directory -> Website using blogdown.’ At this point, in addition to setting the name and location, you can specify a theme. Specifically, in ‘Hugo theme’ field, you specify the GitHub username and repository, which in this case is ‘hugo-apero/apero’ (Figure 7.9).

Using the Apéro theme.

Figure 7.9: Using the Apéro theme.

7.2 Interactive maps

The nice thing about interactive maps is that you can let your users decide what they are interested in. Additionally, if there is a lot of information then you may like to leave it to your users as to selectively focus on what they are interested in. For instance, in the case of Canadian politics, some people will be interested in Toronto ridings, while others will be interested in Manitoba, etc. But it would be difficult to present a map that focuses on both of those, so an interactive map is a great option for allowing users to zoom in on what they want.

That said, it is important to be cognizant of what we are doing when we build maps, and more broadly, what is being done at scale to enable us to be able to build our own maps. For instance, with regard to Google, McQuire (2019) says:

Google began life in 1998 as a company famously dedicated to organising the vast amounts of data on the Internet. But over the last two decades its ambitions have changed in a crucial way. Extracting data such as words and numbers from the physical world is now merely a stepping-stone towards apprehending and organizing the physical world as data. Perhaps this shift is not surprising at a moment when it has become possible to comprehend human identity as a form of (genetic) ‘code.’ However, apprehending and organizing the world as data under current settings is likely to take us well beyond Heidegger’s ‘standing reserve’ in which modern technology enframed ‘nature’ as productive resource. In the 21st century, it is the stuff of human life itself—from genetics to bodily appearances, mobility, gestures, speech, and behaviour —that is being progressively rendered as productive resource that can not only be harvested continuously but subject to modulation over time.

Does this mean that we should not use or build interactive maps? Of course not. But it’s important to be aware of the fact that this is a frontier, and the boundaries of appropriate use are still being determined. Indeed, the literal boundaries of the maps themselves are being consistently determined and updated. The move to digital maps, compared with physical printed maps, means that it is actually possible for different users to be presented with different realities. For instance, ‘…Google routinely takes sides in border disputes. Take, for instance, the representation of the border between Ukraine and Russia. In Russia, the Crimean Peninsula is represented with a hard-line border as Russian-controlled, whereas Ukrainians and others see a dotted-line border. The strategically important peninsula is claimed by both nations and was violently seized by Russia in 2014, one of many skirmishes over control’ Bensinger (2020).

7.2.1 Leaflet

The leaflet package (Cheng, Karambelkar, and Xie 2021) is originally a JavaScript library of the same name that has been brought over to R. It makes it easy to make interactive maps. The basics are similar to the ggmap (Kahle and Wickham 2013) set-up, but of course after that, there are many, many, options.

Let’s redo the bike map from earlier, and possibly the interaction will allow us to see what the issue is with the data.

In the same way as a graph in ggplot begins with the ggplot() function, a map in the leaflet package begins with a call to the leaflet() function. This allows you to specify data, and a bunch of other options such as width and height. After this, we add ‘layers,’ in the same way that we added them in ggplot. The first layer that we’ll add is a tile with the function addTiles(). In this case, the default is from OpenStreeMap. After that we’ll add markers that show the location of each bike parking spot with addMarkers().

library(leaflet)
library(tidyverse)
#> ── Attaching packages ─────────────────── tidyverse 1.3.1 ──
#> ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
#> ✓ tibble  3.1.3     ✓ dplyr   1.0.7
#> ✓ tidyr   1.1.3     ✓ stringr 1.4.0
#> ✓ readr   2.0.0     ✓ forcats 0.5.1
#> ── Conflicts ────────────────────── tidyverse_conflicts() ──
#> x dplyr::filter() masks stats::filter()
#> x dplyr::lag()    masks stats::lag()

bike_data <- read_csv("outputs/data/bikes.csv")
#> Rows: 1400 Columns: 4
#> ── Column specification ────────────────────────────────────
#> Delimiter: ","
#> chr (1): street_address
#> dbl (3): latitude, longitude, number_of_spots
#> 
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

leaflet(data = bike_data) %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lng = bike_data$longitude, 
             lat = bike_data$latitude, 
             popup = bike_data$street_address,
             label = ~as.character(bike_data$number_of_spots))

There are two options here that may not be familiar. The first is ‘popup,’ and this is what happens when you click on the marker. In this example this is giving the address. The second is ‘label,’ which is what happens when you hover over the marker. In this example it is given the number of spots.

Let’s have another go, this time making a map of the fire stations in Toronto. We can use data from Open Data Toronto, via the opendatatoronto R package (Gelfand 2020). To ensure this book works, I will save and then use the dataset as at 13 May 2021, but you are able to get the up-to-date dataset using the link and the code.

library(opendatatoronto)
# Get starter code from: https://open.toronto.ca/dataset/fire-station-locations/
fire_stations_locations <- get_resource('9d1b7352-32ce-4af2-8681-595ce9e47b6e')
# Grab the lat and long - thanks https://stackoverflow.com/questions/47661354/converting-geometry-to-longitude-latitude-coordinates-in-r
fire_stations_locations <- 
  fire_stations_locations %>% 
  tidyr::extract(geometry, c('lon', 'lat'), '\\((.*), (.*)\\)', convert = TRUE)

write_csv(fire_stations_locations, "inputs/data/fire_stations_locations.csv")
fire_stations_locations <- read_csv("inputs/data/fire_stations_locations.csv")

head(fire_stations_locations)
#> # A tibble: 6 × 26
#>   `_id`    ID NAME     ADDRESS   ADDRESS_POINT_ID ADDRESS_ID
#>   <dbl> <dbl> <chr>    <chr>                <dbl>      <dbl>
#> 1     1    21 FIRE ST… 900 TAPS…          4236992     363382
#> 2     2    60 FIRE ST… 106 ASCO…           764237      70190
#> 3     3    61 FIRE ST… 65 HENDR…           819425     127148
#> 4     4    55 FIRE ST… 260 ADEL…         12763904     484214
#> 5     5    24 FIRE ST… 745 MEAD…          6349868     357277
#> 6     6    74 FIRE ST… 140 LANS…         10757599     157562
#> # … with 20 more variables: CENTRELINE_ID <dbl>,
#> #   MAINT_STAGE <chr>, ADDRESS_NUMBER <dbl>,
#> #   LINEAR_NAME_FULL <chr>, POSTAL_CODE <chr>,
#> #   GENERAL_USE <chr>, CLASS_FAMILY_DESC <chr>,
#> #   ADDRESS_ID_LINK <dbl>, PLACE_NAME <chr>, X <lgl>,
#> #   Y <lgl>, LATITUDE <lgl>, LONGITUDE <lgl>,
#> #   WARD_NAME <chr>, MUNICIPALITY_NAME <chr>, …

There is a lot of information here, but we’ll just plot the location of each fire station along with their name and address.

We will introduce a different type of marker here, which is circles. This will allow us to use different colours for the outcomes of each type. There are three possible outcomes: “Fire/Ambulance Stations” “Fire Station,” “Restaurant,” “Unknown.”

library(leaflet)

pal <- colorFactor("Dark2", domain = fire_stations_locations$GENERAL_USE %>% unique())

leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addCircleMarkers(
    data = fire_stations_locations,
    lng = fire_stations_locations$lon, 
    lat = fire_stations_locations$lat, 
    color = pal(fire_stations_locations$GENERAL_USE),
    popup = paste("<b>Name:</b>", as.character(fire_stations_locations$NAME), "<br>",
                  "<b>Address:</b>", as.character(fire_stations_locations$ADDRESS), "<br>")
    ) %>% 
  addLegend("bottomright", 
            pal = pal, 
            values = fire_stations_locations$GENERAL_USE %>% unique(),
    title = "Type",
    opacity = 1
  )

7.2.2 Mapdeck

The package Mapdeck (Cooley 2020) is an R package that is built on top of Mapbox (https://www.mapbox.com).4 It is based on WebGL, which means that your web browser does a lot of work for you. The nice thing is that because of this, it can do a bunch of things that leaflet struggles with, especially dealing with larger datasets. Mapbox is a full-featured application that many businesses that you may have heard of use: https://www.mapbox.com/showcase. To close out this discussion of interactive mapping, I want to briefly touch on mapdeck, as it is a newer, but very exciting, package.

To this point we have used ‘stamen maps’ as our underlying tile, but mapdeck uses ‘Mapbox’ - https://www.mapbox.com/ - and so you need to register and get a token for this. It’s free and you only need to do it once. When you have that token you add it to R. (We cover what is happening here in more detail in a later chapter.) Run this function:

usethis::edit_r_environ() 

When you run that function it will open a file. There you can add your Mapbox secret token.

MAPBOX_TOKEN = 'PUT_YOUR_MAPBOX_SECRET_HERE'

Save your ‘.Renviron’ file, and then restart R (Session -> Restart R).

Then you can call the map. We’ll just plot our firefighters data from earlier.

library(mapdeck)
#> 
#> Attaching package: 'mapdeck'
#> The following object is masked from 'package:tibble':
#> 
#>     add_column

mapdeck(style = mapdeck_style('dark')
        ) %>%
  add_scatterplot(
    data = fire_stations_locations, 
    lat = "lat", 
    lon = "lon", 
    layer_id = 'scatter_layer',
    radius = 10,
    radius_min_pixels = 5,
    radius_max_pixels = 100,
    tooltip = "ADDRESS"
  )
#> Registered S3 method overwritten by 'jsonify':
#>   method     from    
#>   print.json jsonlite

And this is pretty nice!

7.3 Shiny

Shiny (Chang et al. 2021) is a way of making interactive web applications (not just maps) using R. It’s fun, but fiddly. Here we’re going to step through one way to take advantage of Shiny, and that’s to quickly add some interactivity to our graphs. We’ll return to Shiny in later chapters also, so this is very much just a first pass.

We’re going to make a very quick interactive graph based on the ‘babynames’ dataset from the package babynames (Wickham 2019b). First, we’ll build a static version.

library(babynames)
library(tidyverse)

top_five_names_by_year <- 
  babynames %>% 
  group_by(year, sex) %>% 
  arrange(desc(n)) %>% 
  slice_head(n = 5)

top_five_names_by_year %>% 
  ggplot(aes(x = n, fill = sex)) +
  geom_histogram(position = "dodge") +
  theme_minimal() +
  scale_fill_brewer(palette = "Set1") +
  labs(x = "Babies with that name",
       y = "Occurances",
       fill = "Sex"
       )
#> `stat_bin()` using `bins = 30`. Pick better value with
#> `binwidth`.

What we can see is that possibly the most popular boys names tend to be more clustered, compared with the most-popular girls names, which may be more spread out. However, one thing that we might be interested in is how the effect of the ‘bins’ parameter shapes what we see. We might like to use interactivity to explore different values.

To get started, create a new Shiny app from the menu: ‘File -> New File -> Shiny Web App.’ Give it a name, such as ‘not_my_first_shiny’ and then leave all the other options as the default. A new file ‘app.R’ will open and you can click ‘Run app’ to see what it looks like.

Now replace the content in that file - ‘app.R’ with the content below, and then again click ‘Run app’

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Count of names for five most popular names each year."),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput(inputId = "number_of_bins",
                        label = "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$distPlot <- renderPlot({

        # Draw the histogram with the specified number of bins
        top_five_names_by_year %>% 
            ggplot(aes(x = n, fill = sex)) +
            geom_histogram(position = "dodge", bins = input$number_of_bins) +
            theme_minimal() +
            scale_fill_brewer(palette = "Set1") +
            labs(x = "Babies with that name",
                 y = "Occurances",
                 fill = "Sex"
                 )
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

You should find that you are served an interactive graph where you can change the number of bins and it should look something like Figure 7.10.

Example of Shiny app where the user controls the number of bins.

Figure 7.10: Example of Shiny app where the user controls the number of bins.

7.4 Exercises and tutorial

7.4.1 Exercises

7.4.2 Tutorial