40 min read

Map of healthcare indicators in Catalonia

The data

Data of some helthcare indicators in Catalonia is publicly available from the Open Data Repository of the Observatori del Sistema de Salut de Catalunya. We have used the dataset corresponding to year 2017 (you can download it here). This is an Excel file providing indicators at three different levels (territorial units) in different tabs. We concentrate on tab Analisi AGA, containing indicators for the so called healthcare management areas (àrees de gestió assistencial, or AGA, in Catalan).

In the following, we assume some familiarity with R, particularly with basic functions of the dplyr package to work with dataframes, and chaining operations with the pipe (%>%) operator.

We first use package readxl to read the data in tab Analisi AGA of the Excel file, select the columns we need and rename them to English, and save the result in a dataframe (aga_data).

library(readxl)
library(dplyr)

read_excel("Taula_resum_territorial_2017.xlsx", 
           sheet = "Analisi AGA") %>%
  select(indicator_group = `Àmbit`,
         indicator = Indicador,
         aga_code = `codi AGA`,
         value = `valor AGA total`) -> aga_data 

Indicators are grouped in broad topics, recorded in indicator_group. We first look at unique values of this variable to see how many indicator groups there are, and then we list all indicators of a selected group:

# see all groups of indicators
unique(aga_data$indicator_group)
[1] "Dades Generals"                        
[2] "Atenció primària"                      
[3] "Salut Mental"                          
[4] "Prescripció farmacèutica i tractaments"
[5] "Àmbit Hospitalari"                     
[6] "Àmbit d'Urgències"                     
[7] "Sociosanitari"                         
[8] "Llistes d'espera"                      
# see all indicators in a selected group
aga_data  %>%
  filter(indicator_group == "Prescripció farmacèutica i tractaments") %>%
  pull(indicator) %>% 
  unique()
[1] "Població consumidora de fàrmacs pel TDAH de 6 a 17 anys (%)"
[2] "Població consumidora d'IBPs de 50 anys o més (%)"           
[3] "Població consumidora de psicofàrmacs de 65 anys o més (%)"  
[4] "Població consumidora d'antibiòtics (%)"                     
[5] "Taxa de població amb polimedicació (10 medicaments o més)"  
[6] "Taxa de població amb oxigenoteràpia domiciliària"           
[7] "Taxa de població amb CPAP"                                  

The map

Maps of the Catalan helthcare territorial structure can be downloaded here. This is a compressed (.zip) file, containing a folder named ABS_2018, which in turn contains several files for different territorial units and file formats. After extracting the contents of the .zip file, we use package sf to read the vector map for AGA’s. We do some renaming, and redefine aga_code so as to be numeric (instead of a factor); this is important, since we will later need this variable as key to merge with the data in aga_data. Finally, we simplify the map to reduce the weight of the resulting object, saved as aga_map.

library(sf)

st_read(dsn = "../R Catalonia maps/ABS_2018/AGA_2018.shp") %>%
  select(aga_name = NOMAGA,
         aga_code = CODIAGA,
         geometry) %>%
  mutate(aga_code = as.numeric(as.character(aga_code))) %>%
  arrange(aga_code) %>%
  st_simplify(dTolerance = 10) -> aga_map

As seen in the next outputs, aga_map is a dataframe containing standard columns (aga_name, aga_code), plus a list column (geometry) containg the geographic information of AGA’s needed to draw the map. Looking carefully at aga_name in the 6th row of the last output, a problem with encoding is detected, because letters with accent are not well written (<e0> instead of à). Since aga_name is a factor, we fix the problem by defining appropriate encoding for its levels, and verify.

class(aga_map)
[1] "sf"         "data.frame"
head(aga_map)
Simple feature collection with 6 features and 2 fields
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: 275940.3 ymin: 4562093 xmax: 419215.8 ymax: 4747976
epsg (SRID):    NA
proj4string:    +proj=utm +zone=31 +ellps=GRS80 +units=m +no_defs
                        aga_name aga_code                       geometry
