Excercise created by Ben Finkel, adapted with permission by Mika LaVaque-Manty and Ben Goehring

Introduction

Welcome to POLSCI 381 R World! You will learn to work with it by reading, using, and editing this R markdown file.

First, make sure you have oriented yourself to R and R studio with the introductory materials provided.

Quickly notice that this window/panel displays your .Rmd file. You also should see a ‘Console’ window/panel where the code is actually run. This is the environment in which commands get executed. You can always issue commands and code in it by typing at the > prompt.

As you read through this document, you’ll find areas where we have asked you to write in a comment or edit code. Read carefully, or you may miss something.

For instance, write your name next to “author” above at the document header.

Just like in Code Academy, unless text is in a code chunk, it does not run. Here is what an empty code chunk looks like

#This code chunk is empty, other than this comment

When pound signs (hashtags) are outside of code blogs, they indicate headers. # Largest Header. ## Smaller header. ### Even Smaller Header. You can see these when this file is knit (more on that later)

Optional: find the “explore document outline” option to see how headers automatically sort into an interactive document outline in Rmarkdown.

We have divided this into sections. Some sections you will need to do only once; some more frequently. So just read on!

If at any point you want help, try using R Studio’s help (in the menu bar in the bottom-right quadrant) or just Google it: for example, try “r change chr to factor” for the heck of it.

1. Get started

Install packages

First, you will install some packages that will allow you to complete this assignemnt. Note that this is only done once: once the packages are installed, they remain on your computer. So, if you return to this assignemnt tomorrow, the packages should still be there and you will not have to re-run this code.

There are well over 10,000 packages people have created that allow you to do different things with R – and in different ways. (R generally has many different ways of doing the same thing.)

1. Run the code below. To install, note that you must have internet access.

To run code inside of a code chunk you can either: a) Click the right pointing arrow on top of the code chunk to run it’s entire contents b) Highlight the code you want to run, then hit Cmd + Return (Mac) or Ctrl + Return (PC) to send that line over to the console to run. c) Copy/paste thee code you want to run from the code chunk into the Console (the most ‘low-tech’ method).

Now that the packages are installed, you can view them in the R Studio’s “Packages” menu, in the right-bottom quadrant by default.

You can ignore any warnings. Throughout the activity you may get more warnings, but they don’t always mean something bad or that the code hasn’t worked.

Again, you only have to complete this step once as the packages will remain on your computer.

Loading packages

This step you must complete each time you start a new work session, because it tells R to load or use each of those packages.

Each time you begin work sessions (and your scripts), you start by loading the packages you’ll likely need.

1b. Run the following code

#Load necessary packages. Again, highlight, and select "Run Lines" in the Code menu above or hit the right arrow. 
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Set the working directory

In order to get data into R from a csv or other type of file, you have to tell R where to look for it. Your computer’s file system can be thought of as a sort of root system with different folders branching out from a single folder (the trunk, to keep the analogy alive). If you are not explicit about the folder in which your data lives, R will not be able to read it in and will return an error. There are multiple ways to tell R where to look for your data, which is often referred to as “setting your working directory.” (Directory and folder are synonymous terms.) For this assignment we are going to use R Projects.

Look at the top right-hand corner of Rstudio and click on “Project: (None)” and “New Project.” If prompted, save any changes to the Rmarkdown file. A screen should pop up asking you if you would like to create a new directory or use an existing directory. Select existing directory and then, in the next screen, select whichever folder your Rmakrdown file is currently in. Then click “Create Project” in the bottom right-hand corner.

Now, when you look at the top right-hand corner of RStudio it should provide the name of the folder that your Rmarkdown file is in (rather than “Project: (None)). If so, great! Now when you tell R to read in a data file, R will automatically begin searching for the file in your current folder.

