library(tidyverse)
library(kableExtra)
library(tigris)
library(sf)
This notebook collects a number of measures of district competitiveness / ideological leanings and maps them to the candidates dataframe from previous notebooks. The output of this notebook will serve as the final dataset for this project’s ultimate statistical analysis.
The first measure comes from a 2022 study gathering more than a decade of survey data to model ideology at the district level (https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/BQKU4M). As with many of the partisanship scores, the scale is oriented such that “lower values are associated with politically left preferences and higher values with politically right preferences.” So, I will normalize the scores between -1 and 1, then take the absolute value. The results scores will then be oriented such that 0 indicates closeness to the political center and 1 indicates closeness to ideological wings, matching the orientation of the partisanship scores from notebook 4.
load('data/aip_district_ideology_v2022a.RData')
district_ideology <- table
rm(table)
district_ideology %>% head()
## # A tibble: 6 × 14
## cd_fips state mrp_ideology_se mrp_ideology irt_ideology_unweighted
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 1000 Delaware 0.0696 -0.0459 -0.0182
## 2 1000 Delaware 0.0429 -0.0213 -0.0651
## 3 1000 Delaware 0.0409 -0.0710 -0.0798
## 4 1000 Delaware 0.0409 -0.0710 -0.0735
## 5 1000 Delaware 0.0409 -0.0710 -0.0763
## 6 101 Alabama 0.0687 0.201 0.185
## # ℹ 9 more variables: self_ideology <dbl>, self_ideology_se <dbl>,
## # irt_ideology_unweighted_sd <dbl>, irt_ideology_unweighted_se <dbl>,
## # sample_size <dbl>, demshare_pres <dbl>, presidential_year <dbl>,
## # congress <dbl>, survey_period <chr>
district_ideology %>%
filter(congress == 118) %>%
ggplot() +
geom_freqpoly(aes(mrp_ideology)) +
geom_freqpoly(aes(irt_ideology_unweighted), color = 'red')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
min_score <- min(district_ideology$mrp_ideology, na.rm = TRUE)
max_score <- max(district_ideology$mrp_ideology, na.rm = TRUE)
district_ideology <- district_ideology %>%
mutate(mrp_norm = 2 * (mrp_ideology - min_score) / (max_score - min_score) - 1,
mrp_norm_abs = abs(mrp_norm))
min_score <- min(district_ideology$irt_ideology_unweighted, na.rm = TRUE)
max_score <- max(district_ideology$irt_ideology_unweighted, na.rm = TRUE)
district_ideology <- district_ideology %>%
mutate(itr_norm = 2 * (irt_ideology_unweighted - min_score) / (max_score - min_score) - 1,
itr_norm_abs = abs(itr_norm))
district_ideology %>%
filter(congress == 118) %>%
ggplot() +
geom_freqpoly(aes(mrp_norm)) +
geom_freqpoly(aes(itr_norm), color = 'red')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
district_ideology %>%
filter(congress == 118) %>%
ggplot() +
geom_freqpoly(aes(mrp_norm_abs)) +
geom_freqpoly(aes(itr_norm_abs), color = 'red')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
The next measure is the Cook PVI (https://www.cookpolitical.com/cook-pvi/2022-partisan-voting-index/district-map-and-list). This measure uses historical voting results to assign each congressional district a score from far left to far right. As above, we’ll normalize the score between -1 and 1 and take the absolutely value
cookpvi <- read_csv('data/cook_pvi.csv')
## New names:
## Rows: 435 Columns: 9
## ── Column specification
## ──────────────────────────────────────────────────────── Delimiter: "," chr
## (9): district, name, party, district_description, pvi, biden, trump...7,...
## ℹ 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.
## • `trump` -> `trump...7`
## • `trump` -> `trump...9`
cookpvi <- cookpvi %>%
rename(trump2020 = trump...7,
trump2016 = trump...9) %>%
mutate(pvi_num =
case_when(str_detect(pvi,'R') ~ as.numeric(str_extract(pvi,'[0-9]+')),
str_detect(pvi,'D') ~ as.numeric(str_extract(pvi,'[0-9]+'))*-1,
TRUE ~ 0))
min_score <- min(cookpvi$pvi_num)
max_score <- max(cookpvi$pvi_num)
cookpvi <- cookpvi %>%
mutate(pvi_abs = abs(pvi_num),
pvi_norm = 2 * (pvi_num - min_score) / (max_score - min_score) - 1,
pvi_norm_abs = abs(pvi_norm))
cookpvi %>%
ggplot() +
geom_freqpoly(aes(pvi_norm_abs), bins = 20)
cookpvi %>%
ggplot() +
geom_freqpoly(aes(pvi_abs), bins = 20, color = 'red')
The final measure is the Efficiency Gap. This metric is frequently cited in political science literature as a measure of gerrymandering (see Stephanopoulos, Nicholas, and McGhee. “Partisan Gerrymandering and the Efficiency Gap.”). The Efficiency Gap measures “wasted” votes, framed as the amount of (i) votes cast for a candidate who didn’t win, or (ii) votes cast for a candidate who did win in excess of the number of votes required to win.
I was unable to locate a readily available measure of the effiency gap, so I’ll calculate my own using historical election data from the MIT Election Lab (https://electionlab.mit.edu/data).
elections <- read_csv('data/1976-2020-house.csv')
## Rows: 31103 Columns: 20
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (8): state, state_po, office, district, stage, candidate, party, mode
## dbl (7): year, state_fips, state_cen, state_ic, candidatevotes, totalvotes, ...
## lgl (5): runoff, special, writein, unofficial, fusion_ticket
##
## ℹ 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.
elections <- elections %>%
filter(stage == 'GEN',
year > 2010) %>%
mutate(yearStateDist = paste0(year, state_po, district))
elections <- elections %>%
group_by(yearStateDist) %>%
mutate(result = case_when(candidatevotes == max(candidatevotes) ~ 'winner',
candidatevotes != max(candidatevotes) ~ 'loser')) %>%
ungroup()
elections <- elections %>%
mutate(wastedVotes = case_when(result == 'winner' ~ (candidatevotes - (totalvotes %/% 2)),
result == 'loser' ~ candidatevotes),
wastedVotes = case_when(party == 'DEMOCRAT' ~ -wastedVotes,
party == 'REPUBLICAN' ~ wastedVotes,
TRUE ~ 0))
eff_gap <- elections %>%
group_by(year, state_po, district) %>%
summarize(netWastedVotes = sum(wastedVotes),
totalVotes = sum(totalvotes),
eff_gap = abs(netWastedVotes / totalVotes),
.groups = 'drop') %>%
group_by(state_po, district) %>%
summarize(avg_eff_gap = mean(eff_gap),
.groups = 'keep') %>%
ungroup()
summary(eff_gap)
## state_po district avg_eff_gap
## Length:436 Length:436 Min. :0.005533
## Class :character Class :character 1st Qu.:0.059444
## Mode :character Mode :character Median :0.094613
## Mean :0.101799
## 3rd Qu.:0.132682
## Max. :0.465411
## NA's :1
eff_gap %>%
ggplot(aes(avg_eff_gap)) +
geom_histogram(binwidth = 0.02)
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
eff_gap <- eff_gap %>%
mutate(avg_eff_gap = if_else(avg_eff_gap > 0.25, 0.25, avg_eff_gap))
min_score <- min(eff_gap$avg_eff_gap, na.rm = TRUE)
max_score <- max(eff_gap$avg_eff_gap, na.rm = TRUE)
eff_gap <- eff_gap %>%
mutate(avg_eff_gap_norm = (avg_eff_gap - min_score) / (max_score - min_score))
eff_gap %>%
ggplot(aes(avg_eff_gap_norm)) +
geom_histogram(binwidth = 0.05)
## Warning: Removed 1 rows containing non-finite values (`stat_bin()`).
eff_gap %>%
arrange(desc(avg_eff_gap))
## # A tibble: 436 × 4
## state_po district avg_eff_gap avg_eff_gap_norm
## <chr> <chr> <dbl> <dbl>
## 1 FL 010 0.25 1
## 2 FL 012 0.25 1
## 3 FL 014 0.25 1
## 4 FL 015 0.25 1
## 5 FL 021 0.25 1
## 6 FL 025 0.25 1
## 7 FL 027 0.25 1
## 8 GA 003 0.25 1
## 9 GA 005 0.25 1
## 10 OK 001 0.25 1
## # ℹ 426 more rows
Finally, I’ll combine datasets together and map them to the candidates dataframe.
First, I need to account for states that have only a single congressional district, as the naming convention used to identify that district differs across datasets.
one_dist_state_regex <- 'AK|WY|ND|SD|VT|DE|DC|MT'
Then, I’ll prepare the dataframes themselves, aligning the naming of states and districts.
district_ideology <- district_ideology %>%
filter(state != 'NA', !is.na(mrp_ideology), !is.na(irt_ideology_unweighted)) %>%
mutate(cd_fips = sprintf('%04d', as.numeric(cd_fips)),
state_code = str_sub(cd_fips, 1, 2),
district = str_sub(cd_fips, 3, 4)) %>%
left_join(
data.frame(state = state.name,
state_abb = state.abb)) %>%
arrange(cd_fips, desc(congress)) %>%
mutate(state_abb = if_else(is.na(state_abb), 'DC', state_abb),
district = if_else(str_detect(state_abb,one_dist_state_regex),
'00',district),
dupe = duplicated(cd_fips)) %>%
filter(dupe == FALSE) %>%
rename(compete_ideology = mrp_norm_abs)
## Joining with `by = join_by(state)`
cookpvi <- cookpvi %>%
rename(state_dist = district) %>%
mutate(state = str_sub(state_dist, 1, 2),
district = str_sub(state_dist, 4, 5),
district = if_else(str_detect(state, one_dist_state_regex),
'00', district)) %>%
rename(compete_cookpvi = pvi_norm_abs)
eff_gap <- eff_gap %>%
mutate(district = sprintf('%02d', as.numeric(district)),
district = if_else(str_detect(state_po, one_dist_state_regex),
'00', district)) %>%
rename(compete_eff_gap = avg_eff_gap_norm)
Finally, I’ll read in the candidates data and perform the join.
candidates <- read_csv('data/candidates_partisanship.csv')
## Rows: 4216 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (10): fec_id, name, state, party, office, incumbent_challenge, candidate...
## dbl (5): district, govtrack_id, partisan_score_twitter, partisan_score_nomi...
##
## ℹ 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.
candidates <- candidates %>%
mutate(district = sprintf('%02d', district),
district = if_else(str_detect(state, one_dist_state_regex),
'00', district)) %>%
left_join(
select(district_ideology, compete_ideology, state_abb, district),
by = c('state' = 'state_abb', 'district')
) %>%
left_join(
select(cookpvi, compete_cookpvi, state, district),
by = c('state', 'district')
) %>%
left_join(
select(eff_gap, compete_eff_gap, state_po, district),
by = c('state' = 'state_po', 'district')
)
## Warning in left_join(., select(district_ideology, compete_ideology, state_abb, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 155 of `x` matches multiple rows in `y`.
## ℹ Row 309 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
## Warning in left_join(., select(cookpvi, compete_cookpvi, state, district), : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 155 of `x` matches multiple rows in `y`.
## ℹ Row 295 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
data('fips_codes')
districts_sf <- congressional_districts() %>%
st_simplify(dTolerance = 10000) %>%
data.frame() %>%
left_join(
fips_codes %>%
select(state, state_code) %>%
unique(),
by = c('STATEFP' = 'state_code')
)
## Retrieving data for the year 2021
##
|
| | 0%
|
| | 1%
|
|= | 1%
|
|= | 2%
|
|== | 2%
|
|== | 3%
|
|== | 4%
|
|=== | 4%
|
|=== | 5%
|
|==== | 5%
|
|==== | 6%
|
|===== | 6%
|
|===== | 7%
|
|===== | 8%
|
|====== | 8%
|
|====== | 9%
|
|======= | 9%
|
|======= | 10%
|
|======= | 11%
|
|======== | 11%
|
|======== | 12%
|
|========= | 12%
|
|========= | 13%
|
|========= | 14%
|
|========== | 14%
|
|========== | 15%
|
|=========== | 15%
|
|=========== | 16%
|
|============ | 16%
|
|============ | 17%
|
|============ | 18%
|
|============= | 18%
|
|============= | 19%
|
|============== | 19%
|
|============== | 20%
|
|============== | 21%
|
|=============== | 21%
|
|=============== | 22%
|
|================ | 22%
|
|================ | 23%
|
|================= | 24%
|
|================= | 25%
|
|================== | 25%
|
|================== | 26%
|
|=================== | 26%
|
|=================== | 27%
|
|=================== | 28%
|
|==================== | 28%
|
|==================== | 29%
|
|===================== | 29%
|
|===================== | 30%
|
|===================== | 31%
|
|====================== | 31%
|
|====================== | 32%
|
|======================= | 32%
|
|======================= | 33%
|
|======================= | 34%
|
|======================== | 34%
|
|======================== | 35%
|
|========================= | 35%
|
|========================= | 36%
|
|========================== | 36%
|
|========================== | 37%
|
|========================== | 38%
|
|=========================== | 38%
|
|=========================== | 39%
|
|============================ | 39%
|
|============================ | 40%
|
|============================ | 41%
|
|============================= | 41%
|
|============================= | 42%
|
|============================== | 42%
|
|============================== | 43%
|
|============================== | 44%
|
|=============================== | 44%
|
|=============================== | 45%
|
|================================ | 45%
|
|================================ | 46%
|
|================================= | 46%
|
|================================= | 47%
|
|================================= | 48%
|
|================================== | 48%
|
|================================== | 49%
|
|=================================== | 49%
|
|=================================== | 50%
|
|=================================== | 51%
|
|==================================== | 51%
|
|==================================== | 52%
|
|===================================== | 52%
|
|===================================== | 53%
|
|===================================== | 54%
|
|====================================== | 54%
|
|====================================== | 55%
|
|======================================= | 55%
|
|======================================= | 56%
|
|======================================== | 56%
|
|======================================== | 57%
|
|======================================== | 58%
|
|========================================= | 58%
|
|========================================= | 59%
|
|========================================== | 59%
|
|========================================== | 60%
|
|========================================== | 61%
|
|=========================================== | 61%
|
|=========================================== | 62%
|
|============================================ | 62%
|
|============================================ | 63%
|
|============================================ | 64%
|
|============================================= | 64%
|
|============================================= | 65%
|
|============================================== | 65%
|
|============================================== | 66%
|
|=============================================== | 66%
|
|=============================================== | 67%
|
|=============================================== | 68%
|
|================================================ | 68%
|
|================================================ | 69%
|
|================================================= | 69%
|
|================================================= | 70%
|
|================================================= | 71%
|
|================================================== | 71%
|
|================================================== | 72%
|
|=================================================== | 72%
|
|=================================================== | 73%
|
|=================================================== | 74%
|
|==================================================== | 74%
|
|==================================================== | 75%
|
|===================================================== | 75%
|
|===================================================== | 76%
|
|====================================================== | 76%
|
|====================================================== | 77%
|
|====================================================== | 78%
|
|======================================================= | 78%
|
|======================================================= | 79%
|
|======================================================== | 79%
|
|======================================================== | 80%
|
|======================================================== | 81%
|
|========================================================= | 81%
|
|========================================================= | 82%
|
|========================================================== | 82%
|
|========================================================== | 83%
|
|========================================================== | 84%
|
|=========================================================== | 84%
|
|=========================================================== | 85%
|
|============================================================ | 85%
|
|============================================================ | 86%
|
|============================================================= | 86%
|
|============================================================= | 87%
|
|============================================================= | 88%
|
|============================================================== | 88%
|
|============================================================== | 89%
|
|=============================================================== | 89%
|
|=============================================================== | 90%
|
|=============================================================== | 91%
|
|================================================================ | 91%
|
|================================================================ | 92%
|
|================================================================= | 92%
|
|================================================================= | 93%
|
|================================================================= | 94%
|
|================================================================== | 94%
|
|================================================================== | 95%
|
|=================================================================== | 95%
|
|=================================================================== | 96%
|
|==================================================================== | 96%
|
|==================================================================== | 97%
|
|==================================================================== | 98%
|
|===================================================================== | 98%
|
|===================================================================== | 99%
|
|======================================================================| 99%
|
|======================================================================| 100%
states_sf <- states(cb = TRUE) %>%
st_simplify(dTolerance = 1000) %>%
data.frame() %>%
filter(STUSPS != 'AS',
STUSPS != 'GU',
STUSPS != 'MP',
STUSPS != 'VI',
STUSPS != 'PR',)
## Retrieving data for the year 2021
##
|
| | 0%
|
|= | 1%
|
|= | 2%
|
|== | 2%
|
|== | 3%
|
|=== | 4%
|
|==== | 5%
|
|==== | 6%
|
|===== | 7%
|
|===== | 8%
|
|====== | 8%
|
|====== | 9%
|
|======= | 10%
|
|======== | 11%
|
|======== | 12%
|
|========= | 12%
|
|========= | 13%
|
|========== | 14%
|
|========== | 15%
|
|=========== | 15%
|
|=========== | 16%
|
|============ | 17%
|
|============ | 18%
|
|============= | 18%
|
|============= | 19%
|
|============== | 20%
|
|============== | 21%
|
|=============== | 21%
|
|=============== | 22%
|
|================ | 23%
|
|================ | 24%
|
|================= | 24%
|
|================= | 25%
|
|================== | 25%
|
|================== | 26%
|
|=================== | 27%
|
|==================== | 28%
|
|==================== | 29%
|
|===================== | 29%
|
|===================== | 30%
|
|===================== | 31%
|
|====================== | 31%
|
|====================== | 32%
|
|======================= | 32%
|
|======================= | 33%
|
|======================== | 34%
|
|======================== | 35%
|
|========================= | 35%
|
|========================= | 36%
|
|========================== | 37%
|
|========================== | 38%
|
|=========================== | 38%
|
|=========================== | 39%
|
|============================ | 40%
|
|============================ | 41%
|
|============================= | 41%
|
|============================= | 42%
|
|============================== | 42%
|
|============================== | 43%
|
|=============================== | 44%
|
|=============================== | 45%
|
|================================ | 45%
|
|================================ | 46%
|
|================================= | 47%
|
|================================== | 48%
|
|================================== | 49%
|
|=================================== | 50%
|
|==================================== | 51%
|
|==================================== | 52%
|
|===================================== | 52%
|
|===================================== | 53%
|
|===================================== | 54%
|
|====================================== | 54%
|
|====================================== | 55%
|
|======================================= | 55%
|
|======================================= | 56%
|
|======================================== | 57%
|
|========================================= | 58%
|
|========================================= | 59%
|
|========================================== | 59%
|
|========================================== | 60%
|
|=========================================== | 61%
|
|=========================================== | 62%
|
|============================================ | 63%
|
|============================================= | 64%
|
|============================================= | 65%
|
|============================================== | 65%
|
|============================================== | 66%
|
|=============================================== | 67%
|
|================================================ | 68%
|
|================================================ | 69%
|
|================================================= | 70%
|
|================================================== | 71%
|
|================================================== | 72%
|
|=================================================== | 73%
|
|==================================================== | 74%
|
|==================================================== | 75%
|
|===================================================== | 75%
|
|===================================================== | 76%
|
|====================================================== | 77%
|
|======================================================= | 78%
|
|======================================================= | 79%
|
|======================================================== | 80%
|
|======================================================== | 81%
|
|========================================================= | 81%
|
|========================================================= | 82%
|
|========================================================== | 83%
|
|=========================================================== | 84%
|
|=========================================================== | 85%
|
|============================================================ | 85%
|
|============================================================ | 86%
|
|============================================================= | 86%
|
|============================================================= | 87%
|
|============================================================= | 88%
|
|============================================================== | 88%
|
|============================================================== | 89%
|
|=============================================================== | 89%
|
|=============================================================== | 90%
|
|================================================================ | 91%
|
|================================================================ | 92%
|
|================================================================= | 92%
|
|================================================================= | 93%
|
|================================================================== | 94%
|
|================================================================== | 95%
|
|=================================================================== | 96%
|
|==================================================================== | 97%
|
|==================================================================== | 98%
|
|===================================================================== | 98%
|
|===================================================================== | 99%
|
|======================================================================| 100%
candidates_sf <- candidates %>%
left_join(
select(districts_sf, state, CD116FP, geometry),
by = c('state', 'district' = 'CD116FP'),
) %>%
left_join(
select(states_sf, STUSPS, geometry), suffix = c('_distr','_state'),
by = c('state' = 'STUSPS')
)
I’ll also put together a few geographic visuals, mapping scores onto
a map. I’ll use the tigris package (https://cran.r-project.org/web/packages/tigris/tigris.pdf).
candidates_sf %>%
filter(state != 'HI',
state != 'AK',
state != 'PR') %>%
ggplot() +
geom_sf(aes(geometry = geometry_distr, fill = compete_ideology)) +
geom_sf(aes(geometry = geometry_state), color = 'blue', size = 0.5, fill = NA) +
scale_fill_gradient(low = 'gray100',
high = 'purple') +
theme(legend.position="bottom")
candidates_sf %>%
filter(state != 'HI',
state != 'AK',
state != 'PR') %>%
ggplot() +
geom_sf(aes(geometry = geometry_distr, fill = compete_cookpvi)) +
scale_fill_gradient(low = 'gray100',
high = 'purple') +
theme(legend.position="bottom")
candidates_sf %>%
filter(state != 'HI',
state != 'AK',
state != 'PR') %>%
ggplot() +
geom_sf(aes(geometry = geometry_distr, fill = compete_eff_gap)) +
scale_fill_gradient(low = 'gray100',
high = 'purple') +
theme(legend.position="bottom")
candidates %>%
ggplot(aes(compete_ideology, compete_cookpvi)) +
geom_point() +
geom_smooth(method = 'lm', formula = 'y ~ x')
## Warning: Removed 132 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 132 rows containing missing values (`geom_point()`).
candidates %>%
ggplot(aes(compete_ideology, compete_eff_gap)) +
geom_point() +
geom_smooth(method = 'lm', formula = 'y ~ x')
## Warning: Removed 204 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 204 rows containing missing values (`geom_point()`).
candidates %>%
ggplot(aes(compete_cookpvi, compete_eff_gap)) +
geom_point() +
geom_smooth(method = 'lm', formula = 'y ~ x')
## Warning: Removed 225 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 225 rows containing missing values (`geom_point()`).
Distributions
candidates %>%
mutate(dupe = duplicated(name)) %>%
filter(dupe == FALSE) %>%
ggplot() +
geom_freqpoly(aes(compete_cookpvi, after_stat(density), color = 'blue'), bins = 20) +
geom_freqpoly(aes(compete_eff_gap, after_stat(density), color = 'green'), bins = 20) +
geom_freqpoly(aes(compete_ideology, after_stat(density), color = 'red'), bins = 20) +
scale_colour_manual(name = 'Variable',
values = c('blue' = 'blue', 'green' = 'green', 'red' = 'red'),
labels = c('cookpvi','eff_gap', 'ideology')) +
xlab('Competitiveness Score') +
ylab('Density') +
theme(legend.position = 'bottom')
## Warning: Removed 130 rows containing non-finite values (`stat_bin()`).
## Warning: Removed 191 rows containing non-finite values (`stat_bin()`).
## Warning: Removed 107 rows containing non-finite values (`stat_bin()`).
Export
write_csv(candidates, 'data/candidates_partisan_compete.csv')