1                     Alt Urgell        1 MULTIPOLYGON (((377643.2 46...
2                       Cerdanya        2 MULTIPOLYGON (((417782.2 46...
3                        Pallars        3 POLYGON ((348731.1 4674932,...
4                           Aran        4 POLYGON ((320139.5 4720858,...
5                         Lleida        5 POLYGON ((319752.2 4581218,...
6 Alt Camp i Conca de Barber<e0>        6 POLYGON ((354980.7 4566136,...
class(aga_map$aga_name)
[1] "factor"
Encoding(levels(aga_map$aga_name)) <- "latin-1"

head(aga_map)
Simple feature collection with 6 features and 2 fields
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: 275940.3 ymin: 4562093 xmax: 419215.8 ymax: 4747976
epsg (SRID):    NA
proj4string:    +proj=utm +zone=31 +ellps=GRS80 +units=m +no_defs
                     aga_name aga_code                       geometry
1                  Alt Urgell        1 MULTIPOLYGON (((377643.2 46...
2                    Cerdanya        2 MULTIPOLYGON (((417782.2 46...
3                     Pallars        3 POLYGON ((348731.1 4674932,...
4                        Aran        4 POLYGON ((320139.5 4720858,...
5                      Lleida        5 POLYGON ((319752.2 4581218,...
6 Alt Camp i Conca de Barberà        6 POLYGON ((354980.7 4566136,...

We can now plot the map of AGA’s using package tmap, with the following code:

library(tmap)

tm_shape(aga_map) + 
  tm_polygons("aga_name", legend.show = FALSE)

Showing an indicator in the map

Much more interesting is to show a specific indicator in the map of AGA’s. To achieve this, we first need to subset aga_data for the indicator of interest, and then merge the result to aga_map. Note that the merge is based on variable aga_code, which is common to both dataframes.

aga_data%>% 
  filter(indicator == "Població consumidora d'antibiòtics (%)") -> ab_data

# merge map and data
left_join(aga_map, ab_data) -> ab_map
Joining, by = "aga_code"

Finally, we plot the value of the selected indicator on the map. We take advantage of the fact that indicator is a constant in ab_map to define the title of the plot as unique(ab_map$indicator).

tm_shape(ab_map) +
  tm_polygons("value", title = unique(ab_map$indicator))

In the previous static map we see that this indicator takes values in the range 20 - 35, and some heterogeneity is apparent among AGA’s. However, it is hard to identify what are the specific AGA’s showing lower values, particularly, small areas in Barelona city. A better option that overcomes this limitation is to produce an interactive map. Happily, this only takes changing the visualization mode, and then executing exactly the same code that produced the static map above, as done in the following code. Note that, on hovering over any AGA, the name of the AGA is shown in a popup and, on clicking, the actual value of the indicator is also shown. In addition, by zooming in we can easily identify three smaller AGA’s in Barcelona city with low values of the indicator: Barcelona Esquerra, Barcelona Dreta, and Barcelona Litoral Mar.

tmap_mode("view")  

tm_shape(ab_map) +
  tm_polygons("value", title = unique(ab_map$indicator))

Last, note that the visualization mode will remain unchanged until you explicitly redefine it with tmap_mode("plot"), or ttm() (which toggles between the two modes).

More indicators

The following function allows to produce similar plots for other indicators, with little effort. The call of the function providing the desired indicator as argument, will produce the plot. Optionally, a second argument can be provided for the legend title.

# define the function
get_indicator_map <- function (ind, ind_title = ind) {
  
  aga_map %>%
    left_join(filter(aga_data, indicator == ind)) %>%
    tm_shape() +
    tm_polygons("value", 
                title = ind_title) 

}

# call the function
get_indicator_map("Taxa de població amb polimedicació (10 medicaments o més)",
                  "Població polimedicada")

For more on maps and how to plot them with R, see Geocomputation with R.