I would like to visualise the spatial variation in election results on a map. This would answer the question: how did each electoral district vote? In particular, I would like to use non-contiguous cartograms and scale each district's area according to the count of votes cast for each party.
Hence, I produce one map per party, where the size of the district reflects the number of votes cast for that party in that district. For better visual recognition, the districts are coloured in the party's colour. To achieve all this, I use the function cartogram_ncont()
of the package cartogram
in R
.
The resulting scale is not consistent across maps. In other words, the maps are well-suited to campare where a single party did better or worse, but they are ill-suited to compare which party did better or worse. Put differently still, there currently is one "anchor district" on each map which isn't shrunk. However, I would like there to be only one "anchor district" across all maps, namely the district with the highest ballot count in the entire data set. Hence, the range of all vote counts for all parties should set the scale, not the range of vote counts for each individual party.
See as an example the results of the two parties with the most and the least votes overall in the Upper Austrian elections in 2015:
I realise that cartogram_ncont()
takes an optional argument k
which determines how many districts on the map are shrunk and how many are inflated. Yet, I don't understand whether or how I can use this argument to compute all my non-contiguous cartograms to the same underlying scale.
Any hints and ideas would be very welcome for I find myself at an impasse!
That is an interesting question. A sample code would have been helpful for my answer.
Playing around with the k values could be tricky. So I would like to suggest a simpler solution: just combine all variables into one value vector and use that for the cartogram.
I have modified the example from the cartogram_ncont() man page to give you a small demonstration. I did used the sp
-package, but you can easily adopt the code for sf
.
library(maptools)
library(cartogram)
library(rgdal)
library(rgeos)
data(wrld_simpl)
# Remove uninhabited regions
afr <- spTransform(wrld_simpl[wrld_simpl$REGION==2 & wrld_simpl$POP2005 > 0,],
CRS("+init=epsg:3395"))
# and keep only countries with larger area
afr <- afr[afr$AREA > 2568, ]
# Create fake data
set.seed(1234)
afr$V1 <- runif(nrow(afr), 0, 0.08) * 100
afr$V2 <- runif(nrow(afr), 0.3, 0.7) * 100
afr$V3 <- 100 - afr$V2 - afr$V1
# Keep the value for Egypt and Algeria constant
# this allows us to inspect the resulting map
afr$V1[afr$FIPS=="EG"] <- 40
afr$V2[afr$FIPS=="EG"] <- 40
afr$V3[afr$FIPS=="EG"] <- 40
afr$V1[afr$FIPS=="AG"] <- 13
afr$V2[afr$FIPS=="AG"] <- 13
afr$V3[afr$FIPS=="AG"] <- 13
# color vector for plotting
afr$col <- "gray"
afr$col[afr$FIPS=="EG"] <- "red"
afr$col[afr$FIPS=="AG"] <- "blue"
Now we need to create a SpatialDataFrame in long format. So we use rbind
to bind polygons and variable values together. The cartogram is based on this new dataset.
# There is probably a more efficient way to do this...
# create temporary data
tmp <- afr
tmp$W <- tmp$V1 # assign V1 to new weight variable
tmp$variable <- "V1" # add information about variable
# do the same for all other variables and rbind the spatial data
for(v in c("V2", "V3")) {
tt <- afr
tt$W <- tt[[v]]
tt$variable <- v
tmp <- rbind(tmp, tt)
}
# cartogram calculation
afr_nc <- cartogram_ncont(tmp, "W", k = 8)
Now we can plot the distorted map.
# plot side-by-side
par(mfrow = c(1,3))
for(v in c("V1", "V2", "V3")) {
plot(afr)
plot(afr_nc[afr_nc$variable==v, ], add=T, col = afr_nc$col)
}
# overplot new polygons
par(mfrow = c(1,1))
plot(afr)
for(v in c("V1", "V2", "V3")) {
plot(afr_nc[afr_nc$variable==v, ], add=T, col = "#00000022")
}