Skip to content

Commit 8b7614a

Browse files
author
Sean Long
committed
ensure updated documentation, moved eiExpand features to rpv_coef_plot and rpv_toDF, moved example_rpvDF for test of new functions
1 parent 5acfdce commit 8b7614a

File tree

11 files changed

+409
-56
lines changed

11 files changed

+409
-56
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ export(predict_race_multi_barreled)
4040
export(race_cand_cors)
4141
export(race_check_2_3)
4242
export(resolve_missing_vals)
43+
export(rpv_coef_plot)
4344
export(rpv_density)
45+
export(rpv_toDF)
4446
export(stdize_votes)
4547
export(stdize_votes_all)
4648
export(strip_special_characters)

R/data.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,7 @@
339339
#' @usage data(gwinnett_ei)
340340
"gwinnett_ei"
341341

342+
#' Example RPV analysis results in Washington State
343+
#'
344+
"example_rpvDF"
345+

R/ei_rxc.R

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,9 @@ ei_rxc <- function(
164164
chain = seq_len(n_chains),
165165
.inorder = TRUE,
166166
.packages = c("ei"),
167-
.options.snow = opts
168-
) %myinfix% {
169-
# Bayes model estimation
170-
suppressWarnings(
167+
.options.snow = opts) %myinfix%
168+
{
169+
suppressWarnings(
171170
md_out <- ei.MD.bayes(
172171
formula = formula,
173172
sample = samples,
@@ -301,7 +300,7 @@ ei_rxc <- function(
301300
)
302301
} else {
303302
colnames(results_table) <- c(
304-
"cand", "race", "mean", "sd", "ci_95_lower", "ci_95_upper"
303+
"cand", "race", "mean", "sd", "ci_lower", "ci_upper"
305304
)
306305
}
307306

R/rpv_coef_plot.R

Lines changed: 150 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,161 @@
1-
rpv_coef_plot <- function (rpvDF = NULL,
2-
title = "Racially Polarized Voting Analysis Estimates",
3-
caption = "Data: eiCompare RPV estimates",
4-
ylab = "Pct. Voting Black-Preferred Candidate",
5-
colors = NULL,
6-
race_order = c("Black", "White")
7-
)
8-
9-
{
1+
#' @export
2+
#' @import ggplot2
3+
#' @importFrom rlang .data
4+
#'
5+
#' @author Rachel Carroll <rachelcarroll4@gmail.com>
6+
#' @author Stephen El-Khatib <stevekhatib@gmail.com>
7+
#' @author Loren Collingwood <lcollingwood@unm.edu>
8+
#'
9+
#' @title Racially Polarized Voting Analysis (RPV) Coefficient Plot
10+
#' @description Creates a coefficient plot showing of RPV results estimate ranges
11+
#' of all contests by voter race
12+
#' @param rpvDF A data.frame containing RPV results
13+
#' @param title The plot title
14+
#' @param caption The plot caption
15+
#' @param ylab Label along y axis
16+
#' @param colors Character vector of colors, one for each racial group. The order
17+
#' of colors will be respective to the order of racial groups.
18+
#' @param race_order Character vector of racial groups from the \code{voter_race} column of
19+
#' \code{rpvDF} in the order they should appear in the plot. If not specified,
20+
#' the race groups will appear in alphabetical order.
21+
#'
22+
#' @return Coefficient plot of RPV analysis as a ggplot2 object
23+
#'
24+
#' @examples
25+
#'library(eiCompare)
26+
#'data(example_rpvDF)
27+
#'
28+
#'dem_rpv_results <- example_rpvDF %>% dplyr::filter(Party == "Democratic")
29+
#'rpv_coef_plot(dem_rpv_results)
30+
#'
31+
rpv_coef_plot <- function(
32+
rpvDF = NULL,
33+
title = "Racially Polarized Voting Analysis Estimates",
34+
caption = "Data: eiCompare RPV estimates",
35+
ylab = NULL,
36+
colors = NULL,
37+
race_order = NULL
38+
) {
39+
40+
# ----------------------------- QC CHECKS -----------------------------
41+
1042
colnames(rpvDF) <- stringr::str_to_lower(colnames(rpvDF))
11-
12-
rpvDF$voter_race <- factor(rpvDF$voter_race, levels = race_order)
43+
44+
##### new code (copied from eiExpand lines 40-58)
45+
# make sure rpvDF argument is defined
46+
if(is.null(rpvDF)){stop("you must include rpvDF argument")}
47+
48+
# make sure necessary columns are included
49+
dif <- setdiff(c("party", "voter_race", "estimate", "lower_bound", "upper_bound"),
50+
colnames(rpvDF))
51+
52+
if( length(dif) > 0 ) {
53+
stop(paste("rpvDF is missing the following fields:",
54+
paste(dif, collapse = ", ")))
55+
}
56+
57+
# make sure only one party is in rpvDF
58+
if( length(unique(rpvDF$party)) > 1 ){
59+
stop("rpvDF should only contain one unique values in column Party")}
60+
##### end QC checks
61+
62+
# ---------------------- Prep Data and Plot Inputs ----------------------
63+
64+
##### Voter Race Order #####
65+
##### old code (from Updates_7_1_2024.R)
66+
# rpvDF$voter_race <- factor(rpvDF$voter_race, levels = race_order)
67+
##### new code (copied from eiExpand lines 64-69)
68+
# proper case for plot
69+
rpvDF$voter_race <- stringr::str_to_title(rpvDF$voter_race)
70+
#get factor order if not specified
71+
if( is.null(race_order) ) { race_order <- sort(unique(rpvDF$voter_race)) }
72+
#set factor
73+
rpvDF$voter_race <- factor(rpvDF$voter_race,
74+
levels = race_order)
75+
76+
##### Colors #####
1377
len_race <- length(unique(rpvDF$voter_race))
14-
if (is.null(colors)) {
15-
if (len_race == 2) {
78+
##### old code (from Updates_7_1_2024.R)
79+
# if (is.null(colors)) {
80+
# if (len_race == 2) {
81+
# race_colors <- c(viridis::viridis(10)[4], viridis::viridis(10)[7])
82+
# names(race_colors) <- race_order
83+
# ggplot_color_obj <- scale_color_manual(values = race_colors)
84+
# }
85+
# else {
86+
# ggplot_color_obj <- viridis::scale_color_viridis(drop = FALSE,
87+
# discrete = TRUE, option = "turbo", alpha = 0.8)
88+
# }
89+
# }
90+
##### new code (copied from eiExpand lines 71-85)
91+
if( is.null(colors) ){
92+
if( len_race == 2 ){
1693
race_colors <- c(viridis::viridis(10)[4], viridis::viridis(10)[7])
1794
names(race_colors) <- race_order
95+
1896
ggplot_color_obj <- scale_color_manual(values = race_colors)
97+
98+
} else {
99+
ggplot_color_obj <- viridis::scale_color_viridis(drop = FALSE,
100+
discrete = TRUE,
101+
option = "turbo",
102+
alpha = .8)
19103
}
20-
else {
21-
ggplot_color_obj <- viridis::scale_color_viridis(drop = FALSE,
22-
discrete = TRUE, option = "turbo", alpha = 0.8)
23-
}
24-
}
25-
if (is.null(ylab)) {
104+
} # END if( is.null(colors) )
105+
106+
##### ylab #####
107+
if( is.null(ylab) ){
26108
prty <- unique(rpvDF$party) %>% stringr::str_to_title()
27-
ylab <- paste("Percent Voting for", prty, "Candidate")
109+
ylab <- paste("Percent Voting for", prty, "Candidate")
28110
}
29-
30-
coef_plot <- ggplot(rpvDF, aes(x = 0, y = 0:100)) +
31-
scale_y_continuous(breaks = seq(0, 100, by = 10),
32-
limits = c(0, 100),
33-
labels = sprintf("%0.1f%%", seq(0, 100, by = 10)),
34-
expand = c(0, 0)) + geom_hline(yintercept = 50,
35-
colour = "#000000", size = 0.75) +
36-
geom_pointrange(aes(y = .data$estimate,
37-
ymin = .data$lower_bound,
38-
ymax = .data$upper_bound,
111+
112+
##### mean percent vote for label #####
113+
mean <- rpvDF %>%
114+
dplyr::group_by(.data$voter_race) %>%
115+
dplyr::summarize(avg = mean(.data$estimate))
116+
117+
rpvDF <- dplyr::left_join(rpvDF, mean, by = "voter_race")
118+
rpvDF$panelLab <- paste0(rpvDF$voter_race, "\n(mean: ", round(rpvDF$avg,1), "%)")
119+
120+
# -------------------------- Build Plot --------------------------
121+
122+
coef_plot <- ggplot(rpvDF,
123+
aes(x = 0, y = 0:100)) +
124+
scale_y_continuous(breaks = seq(0,100, by = 10),
125+
limits = c(0, 100),
126+
labels = sprintf("%0.1f%%", seq(0,100, by = 10)),
127+
expand = c(0, 0)) +
128+
geom_hline(yintercept = 50, colour = "#000000", size = 0.75) + # Line at 0
129+
geom_pointrange(aes(y = .data$estimate,
130+
ymin = .data$lower_bound,
131+
ymax = .data$upper_bound,
39132
color = .data$voter_race),
40-
position = position_jitter(width = 0.1),
41-
linewidth = .5, fatten = 1.5,
42-
show.legend = F) +
43-
ggplot_color_obj +
44-
facet_grid(~panellab) +
45-
labs(y = ylab, title = title, caption = caption) + theme_minimal() +
46-
theme(legend.title = element_blank(), axis.title.x = element_blank(),
47-
axis.ticks.x = element_blank(), axis.text.x = element_blank(),
48-
panel.border = element_rect(fill = NA, colour = "grey"),
49-
panel.grid.major.x = element_blank(),
50-
panel.grid.minor.x = element_blank(),
51-
panel.grid.minor.y = element_blank(),
52-
axis.text.y = element_text(size = 20, face = "bold",
53-
family = "serif"),
54-
axis.title.y = element_text(size = 24,
55-
face = "bold", family = "serif"),
56-
strip.text.x = element_text(size = 15,
57-
face = "bold", family = "serif"),
58-
title = element_text(size = 25, hjust = 0.5, face = "bold", family = "serif"),
59-
plot.caption = element_text(size = 12, face = "italic",
60-
family = "serif"))
133+
position = position_jitter(width = 0.1),
134+
size = 2,
135+
fatten = 1.5,
136+
show.legend = F) + # Ranges for each coefficient
137+
ggplot_color_obj +
138+
facet_grid(~panelLab) +
139+
labs(y = ylab,
140+
title = title,
141+
caption = caption) + # Labels
142+
theme_minimal() +
143+
theme(legend.title = element_blank(),
144+
axis.title.x = element_blank(),
145+
axis.ticks.x = element_blank(),
146+
axis.text.x = element_blank(),
147+
panel.border = element_rect(fill = NA, colour = "grey"),
148+
panel.grid.major.x = element_blank(),
149+
panel.grid.minor.x = element_blank(),
150+
panel.grid.minor.y = element_blank(),
151+
axis.text.y = element_text(size = 20, face = "bold", family = "serif"),
152+
axis.title.y = element_text(size = 24, face = "bold", family = "serif"),
153+
strip.text.x = element_text(size = 15, face = "bold", family = "serif"),
154+
#strip.text.x = element_blank(),
155+
title = element_text(size = 30, hjust = .5, face = "bold", family = "serif"),
156+
plot.caption = element_text(size = 12, face = "italic", family = "serif")
157+
)
158+
159+
# -------------------------- Return --------------------------
61160
return(coef_plot)
62161
}

R/rpv_toDF.R

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,73 @@
1+
#' @export
2+
#'
3+
#'@author Rachel Carroll <rachelcarroll4@@gmail.com>
4+
#'@author Kassra Oskooii <kassrao@@gmail.com>
5+
6+
#' @title Transform RPV results from \code{eiCompare} into a simple dataframe
7+
#' object
8+
#'
9+
#' @description Create a dataframe from RPV analysis output to facilitate
10+
#' RPV visualizations. The output dataframe of this function can be used directly
11+
#' in \code{rpv_plot()}.
12+
#'
13+
#' @param rpv_results RPV analysis results either from the output of
14+
#' \code{ei_iter()} or \code{ei_rxc()} from the \code{eiCompare} package or from
15+
#' the internal function \code{ci_cvap_full()}.
16+
#' @param model A string indicating the model used to create \code{rpv_results}.
17+
#' Examples include "ei", "rxc", "ei cvap", etc.
18+
#' @param candidate A character vector of candidate names written as they would
19+
#' appear on a visualization. The candidate names must be listed in the same order
20+
#' as the candidate estimates appear in \code{rpv_results}, i.e the same order
21+
#' as the \code{cands} argument in \code{eiCompare::ei_iter()} or
22+
#' \code{eiCompare::ei_rxc()}.
23+
#' @param preferred_candidate A character vector of races indicating racial
24+
#' preference of each candidate. The racial preferences must be listed
25+
#' in the correct order with respect to \code{candidate}.
26+
#' @param party A character vector containing the political parties of the
27+
#' candidates. Must be listed in the correct order with respect to
28+
#' \code{candidate}.
29+
#' @param jurisdiction A string of the jurisdiction.
30+
#' @param election_type A string on the election type (usually "General" or
31+
#' "Primary")
32+
#' @param year The year of the contest
33+
#' @param contest A string of contest name as it would appear in an rpv visualization
34+
#' (e.g. "President" or "Sec. of State")
35+
#'
36+
#' @return rpv results in a data.frame
37+
#'
38+
#' @examples
39+
#' \donttest{
40+
#' #library(eiCompare)
41+
#' #data("south_carolina")
42+
#' #prec_election_demog <- south_carolina[1:50,]
43+
#'
44+
#' ## run rpv analysis
45+
#' #eiVote <- eiCompare::ei_iter(
46+
#' # data = prec_election_demog,
47+
#' # cand_cols = c('pct_mcmaster', 'pct_smith'),
48+
#' # race_cols = c('pct_white', 'pct_black'),
49+
#' # totals_col = "total_vap"
50+
#' #) %>%
51+
#' # rpv_normalize(
52+
#' # cand_cols = c('pct_mcmaster', 'pct_smith'),
53+
#' # race_cols = c('pct_white', 'pct_black')
54+
#' # )
55+
#'
56+
#' ## use function to create dataframe from rpv results
57+
#' #plotDF <- rpv_toDF(
58+
#'# rpv_results = eiVote,
59+
#'# model = "ei vap", #since we used ei_iter model normalized with vap denominator
60+
#'# jurisdiction = "Statewide",
61+
#'# candidate = c("McMaster", "Smith"), #must be in correct order relative to rpv_results
62+
#'# preferred_candidate = c("White", "Black"), #must be in correct order rpv_results
63+
#'# party = c("Republican", "Democratic"),
64+
#'# election_type = "General",
65+
#'# year = "2020",
66+
#'# contest = "Governor"
67+
#'# )
68+
#' }
69+
70+
171
rpv_toDF <- function (rpv_results = NULL, model = NULL, jurisdiction = "",
272
preferred_candidate = "", party = "", election_type = "",
373
year = "", contest = "", candidate = "")
@@ -26,8 +96,11 @@ rpv_toDF <- function (rpv_results = NULL, model = NULL, jurisdiction = "",
2696
rownames(rpv_data) <- 1:nrow(rpv_data)
2797
colnames(rpv_data) <- colnames(rpv_data) %>% stringr::str_to_lower()
2898
newcols <- gsub("mean", "Estimate", colnames(rpv_data))
99+
# Handle both ci_95_lower and ci_lower naming conventions
29100
newcols <- gsub("ci_95_lower", "Lower_Bound", newcols)
30101
newcols <- gsub("ci_95_upper", "Upper_Bound", newcols)
102+
newcols <- gsub("ci_lower", "Lower_Bound", newcols)
103+
newcols <- gsub("ci_upper", "Upper_Bound", newcols)
31104
colnames(rpv_data) <- newcols
32105
plotDF <- rpv_data %>% dplyr::mutate(Model = model, Jurisdiction = jurisdiction,
33106
Election_Type = election_type, Year = as.numeric(year),

data/example_rpvDF.rda

2.05 KB
Binary file not shown.

man/eiCompare-package.Rd

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/ei_iter.Rd

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/example_rpvDF.Rd

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)