(As an aside, some of you may have used the setwd() function to set your working directory in other classes. That is fine. I much prefer R Projects to setwd() for reproducibility reasons, but either will get the job done. If you want a hotter take on this: https://www.tidyverse.org/blog/2017/12/workflow-vs-script/.)

Driving R: Basic Concepts

Note: this section includes extra review of R language basics to familiarize you with working inside of Rstudio.

The console

At the command prompt, type “3 + 2” (without the quotes) and hit return.

2a. What happened? > [1] 5

(Note: by keeping your text to the right of the >, your answer will be formatted as a quote in the final knitted document.)

Now, run the code below

# Variables:
x <- 2
y <- 5
z <- x * y
print(z)
## [1] 10

2b. What did print(z) produce and why? > [1] 10. Because ‘x <- 2” assigned 2 to the variable x, and ’y <-5’ assigned 5 to the variable y. Then, ‘z <- x * y’ assigned the multiplication of x and y to the variable z. So, by printing z, it must have ran ‘x * y’ which is the same as 2 * 5– so the answer is 10.

Above, the values of X and Y and Z were saved into the enviornment. You can see the list of saved values by going to the ‘Environment panel’ in the top-right corner of RStudio. Try to find that now. You can always see all of your stored “objects”(variables, data frames, etc.) in the console by running:

ls()
## [1] "x" "y" "z"

2c. How many objects were listed? > Three objects were listed– x, y, and z.

OK, let’s continue with variables. Instead of single values, we will make them “vectors,” with multiple values.

Run the code below one line at a time and consider what action each line performs.

x <- c(1,2,3,4) #  overwrites our earlier variable x
x 
## [1] 1 2 3 4
sum(x) # This adds up the values of x
## [1] 10
min(x) 
## [1] 1
max(x) 
## [1] 4
mean(x) 
## [1] 2.5

2d. If “sd” is the command for standard deviation, write one line of code below that returns the standard deviation of vector x as defined above. (The standard deviation is a measure of variation. For instance, the vector (10, 1, -5) has a higher standard deviation than the vector (1, 2, 3).

sd(x)
## [1] 1.290994

OK, let’s continue with variables

y <- c(5,6,7)
z <- c(x,y)
print(z)
## [1] 1 2 3 4 5 6 7

Using the c() combine function, it added the new vector y the end of vector x and called it z.

Data frames

Data usually comes in more complex arrangements than single values or vectors. Data frames are objects where we store that complexity.

Data comes in the form of “observations,” which are represented as rows in data frames, and “variables,” which are in columns.

The following process creates a data frame for us. Our variables are “students,” “scores,” and “grades”, our observations are individual students, the score, and the grade each got.

#We create the variables first as vectors:
students <- c("Harry", "Hermione", "Neville", "Ron", "Draco")
scores <- c(3.3, 4, 1.7, 2.7, 0)
grades <- c("B+", "A", "C-", "B-", "F")

# ...and then combine them into the data frame itself:
dark.arts.exam <- data.frame(students, scores, grades)

In R Studio, you will notice that in the Environment window on the top-right, you now have an object called dark.arts.exam. Click on it and you will be able to take a closer look at what you created. You can get back to this window simply by clicking the “381_r_assignment.Rmd” tab after looking at your data frame.

This allowed you to open and view your data frame manually. We have much better tools to do this using R, especially when working with large datasets that cant’ fit in a single frame.

Here are some basic, and often more useful ways of looking at your data. Issue the following commands one at a time:

summary(dark.arts.exam) # summarizes the data
##    students             scores        grades         
##  Length:5           Min.   :0.00   Length:5          
##  Class :character   1st Qu.:1.70   Class :character  
##  Mode  :character   Median :2.70   Mode  :character  
##                     Mean   :2.34                     
##                     3rd Qu.:3.30                     
##                     Max.   :4.00
names(dark.arts.exam) # lists your variable names
## [1] "students" "scores"   "grades"
str(dark.arts.exam) # lists your variable names and types
## 'data.frame':    5 obs. of  3 variables:
##  $ students: chr  "Harry" "Hermione" "Neville" "Ron" ...
##  $ scores  : num  3.3 4 1.7 2.7 0
##  $ grades  : chr  "B+" "A" "C-" "B-" ...

2e. What is the mean score of all students in the dark.arts.exam dataframe? > The mean of all students in the dark.arts.exam dataframe is 2.34.

Manipulating data frames

We’ll create a second data frame and then merge it with the first.

first.name <- c("Harry", "Hermione", "Neville", "Ron", "Draco")
last.name <- c("Potter", "Granger", "Longbottom", "Weasley", "Malfoy")
house <- c("Gryffindor", "Gryffindor", "Gryffindor", "Gryffindor", "Slytherin")

houses <- data.frame(first.name, last.name, house)

exam.with.houses <- merge(dark.arts.exam, houses, by.x = "students", by.y = "first.name")

2f. In your own words, what did the merge() function do above? > The merge() combined the new house data frame with the previous dark.arts.exam data frame to create one data frame that includes all the data from both data frames.

Now run this.

only.the.good.guys <- filter(exam.with.houses, house == "Gryffindor") 

2g. In one sentence, what did the filter() function do above? You may want to examine the only.the.good.guys. object to confirm your guess. > It removed anyone who was assigned to the Gryffindor house.

(Important technical note: “filter” in the above example is part of the “tidyr” package. If you haven’t installed and loaded tidyr, the command will produce a mistaken result because “filter” is then a different function.)

challenge2 <- filter(exam.with.houses, house != "Gryffindor")

2h. In one sentence, what did the filter() function do above? > It filtered out everyone who was not assigned to the Gryffindor house.

2i. Next, write your own code that selects all students who scored above average (you’ll probably want to cut and paste the above code and then modify it)

#your code here
challenge3 <- filter(exam.with.houses, scores > "2.34")

R Graphics

R is very powerful for producing excellent graphical plots. Base R (the default) has it’s own graphics, but we will use the package ggplot2 as an alternative graphics engine.

( You can find its documentation and details at http://docs.ggplot2.org/current/index.html)

In the following, we’ll create a basic boxplot with the exam data. Notice how adding a plus-sign at the end of a line lets you add further operations.

ggplot(exam.with.houses, aes(x = house, y = scores)) + 
  geom_boxplot(aes(fill=house)) +
  ylab("Score") +
  ggtitle("Proof of Slytherin lameness")

2j. Reproduce the code above with two changes: with the title as “Exam results,” and with a blank x-axis label.

#new plot here
ggplot(exam.with.houses, aes(x = , y = scores)) + 
  geom_boxplot(aes(fill=house)) +
  ylab("Score") +
  ggtitle("Exam results")

Great job! You’ve learned the basics, and you can start playing with real data.

3. Working with real data: 2016 county-level election returns and income

For the rest of the exercise, we’re going to use data on Trump’s votes in every US county in the 2016 election paired with county-level data on median income. The vote data is from the MIT Election Lab and the income data is from the Census ACS survey.

The data is stored in the trump_income.csv file on Canvas. Download it and place it in whichever folder you set as your working directory when you created an R Project.

Load data

#Note: This will only work if trump_income.csv is located in your working directory 

d <- read_csv("trump_income.csv") # naming your dataframe d or df is a common shorthand
## Rows: 3114 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): state, county, name
## dbl (5): fips, median_income, trump_votes, total_votes, vote_share
## 
## ℹ 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.

Explore and clean

Let’s take a look at our new data frame, d

nrow(d) # nrow and ncol return the number of rows or columns present in x. NCOL and NROW do the same treating a vector as 1-column matrix, even a 0-length vector, compatibly with as.matrix() or cbind(), see the example.
## [1] 3114
ncol(d)
## [1] 8
dim(d) #Retrieve or set the dimension of an object
## [1] 3114    8

Note: you can ask R for the definition of a function by typing ?function() e.g. ?nrow. The description will appear in the Help window.

3a. Briefly summarize what the above three functions each did. > The nrow() function returns with the number of rows in the trump_income.csv. The ncol() function returns with the number of rows in the trump_income.csv. The dim(d) function returns with the dimension aka number of rows and columns in the trum_income.csv.

names(d) 
## [1] "state"         "county"        "name"          "fips"         
## [5] "median_income" "trump_votes"   "total_votes"   "vote_share"

Our data has 7 variables. Read carefully!

  • state: The state in which the county is located.
  • county: The name of the county.
  • name: The combined county/state name (mostly included because AK is a little odd).
  • fips: The unique FIPS census identifier for the county.
  • median_income: The median income of the county in 2016, in dollars.
  • trump_votes: The number of votes trump won in 2016.
  • total_votes: The total number of votes cast in 2016.
  • vote_share: Trump’s share of votes in 2016.

Let’s look at the first row of data

d[1,] #this shows the first row of data for all columns
## # A tibble: 1 × 8
##   state   county name      fips median_income trump_votes total_votes vote_share
##   <chr>   <chr>  <chr>    <dbl>         <dbl>       <dbl>       <dbl>      <dbl>
## 1 Alabama Wilcox Wilcox …  1131         24442        1737        6118      0.284

3b. What county is in this row? And how many votes did Trump win in this county? > Trump won 1737 votes in Wilcox County, Alabama.

Here are some usual functions to take a quick look at your data

head(d) #look at first rows
## # A tibble: 6 × 8
##   state   county    name   fips median_income trump_votes total_votes vote_share
##   <chr>   <chr>     <chr> <dbl>         <dbl>       <dbl>       <dbl>      <dbl>
## 1 Alabama Wilcox    Wilc…  1131         24442        1737        6118      0.284
## 2 Alabama Winston   Wins…  1133         33896        9228       10313      0.895
## 3 Alaska  District… Aleu…  2013         65926        4028        6533      0.617
## 4 Alaska  District… Aleu…  2016         86964        3203        7436      0.431
## 5 Alaska  District… Anch…  2020         80862        2187        7140      0.306
## 6 Arizona Apache    Apac…  4001         32460        8240       27661      0.298
str(d) #again, see overall str of each variable
## spc_tbl_ [3,114 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ state        : chr [1:3114] "Alabama" "Alabama" "Alaska" "Alaska" ...
##  $ county       : chr [1:3114] "Wilcox" "Winston" "District 13" "District 16" ...
##  $ name         : chr [1:3114] "Wilcox County, Alabama" "Winston County, Alabama" "Aleutians East Borough, Alaska" "Aleutians West Census Area, Alaska" ...
##  $ fips         : num [1:3114] 1131 1133 2013 2016 2020 ...
##  $ median_income: num [1:3114] 24442 33896 65926 86964 80862 ...
##  $ trump_votes  : num [1:3114] 1737 9228 4028 3203 2187 ...
##  $ total_votes  : num [1:3114] 6118 10313 6533 7436 7140 ...
##  $ vote_share   : num [1:3114] 0.284 0.895 0.617 0.431 0.306 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   state = col_character(),
##   ..   county = col_character(),
##   ..   name = col_character(),
##   ..   fips = col_double(),
##   ..   median_income = col_double(),
##   ..   trump_votes = col_double(),
##   ..   total_votes = col_double(),
##   ..   vote_share = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

3c. According to the str(), what class of data is variable ‘county’? > The ‘county’ variable is character data.

Often when starting with data, a key early step is to make sure R knows what kind of data each of your variables are.

unique(d$state)
##  [1] "Alabama"              "Alaska"               "Arizona"             
##  [4] "Arkansas"             "Illinois"             "California"          
##  [7] "Colorado"             "Connecticut"          "Delaware"            
## [10] "District of Columbia" "Florida"              "Georgia"             
## [13] "Hawaii"               "Idaho"                "Kentucky"            
## [16] "Louisiana"            "Indiana"              "Iowa"                
## [19] "Kansas"               "Missouri"             "Montana"             
## [22] "Maine"                "Maryland"             "Massachusetts"       
## [25] "Michigan"             "Minnesota"            "Mississippi"         
## [28] "North Carolina"       "North Dakota"         "Ohio"                
## [31] "Nebraska"             "Nevada"               "New Hampshire"       
## [34] "New Jersey"           "New Mexico"           "New York"            
## [37] "Tennessee"            "Texas"                "Oklahoma"            
## [40] "Oregon"               "Pennsylvania"         "Rhode Island"        
## [43] "South Carolina"       "South Dakota"         "Virginia"            
## [46] "Washington"           "Utah"                 "Vermont"             
## [49] "West Virginia"        "Wisconsin"            "Wyoming"

3d. In one or two sentences, what does the code above do? > The uique() function reads the trump_income.csv and displays the name of every state while removing duplicated variables.

min(d$median_income)
## [1] 18972
max(d$median_income)
## [1] 125672

3e. According to the output of the code above, what is the highest and lowest median income in the data set? > The lowest median income is 18972 and the highest median income is 125675

Summarizing data

Let’s see what R can do. Right now our code is at the county-level. Let’s try and summarize it by state, so we we can figure out Trump’s share of state-level votes.

summary_2016 <- d %>% # This is a nifty operation called the "pipe" that lets you pass down arguments to subsequent functions i.e., the group_by is now performed on object d
  group_by(state) %>% # summarize the data by state
  mutate(state_trump_votes = sum(trump_votes)) %>% # Create a new variable called state_trump_votes that equals the sum of all trump votes in a given state
  mutate(state_total_votes = sum(total_votes)) %>%# Create a new variable called state_total_votes that equals the sum of all votes in a given state
  ungroup() %>% # Don't worry about this. It is just good practice to always ungroup a dataframe when you have done any grouping.
  mutate(state_vote_share = round(state_trump_votes / state_total_votes, 2))

# Since we are interested in state-level data, we do not need 3114 observations, only 51 observations (DC is in here too)
# Let's drop the county-level information and remove any duplicates. 
summary_2016 <- summary_2016 %>% 
  select(-name, -county, -fips, -median_income, -trump_votes, -total_votes, -vote_share) %>% 
  distinct()

# Finally, let's arrange the rows with the most Trump-supporting states at the top.
summary_2016 <- summary_2016 %>% 
  arrange(-state_vote_share)

#now, let's just view the top 10 trump-supporting states  
head(summary_2016, 10) 
## # A tibble: 10 × 4
##    state         state_trump_votes state_total_votes state_vote_share
##    <chr>                     <dbl>             <dbl>            <dbl>
##  1 West Virginia            489371            713051             0.69
##  2 Wyoming                  174419            255849             0.68
##  3 Oklahoma                 949136           1452992             0.65
##  4 Kentucky                1202971           1924149             0.63
##  5 North Dakota             216794            344360             0.63
##  6 Alabama                 1318250           2123367             0.62
##  7 South Dakota             227480            367188             0.62
##  8 Arkansas                 684872           1129896             0.61
##  9 Tennessee               1522925           2508027             0.61
## 10 Idaho                    409055            690433             0.59

3f. What state and vote share are listed in the fourth row? How many people voted in total in the state? > Kentucky, .63. The total number of votes in the state are 1924149.

3g. In the code chunk below, edit the previous code so that it also tells includes state-level median income data (hint: you will be using mutate(new_var = median(median_income) to create the new variable.)

summary_2016 <- d %>% # This is a nifty operation called the "pipe" that lets you pass down arguments to subsequent functions i.e., the group_by is now performed on object d
  group_by(state) %>% # summarize the data by state
  mutate(state_trump_votes = sum(trump_votes)) %>% # Create a new variable called state_trump_votes that equals the sum of all trump votes in a given state
  mutate(state_total_votes = sum(total_votes)) %>%# Create a new variable called state_total_votes that equals the sum of all votes in a given state
  mutate(median = median(median_income)) %>%
  ungroup() %>% # Don't worry about this. It is just good practice to always ungroup a dataframe when you have done any grouping.
  mutate(state_vote_share = round(state_trump_votes / state_total_votes, 2))

# Since we are interested in state-level data, we do not need 3114 observations, only 51 observations (DC is in here too)
# Let's drop the county-level information and remove any duplicates. 
summary_2016 <- summary_2016 %>% 
  select(-name, -county, -fips, -median_income, -trump_votes, -total_votes, -vote_share) %>% 
  distinct()

# Finally, let's arrange the rows with the most Trump-supporting states at the top.
summary_2016 <- summary_2016 %>% 
  arrange(-median)

#now, let's just view the top 10 trump-supporting states  
head(summary_2016, 10)
## # A tibble: 10 × 5
##    state             state_trump_votes state_total_votes median state_vote_share
##    <chr>                         <dbl>             <dbl>  <dbl>            <dbl>
##  1 Alaska                         9418             21109 80862              0.45
##  2 New Jersey                  1601933           3874046 72999              0.41
##  3 District of Colu…             12723            311268 72935              0.04
##  4 Rhode Island                 180490            463416 71347              0.39
##  5 Connecticut                  673215           1644920 70773              0.41
##  6 Hawaii                       128847            428937 68500.             0.3 
##  7 Maryland                     943169           2781446 68464.             0.34
##  8 Massachusetts               1090893           3274555 64458              0.33
##  9 New Hampshire                345790            744296 59850.             0.46
## 10 North Dakota                 216794            344360 55900              0.63

3h. Report the five states with the highest median income in 2016

Alaska, New Jersey, District of Columbia, Rhode Island, and Connecticut.

Visualizing

State counts

Graphics are sometimes a better way of seeing basic data quickly. Let’s do a bar graph of Trump’s share of votes in a state.

We are going to use the summary_2016 dataframe we created above and the powerful ggplot function to create a bar plot.

Now, we use a powerful function called ggplot to create a barplot of these values

ggplot(data = summary_2016, #identify the data being used
       aes(x = state, y = state_vote_share)) + #define the x and y variables 
  geom_bar(stat = "identity") #tells ggplot to use values as listed rather than aggregating

Pretty good, but the data lost its order. It would be easier to quickly get information from this plot if the data were ordered.

ggplot(summary_2016, 
       aes(x = reorder(state, -state_vote_share), #reorder
           y = state_vote_share)) +
  geom_bar(stat = "identity") 

Better. But it’s hard to read and the labels are messy. So, we’re going to add more functions to label our plot.

plot <- ggplot(summary_2016, 
       aes(x = reorder(state, -state_vote_share), #reorder
           y = state_vote_share)) +
  geom_bar(stat = "identity") +
  xlab("") +
  ylab("Share of state votes") +
  ggtitle("Trump's share of votes in 2016 election")  +
  scale_y_continuous() +
  theme(axis.text.x = element_text(angle = 60, hjust = 1, size = 7))   # rotates county names so more readable

plot #What this did was assign the whole plotting code to a single object, and then just called that object. 

Much better, but of course further improvements can be made.

One useful feature of ggplot is setting default graphics through the use of different themes. Here are a few.

plot +
  theme_bw() #by assigning the prior plot to object 'plot'. I can make adjustments to it here in much more concise code (although the new theme overwrites the changes I made previously to the label formatting)

plot +
  theme_dark() 

plot +
  theme_void() 

#You can always download more themes! There are tons out there. 
# (I am a dork and have my own custom theme #gradschool. You can take a look at it by running the code below.)


# install.packages('devtools')
# devtools::install_github('benfromfargo/BeRn')
# BeRn::set_my_theme()
# 
# plot
# rm(my_theme)

3i. Make at least one edit to a plot above to create your own plot. You can try a new theme (not one listed above), change the labels, or mess with any part of it. It doesn’t have to be more clear than the plots above. The goal here is just to play around with the code.

plot <- ggplot(summary_2016, 
       aes(x = reorder(state, state_vote_share), #reorder from least to greatest
           y = state_vote_share)) +
  geom_bar(stat = "identity") +
  xlab("") +
  ylab("Cents per Dollar") + #Change title of y-axis
  ggtitle("Amount of money donated to political campaigns")  + #Change title of bar plot
  scale_y_continuous() +
  theme(axis.text.x = element_text(angle = 60, hjust = 1, size = 7))

plot 

4. Make your own plot

It is OK if this doesn’t work 100%. Your plots do not need to be pretty, you just have to produce something similar. The goal here is to challenge you some at the end.

4a. Make a dot plot of the relationship between income and trump’s vote share in the state of Michigan. Give it nice labels and a title.

Save plots as files

4b. Last challenge (bonus). Can you figure out how to save your two new plots as files? This will take some googling.

#remember to comment on your code

4c Bonus bonus: Create a dot plot of the relationship between median income and Trump’s vote share with two facets: one contains counties in states in which Trump won more votes than Clinton and the other contains counties in states in which Trump won fewer votes than Clinton.

#remember to comment on your code

4d Bonus bonus bonus: Replicate the dot plot of relationship between median income and Trump’s vote share across all counties in the US (this is just the simple plot without any facets). This time, though, figure out how to highlight Washtenaw county so that it is a different color than every other county in the US.

5.Submit / knit!

See assignment instructions for more info on knitting.