Skip to main content

Section 3.3 Mapping rates rather than counts

Subsection 3.3.1 Generating the rates

We have now seen the importance of mapping rates rather than counts of things, and that is for the simple reason that population is not equally distributed in space. That means that if we do not account for how many people are somewhere, we end up mapping population size rather than our topic of interest.
The manchester object we are working with has a column named respop that includes the residential population in each of the LSOA areas. These areas for the whole of the UK have an average population of around 1500 people. We can see a similar picture for Manchester city, with an average closer to 1700 here.
hist(manchester$respop, main = "Distribution of residential population across LSOAs")
A bar chart, titled ’Distribution of residential population across LSOAs’. The vertical axis is labeled ’Frequency’, and ranges from zero to one hundred and fifty. The horizontal axis does not have a nice label, but clearly represents residential population, starting at one thousand, and ending at four thousand and five hundred, with each bar including a range of five hundred. The second bar at fifteen hundred to two thousand dominates, with a frequency over one hundred and fifty. The first and third bars follow, sitting around fifty. The last few bars are very short.
Figure 3.3.1. Histogram of respop variable
We also have a variable wkdpop that represents the workday population. This variable re-distributes the usually resident population to their places of work, while those not at work are recorded at their usual residence. The picture it offers is much more diverse than the previous variable and in some ways is closer to the notion of an "ambient population" (with some caveats: for example, it does not capture tourists, people visiting a place to shop or to entertain themselves in the night time economy, etc.).
hist(manchester$wkdpop, main = "Distribution of workday population across LSOAs")
A bar chart, titled ’Distribution of workday population across LSOAs’. The vertical axis is labeled ’Frequency’, and ranges from zero to two hundred and fifty. The horizontal axis does not have a nice label, but clearly represents workday population, starting at zero, and ending at forty thousand, with each bar including a range of five thousand. The first bar at zero to five thousand dominates, with a frequency over two hundred and fifty. The rest of the bars are very short, and are hard to see.
Figure 3.3.2. Histogram of wkdpop variable
The distribution of the workday population is much more skewed than the residential population, with most LSOAs having only 5,000 or fewer workday population, but the busiest LSOAs attracting over 40,000 people during the work day. This gives an idea of the relevance of the "denominator dilemma" we mentioned earlier. In this section we will create rates of crime using both variables in the denominator to observe how the emerging picture varies.
First we need to create new variables. For this we can use the mutate() function from the dplyr package. This is a very helpful function to create new variables in a dataframe based on transformations or mathematical operations performed on other variables within the dataframe. In this function, the first argument is the name of the data frame, and then we can pass as arguments all new variables we want to create as well as the instructions as to how we are creating those variables.
First, we want to create a rate using the usual residents, since crime rates are often expressed by 100,000 inhabitants we will multiply the division of the number of crimes by the number of usual residents by 100,000. We will then create another variable, crimr2, using the workday population as the denominator. We will store this new variables in our existing manchester dataset. You can see that below we then specify the name of a new variable crimr1 and then tell the function we want that variable to equal (for each case) the ratio of the values in the variable count (number of crimes) by the variable respop (number of people residing in the area). We then multiply the result of this division by 100,000 to obtain a rate expressed in those terms. We can do likewise for the alternative measure of crime.
manchester <- mutate(manchester,
                     # crime per residential population
                     crimr1 = (count/respop)*100000,
                     # crime per workday population
                     crimr2 = (count/wkdpop)*100000)
And now we have two new variables: one for crime rate with residential population as a denominator, and another with workplace population as a denominator.

Subsection 3.3.2 Creating a choropleth map with tmap

The structure of the grammar for producing a choropleth map is similar to what we use for proportional symbols. First, we identify the object with tm_shape() and then we use a geometry to be represented. We will be using the tm_polygons() passing as an argument the name of the variable with our crime rate.
tm_shape(manchester) +
  tm_polygons("crimr1")
An outline of the borders of the LSOAs in Manchester, each of which is shaded according to a gradient from yellow to red. A legend in the bottom right labeled ’crimr1’ explains the six possible colours, each of which covers a range of ten thousand. Only the lightest colour is used, except for a few LSOAs near the centre of the city.
Figure 3.3.3. Crime rate per residential population
We have used tm_polygons() but we can also add the elements of a polygon map using different functions that break down what we represent here. In the map above, you see the polygons have a dual representation, the borders are represented by lines and the colour is mapped to the intensity of the quantitative variable we are displaying. With darker colours representing more of the variable, the areas with more crimes.
Instead of using tm_polygon(), we can use the related functions tm_fill(), for the colour inside the polygons, and tm_borders(), for the aesthetics representing the border of the polygons. Say, we find the borders distracting and we want to set them to be transparent. In that case we could just use tm_fill().
tm_shape(manchester) +
  tm_fill("crimr1")
The previous shaded map now appears without the borders.
Figure 3.3.4. Crime rate per residential population mapped with tm_fill() function
As you can see here, the look is a bit cleaner. However, we don’t need to get rid of the borders completely. Perhaps we want to make them a bit more translucent. We could do that by adding the border element but making the drawing of the borders less pronounced.
tm_shape(manchester) +
  tm_fill("crimr1") +
  tm_borders(alpha = 0.1)
A visual compromise between the previous two figures, the borders have returned, but are much thinner and attract less attention.
Figure 3.3.5. Crime rate per residential population mapped with tm_fill() and borders
The alpha parameter that we are inserting within tm_borders() controls the transparency of the borders, we can go from 0 (totally transparent) to 1 (not transparent). You can play around with this value and see the results.
Notice as well that the legend in this map is not very informative and could be improved in terms of aesthetics. We can add a title within the tm_fill to clarify what "crimr1" means, and we can use the tm_layout() function to control the appearance of the legend. This latter function, tm_layout(), allows you to think about many of the more general cosmetics of the map.
tm_shape(manchester) +
  tm_fill("crimr1", title = "Crime per 100,000 residents") +
  tm_borders(alpha = 0.1) +
  tm_layout(main.title = "Crime in Manchester City, Nov/2017",
            main.title.size = 0.7 ,
            legend.outside = TRUE,  # Place legend outside of map
            legend.title.size = 0.8)
The previous figure, now with a title ’Crime in Manchester City, Nov 2017’. The legend has moved further to the right of the shaded map, enlargened, and its label has been updated to ’Crime per hundred thousand residents’.
Figure 3.3.6. Crime rate per residential population mapped with aesthetics considered
We can also change the style of the maps we produce, for example by making them more friendly to colour-blind people. We can use the tmap_style() function to do so. Once you set the style, the subsequent maps will stick to this style. If you want to reverse to a different style when using tmap, you need to do so explicitly.
current_style <- tmap_style("col_blind")
See how the map changes.
tm_shape(manchester) +
  tm_fill("crimr1", title = "Crime per 100,000 residents") +
  tm_borders(alpha = 0.1) +
  tm_layout(main.title = "Crime in Manchester City, Nov/2017",
            main.title.size = 0.7 ,
            legend.outside = TRUE,  # Takes the legend outside the main map
            legend.title.size = 0.8,
            )
The previous figure, with the colour gradient now being a cool blue, in both the shading and the legend.
Figure 3.3.7. Crime rate per residential population with col_blind style
We will discuss more about map aesthetics in Chapter 5. For now, let’s talk about another important decision when it comes to thematic maps, the choice of classification system.