Goal: Make our first map!
Steps for lab:
vote margin
Problem set:
We’ll make this
Data
For this exercise we will be using a precinct-level dataset on 2020 presidential election results for the state of Michigan, mi_2020
.
Let’s get started:
PS01.zip
from Canvas (Files
\(\to\) Labs
\(\to\) PS01.zip
)PS01/Data/mi_2020.geojson
Dataset citation info:
Voting and Election Science Team, 2020, “2020 Precinct-Level Election Results”, Harvard Dataverse, V41. doi.org/10.7910/DVN/K7760H
Let’s open QGIS!
Get ready to rock!
Pro tip: Save your progress!
Go to Project
\(\to\) Save As...
QGIS can save the state of your workspace into a project file
.qgz
, a compressed file format.qgz
file contains information on your project’s layers, symbolization and styles, projections and print layoutsLayer
\(\to\) Add Layer
\(\to\) Add Vector Layer...
Click on Source
\(\to\) [...]
Navigate to folder with file, select mi_2020.geojson
, click Open
Click Add
You should see a map of Michigan’s electoral precincts
You can explore the vote tallies in each precinct by using the Identify Features
tool (looks like an “i” in a circle). Using this tool, click on any polygon.
mi_2020
), select Properties...
Select Symbology
type \(\to\) Graduated
Set graphical parameters (select variable):
Value
\(\to\) VOTESHARE
(percent Trump)
Set graphical parameters (select colors):
Color ramp
\(\to\) All Color Ramps
\(\to\) RdBu
Set graphical parameters (select colors):
Color ramp
\(\to\) Invert Color Ramps
(flip blue and red)
Set graphical parameters (make borders transparent):
Symbol
\(\to\) Simple fill
\(\to\) Stroke color
\(\to\) Transparent stroke
Set graphical parameters (select interval type):
Mode
\(\to\) Equal interval
Set graphical parameters (center intervals at 50%):
Symmetric classification
\(\to\) Around
\(=\) 50.0.
Click Classify
, then OK
.
The precincts should now be colored on a blue - red gradient.
Project
\(\to\) New Print Layout...
Name the print layout “Map 1
”
Add map:
Add item
\(\to\) Add map
Add map:
use mouse (click and drag) to place map on layout
Add legend:
Add item
\(\to\) Add legend
Add legend:
a legend should appear, with subtitle mi_2020
Add legend (change legend title):
Item Properties
\(\to\) Title
\(=\) “Trump’s 2020 vote share (%)”
Add scale bar:
Add item
\(\to\) Add scale bar
Add scale bar: a scale bar should appear, units are metric by default
Add scale bar (change units to miles):
Item Properties
\(\to\) Units
\(\to\) Scalebar units
\(=\) “Miles”
Save image to file:
Layout
\(\to\) Export as image...
Select folder, name the file mi_2020_voteshare.png
, click Save
Accept default settings, click Save
The image file should look something like this. Not pretty, but it’s the first pancake.
mi_2020
layer \(\to\) Open attribute table
On top of spreadsheet, click Open field calculator
button
Create New Field
\(\to\) Output field name
\(=\) VOTEMARGIN
Output field type
\(=\) “Decimal number (real)”Expression
: (G20PRERTRU-G20PREDBID)/(G20PRERTRU+G20PREDBID)*100
Click OK
Repeat the same steps as before:
Color precincts by vote margin, symmetric classification Around
\(=\) 0.00
Repeat the same steps as before:
Save map as image (mi_2020_votemargin.png
)
Can you make this map?
Your assignment (if using QGIS):
create the same kind of map for Massachusetts, using dataset ma_2020.geojson
VOTEMARGIN
(new variable)ma_2020_votemargin.png
Quick introduction to R
R is a programming language, which means you need to type commands, rather than pointing-and-clicking (unlike QGIS)
R scripts are plain text files with extension .R
File
menu \(\to\) New File
\(\to\) R Script
File
\(\to\) Open File...
File
\(\to\) Save
or File
\(\to\) Save As...
.R
)
The advantage of running scripts is that
To run a single line of code, type a command, place your cursor at the line and hit Ctrl+Enter
(Windows, Linux) or Command+Enter
(MacOS)
print("Hello world!")
## [1] "Hello world!"
Let’s try some arithmetic
2+2
## [1] 4
2-2
## [1] 0
2*2
## [1] 4
2/2
## [1] 1
Basic syntax
To create a vector, we use
=
or <-
c()
x = c(1,2,3,4,5)
x
## [1] 1 2 3 4 5
x <- c(1,2,3,4,5) # Equivalent to above
x
## [1] 1 2 3 4 5
y = x^2
y
## [1] 1 4 9 16 25
Basic syntax (cont’d)
Combine vectors into a dataset with the data.frame()
command
my_dataset = data.frame(x=x,y=y)
summary(my_dataset)
## x y
## Min. :1 Min. : 1
## 1st Qu.:2 1st Qu.: 4
## Median :3 Median : 9
## Mean :3 Mean :11
## 3rd Qu.:4 3rd Qu.:16
## Max. :5 Max. :25
Basic plotting
Create a basic scatterplot with the plot()
function
plot(x,y)
Getting help
To access the help file for any function, type ?
before the function’s name
?c
?data.frame
?summary
?plot
There is also an abundance of online help forums for R users:
Loading external packages
For this exercise, we will need two external packages:
sf
(“simple features”, to handle spatial vector data)PlotTools
(to customize plot legends)
We install packages with install.packages()
and load them with library()
install.packages("sf") # (first time only)
install.packages("PlotTools") # (first time only)
library(sf)
library(PlotTools)
Setting working directory
We should always set a working directory, using the setwd()
function.
The working directory is a folder on your hard drive, where you stored the data for this exercise, and where you’ll be saving maps.
You can find the path of a folder through Right-click
\(\to\) Get Info
(MacOS) or Right-click
\(\to\) Properties
(Windows).
On MacOS and Linux:
setwd("/home/username/Documents/API231")
On Windows:
setwd("C:/Documents/API231")
To find out your current working directory:
getwd()
Loading spatial vector data
Load the data through the read_sf()
function from the sf
package
mi_2020 = sf::read_sf(dsn = "Data/mi_2020.geojson")
Let’s take a look at the first 5 rows of mi_2020
, using print(mi_2020,n=5)
print(mi_2020,n=5)
## Simple feature collection with 4751 features and 16 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -90.41829 ymin: 41.69613 xmax: -82.41348 ymax: 48.26269
## Geodetic CRS: WGS 84
## # A tibble: 4,751 × 17
## PRECINCTID COUNTYFIPS cousubname elexpre G20PRERTRU G20PREDBID G20PRELJOR
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 WP-001-01040-0… 001 Alcona to… 001-AL… 564 248 3
## 2 WP-001-12460-0… 001 Caledonia… 001-CA… 508 245 4
## 3 WP-001-19320-0… 001 Curtis to… 001-CU… 486 238 2
## 4 WP-001-34820-0… 001 Greenbush… 001-GR… 560 302 9
## 5 WP-001-35740-0… 001 Gustin to… 001-GU… 317 112 9
## # ℹ 4,746 more rows
## # ℹ 10 more variables: G20PREGHAW <dbl>, G20PRENDEL <dbl>, G20PRETBLA <dbl>,
## # G20USSRJAM <dbl>, G20USSDPET <dbl>, G20USSGSQU <dbl>, G20USSNDER <dbl>,
## # G20USSTWIL <dbl>, VOTESHARE <dbl>, geometry <MULTIPOLYGON [°]>
This tells us:
Simple feature collection
)MULTIPOLYGON
) and spatial extent WGS 84
)… more on this laterA tibble: 4,751 x 17
)Visualizing spatial vector data
Let’s visualize the study region with plot(mi_2020["geometry"])
plot(mi_2020["geometry"])
Now let’s plot some attributes…
For a categorical variable (win/lose), visualization is simple…
cols = ifelse(mi_2020$G20PRERTRU>mi_2020$G20PREDBID,"red","blue")
plot(mi_2020["geometry"],col=cols)
plot(col=...)
command.plot(mi_2020["geometry"],col=cols)
Change the border’s thickness by changing lwd
parameter
plot(mi_2020["geometry"],col=cols,lwd=.1)
Make border
semi-transparent through alpha
parameter in rgb
plot(mi_2020["geometry"],col=cols,lwd=.1,
border=rgb(red = 0,green = 0,blue = 0,alpha = .5))
legend()
commandlegend(
x = "bottomleft",
title = "2020 Election Results",
fill = c("red","blue"),
legend =c("Trump win","Biden win")
)
Visualizing a continuous variable (e.g. vote share) is a little more tricky.
A relatively simple approach is to accept the default color palette like this:
plot(mi_2020["VOTESHARE"])
Or we can create a custom color palette (e.g. a red - blue gradient).
breaks = seq(0,100,by=1)
VOTESHARE
variableramp = findInterval(mi_2020$VOTESHARE,breaks)
cols = rgb(ramp/length(breaks),0,(length(breaks)-ramp)/length(breaks))
plot(col=...)
commandplot(mi_2020["VOTESHARE"],col=cols)
Add a title to the plot, with main
parameter
plot(mi_2020["VOTESHARE"],col=cols,
main="Michigan 2020 Presidential Election Results")
Add a gradient legend, with the SpectrumLegend
command from PlotTools
PlotTools::SpectrumLegend(
x = "bottomleft", title = "Donald Trump's\n vote share (%)",
palette = rgb(red=(100:0)/100,green=0,blue=(0:100)/100),
legend = seq(0,100,by=20), lwd = 10
)
Creating a new variable in R is quite simple
mi_2020$VOTEMARGIN = 100*(mi_2020$G20PRERTRU-mi_2020$G20PREDBID)/
(mi_2020$G20PRERTRU+mi_2020$G20PREDBID)
where
mi_2020
is the dataset, and $
calls variables within it=
sign is the new variable you want to create=
is an expression, specifying the calculation you want to make: \(\text{Vote Margin}= 100\cdot \frac{\text{Trump}-\text{Biden}}{\text{Trump}+\text{Biden}}\)To plot VOTEMARGIN
, we can follow the same procedure as before, just modifying the color breaks and legend to reflect the new range of this variable
breaks = seq(-100,100,by=1)
...
PlotTools::SpectrumLegend(..., legend = seq(-100,100,by=25), ...)
To export your map as an image file, use the png()
command
(or pdf()
, jpeg()
, tiff()
for other file formats)
png(filename="Output/mi_2020_votemargin_r.png",width=6,height=6,units="in",res=150)
par(mar=c(0,0,0,0))
plot(mi_2020["VOTEMARGIN"],col=cols,lwd=.1,border=rgb(0,0,0,.1),
main="Michigan 2020 Presidential Election Results")
PlotTools::SpectrumLegend(x = "bottomleft",title = "Donald Trump's\n vote margin (%)",
palette = rgb(red=(100:0)/100,green=0,blue=(0:100)/100),
legend = seq(-100,100,by=20), lwd = 20, bty = "n")
dev.off()
where
filename
argument specifies the location (e.g. Output/
) and name (e.g. mi_2020_votemargin_r.png
), in one stringwidth
and height
arguments specify the dimensions of the fileunits
specifies the units of measurement (in
\(=\) inches) and res
specifies the resolution (150 pixels per inch)dev.off()
closes the graphics devicepng()
and dev.off()
is printed to file)
The exported image should look like this: \(\to\)
mi_2020_votemargin_r.png
Your assignment (if using R):
create the same kind of map for Massachusetts, using dataset ma_2020.geojson
VOTEMARGIN
(new variable)ma_2020_votemargin_r.png
Can you make this map?