├── .Rprofile
├── .gitattributes
├── .gitignore
├── CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── R
├── funs_data-generation.R
├── funs_knitting.R
├── funs_notebook.R
├── funs_plots.R
├── funs_survey.R
└── graphics.R
├── README.Rmd
├── README.md
├── _packages.R
├── _targets.R
├── analysis
├── 01_clean-generate-data.Rmd
├── 02_tables.Rmd
├── 03_analysis.Rmd
├── Makefile
├── _site.yml
├── experiment.Rmd
├── files
│ ├── byu-irb-approval.pdf
│ ├── cnu-irb-approval.pdf
│ └── gsu-irb-approval.pdf
├── html
│ ├── fixes.css
│ └── footer.html
├── index.Rmd
├── output
└── text
│ ├── experiment.md
│ └── experiment_anonymized.md
├── data
├── DO-NOT-EDIT-ANY-FILES-IN-HERE-BY-HAND
├── derived_data
│ ├── survey_results.csv
│ └── survey_results.yaml
└── raw_data
│ ├── Market Simulator Version 01.xlsx
│ ├── data-FTjUv.csv
│ ├── data-xextT.csv
│ └── posterior_draws
│ └── .gitignore
├── img
├── data_large_color.png
├── materials_large_color.png
└── preregistered_large_color.png
├── manuscript
├── _output.yaml
├── appendix.Rmd
├── bibliography.bib
├── manuscript.Rmd
├── output
│ ├── extracted-citations.bib
│ └── html-support
│ │ ├── ath-1.0.0
│ │ └── ath-clean.css
│ │ ├── header-attrs-2.7
│ │ └── header-attrs.js
│ │ ├── kePrint-0.0.1
│ │ └── kePrint.js
│ │ └── lightable-0.0.1
│ │ └── lightable.css
└── pandoc
│ ├── bin
│ ├── anonymize.py
│ └── replacements.csv
│ ├── csl
│ ├── american-political-science-association.csl
│ ├── apa.csl
│ ├── apsa-no-bib.csl
│ ├── chicago-author-date.csl
│ ├── chicago-fullnote-no-bib.csl
│ ├── chicago-syllabus-no-bib.csl
│ ├── chicago-syllabus.csl
│ └── the-open-university-harvard.csl
│ ├── css
│ └── ath-clean.css
│ └── templates
│ ├── ath-manuscript.docx
│ ├── html.html
│ ├── odt-manuscript.odt
│ ├── odt.odt
│ ├── reference-manuscript.odt
│ ├── reference.odt
│ ├── xelatex-manuscript.tex
│ └── xelatex.tex
├── renv.lock
├── renv
├── .gitignore
├── activate.R
└── settings.dcf
└── whocares.Rproj
/.Rprofile:
--------------------------------------------------------------------------------
1 | source("renv/activate.R")
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Site output
2 | analysis/_site/*
3 | analysis/survey/*.pdf
4 | analysis/survey/*.html
5 | README.html
6 |
7 | # Site output
8 | analysis/_site/*
9 | analysis/site_libs/*
10 |
11 | # knitr and caching stuff
12 | *_files/*
13 | *_cache/*
14 |
15 | # Targets stuff
16 | _targets
17 |
18 | # Manuscript output
19 | manuscript/*.log
20 | manuscript/output/*.docx
21 | manuscript/output/*.html
22 | manuscript/output/*.pdf
23 | manuscript/output/*.tex
24 | manuscript/output/figures/*
25 | manuscript/submissions/*
26 |
27 | # Large data files (these get downloaded automatically with
28 | # targets::tar_make(c(survey_results_file, gamma_draws_file)))
29 | data/private_data/*
30 | data/raw_data/final_data.rds
31 | data/raw_data/posterior_draws/*.rds
32 |
33 | # Other folders
34 | admin/*
35 | private/*
36 |
37 | # R stuff
38 | .Rproj.user
39 | .Rhistory
40 | .RData
41 | .Ruserdata
42 |
43 | # Miscellaneous
44 | *.~lock.*
45 | .DS_Store
46 | .dropbox
47 | Icon?
48 |
--------------------------------------------------------------------------------
/CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who
4 | contribute through reporting issues, posting feature requests, updating documentation,
5 | submitting pull requests or patches, and other activities.
6 |
7 | We are committed to making participation in this project a harassment-free experience for
8 | everyone, regardless of level of experience, gender, gender identity and expression,
9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
10 |
11 | Examples of unacceptable behavior by participants include the use of sexual language or
12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment,
13 | insults, or other unprofessional conduct.
14 |
15 | Project maintainers have the right and responsibility to remove, edit, or reject comments,
16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this
17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
18 | from the project team.
19 |
20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
21 | opening an issue or contacting one or more of the project maintainers.
22 |
23 | This Code of Conduct is adapted from the Contributor Covenant
24 | (http:contributor-covenant.org), version 1.0.0, available at
25 | http://contributor-covenant.org/version/1/0/0/
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We love pull requests from everyone. By participating in this project, you
4 | agree to abide by our [code of conduct](CONDUCT.md).
5 |
6 | ## Getting Started
7 |
8 | * Make sure you have a [GitHub account](https://github.com/signup/free). If you are not familar with git and GitHub, take a look at to get started.
9 | * [Submit a post for your issue](https://github.com///issues/), assuming one does not already exist.
10 | * Clearly describe your issue, including steps to reproduce when it is a bug, or some justification for a proposed improvement.
11 | * [Fork](https://github.com///#fork-destination-box) the repository on GitHub to make a copy of the repository on your account. Or use this line in your shell terminal:
12 |
13 | `git clone git@github.com:your-username/.git`
14 |
15 | ## Making changes
16 |
17 | * Edit the files, save often, and make commits of logical units, where each commit indicates one concept
18 | * Follow our [style guide](http://adv-r.had.co.nz/Style.html).
19 | * Make sure you write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
20 | * Make sure you have added the necessary tests for your code changes.
21 | * Run _all_ the tests using `devtools::check()` to assure nothing else was accidentally broken.
22 | * If you need help or unsure about anything, post an update to [your issue](https://github.com///issues/).
23 |
24 | ## Submitting your changes
25 |
26 | Push to your fork and [submit a pull request](https://github.com///compare/).
27 |
28 | At this point you're waiting on us. We like to at least comment on pull requests
29 | within a few days (and, typically, one business day). We may suggest
30 | some changes or improvements or alternatives.
31 |
32 | Some things you can do that will increase the chance that your pull request is accepted:
33 |
34 | * Engage in discussion on [your issue](https://github.com///issues/).
35 | * Be familiar with the backround literature cited in the [README](README.Rmd)
36 | * Write tests that pass.
37 | * Follow our [code style guide](http://adv-r.had.co.nz/Style.html).
38 | * Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2021 Suparna Chaudhry, Marc Dotson, and Andrew Heiss
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/R/funs_knitting.R:
--------------------------------------------------------------------------------
1 | library(bib2df)
2 | library(rvest)
3 | library(xml2)
4 | library(stringi)
5 |
6 | render_html <- function(input, output, csl, support_folder, ...) {
7 | # Add CSS file as a dependency so it goes into the _files directory
8 | dep <- htmltools::htmlDependency(
9 | name = "ath",
10 | version = "1.0.0",
11 | "pandoc/css",
12 | stylesheet = "ath-clean.css"
13 | )
14 | extra_dependencies <- list(dep)
15 |
16 | # IMPORTANT
17 | # When knitting to docx, bookdown deletes the _files directory for whatever
18 | # reason, so if you knit to HTML and then docx, you get a nice *_files
19 | # directly that then disappears when the Word file is done. One way around
20 | # this is to specify lib_dir here in rmarkdown::render()
21 |
22 | out <- rmarkdown::render(
23 | input = input,
24 | output_file = output,
25 | bookdown::html_document2(
26 | template = "pandoc/templates/html.html",
27 | pandoc_args = c("--metadata", "link-citations=true",
28 | "--metadata", "linkReferences=true",
29 | paste0("--csl=", csl)),
30 | md_extensions = "+raw_tex+smart-autolink_bare_uris+ascii_identifiers",
31 | toc = TRUE,
32 | number_sections = FALSE,
33 | self_contained = FALSE,
34 | theme = NULL,
35 | extra_dependencies = extra_dependencies,
36 | lib_dir = support_folder
37 | ),
38 | encoding = "UTF-8"
39 | )
40 |
41 | return(fs::path_rel(out))
42 | }
43 |
44 | render_pdf <- function(input, output, bibstyle, ...) {
45 | out <- rmarkdown::render(
46 | input = input,
47 | output_file = output,
48 | bookdown::pdf_document2(
49 | template = "pandoc/templates/xelatex.tex",
50 | latex_engine = "xelatex",
51 | dev = "cairo_pdf",
52 | pandoc_args = c("--top-level-division=section",
53 | "--shift-heading-level-by=0",
54 | "-V", bibstyle,
55 | "-V", "chapterstyle=hikma-article"),
56 | md_extensions = "+raw_tex+smart-autolink_bare_uris",
57 | toc = FALSE,
58 | keep_tex = FALSE,
59 | citation_package = "biblatex"
60 | ),
61 | encoding = "UTF-8"
62 | )
63 |
64 | return(fs::path_rel(out))
65 | }
66 |
67 | render_pdf_ms <- function(input, output, bibstyle, ...) {
68 | out <- rmarkdown::render(
69 | input = input,
70 | output_file = output,
71 | bookdown::pdf_document2(
72 | template = "pandoc/templates/xelatex-manuscript.tex",
73 | latex_engine = "xelatex",
74 | dev = "cairo_pdf",
75 | pandoc_args = c("--top-level-division=section",
76 | "--shift-heading-level-by=0",
77 | "-V", bibstyle),
78 | md_extensions = "+raw_tex+smart-autolink_bare_uris",
79 | toc = FALSE,
80 | keep_tex = FALSE,
81 | citation_package = "biblatex"
82 | ),
83 | encoding = "UTF-8"
84 | )
85 |
86 | return(fs::path_rel(out))
87 | }
88 |
89 | render_docx <- function(input, output, csl, ...) {
90 | out <- rmarkdown::render(
91 | input = input,
92 | output_file = output,
93 | bookdown::word_document2(
94 | reference_docx = "pandoc/templates/ath-manuscript.docx",
95 | pandoc_args = c(paste0("--csl=", csl)),
96 | md_extensions = "+raw_tex+smart-autolink_bare_uris",
97 | toc = FALSE,
98 | number_sections = FALSE
99 | ),
100 | encoding = "UTF-8"
101 | )
102 |
103 | return(fs::path_rel(out))
104 | }
105 |
106 |
107 | extract_bib <- function(input_rmd, input_bib, output, ...) {
108 | # Load the document
109 | document <- readLines(input_rmd)
110 |
111 | # Find all the citation-looking things in the document. This picks up e-mail
112 | # addresses (like example@gsu.edu) and it picks up bookdown-style references,
113 | # like \@ref(fig:thing). I filter out the "ref" entries, but leave the e-mail
114 | # addresses because there's too much possible variation there to easily remove
115 | # them. As long as there aren't citation keys like "gsu" or "gmail", it should
116 | # be fine. I also remove pandoc-crossref-style references like @fig:thing,
117 | # @tbl:thing, @eq:thing, and @sec:thing
118 | found_citations <- document %>%
119 | map(~as_tibble(str_match_all(., "@([[:alnum:]:&!~=_+-]+)")[[1]][,2])) %>%
120 | bind_rows() %>%
121 | filter(value != "ref",
122 | !str_starts(value, "fig:"),
123 | !str_starts(value, "tbl:"),
124 | !str_starts(value, "eq:"),
125 | !str_starts(value, "sec:")) %>%
126 | distinct(value) %>%
127 | pull(value)
128 |
129 | # Load the bibliography and convert to a data frame
130 | # When year is the last key in an entry and the closing entry brace is on the
131 | # same line, like `Year = {2006}}`, bib2df() parses the year as "2006}" and
132 | # includes the closing }, which then causes warnings about year not being an
133 | # integer. So here I remove all curly braces from the YEAR column
134 | suppressWarnings(suppressMessages({
135 | bib_df <- bib2df(input_bib) %>%
136 | mutate(YEAR = str_remove_all(YEAR, "\\{|\\}"))
137 | }))
138 |
139 | # In biblatex, entries can be cross-referenced using the crossref key. When
140 | # including something that's cross-referenced, like an incollection item, the
141 | # containing item should also be extracted
142 | if (any(names(bib_df) == "CROSSREF")) {
143 | crossrefs <- bib_df %>%
144 | filter(BIBTEXKEY %in% found_citations) %>%
145 | filter(!is.na(CROSSREF)) %>%
146 | pull(CROSSREF)
147 | } else {
148 | crossrefs <- as.character(0)
149 | }
150 |
151 | # Write a simplified bibtex file to disk (no BibDesk-specific entries) and only
152 | # include citations that appear in the document or that are cross-referenced
153 | bib_df %>%
154 | filter(BIBTEXKEY %in% c(found_citations, crossrefs)) %>%
155 | select(-starts_with("BDSK"), -RATING, -READ,
156 | -starts_with("DATE."), -KEYWORDS) %>%
157 | arrange(CATEGORY, BIBTEXKEY) %>%
158 | df2bib(output)
159 | }
160 |
161 |
162 | # string::stri_count_words() counts, um, words. That's its whole job. But it
163 | # doesn't count them like Microsoft Word counts them, and academic publishing
164 | # portals tend to care about Word-like counts. For instance, Word considers
165 | # hyphenated words to be one word, while stringr counts them as two (and even
166 | # worse, stringi counts / as word boundaries, so URLs can severely inflate your
167 | # word count).
168 | #
169 | # Also, academic writing typically doesn't count the title, abstract, table
170 | # text, figure captions, or equations as words in the manuscript (and it
171 | # SHOULDN'T count bibliographies, but it always seems to ugh).
172 | #
173 | # This parses the rendered HTML, removes extra elements, adjusts the text so
174 | # that stringi treats it more like Word, and then finally provides a more
175 | # accurate word count.
176 | count_words <- function(html) {
177 | # Read the HTML file
178 | ms_raw <- read_html(html)
179 |
180 | # Extract just the article, ignoring title, abstract, etc.
181 | ms <- ms_raw %>%
182 | html_nodes("article")
183 |
184 | # Get rid of figures, tables, and math
185 | xml_remove(ms %>% html_nodes("figure"))
186 | xml_remove(ms %>% html_nodes("table"))
187 | xml_remove(ms %>% html_nodes(".display")) # Block math
188 | xml_replace(ms %>% html_nodes(".inline"), read_xml("MATH")) %>%
189 | invisible()
190 |
191 | # Go through each child element in the article and extract it
192 | ms_cleaned_list <- map(html_children(ms), ~ {
193 | .x %>%
194 | html_text(trim = TRUE) %>%
195 | # ICU counts hyphenated words as multiple words, so replace - with DASH
196 | str_replace_all("\\-", "DASH") %>%
197 | # ICU also counts / as multiple words, so URLs go crazy. Replace / with SLASH
198 | str_replace_all("\\/", "SLASH") %>%
199 | # ICU *also* counts [things] in brackets multiple times, so kill those too
200 | str_replace_all("\\[|\\]", "") %>%
201 | # Other things to ignore
202 | str_replace_all("×", "")
203 | })
204 |
205 | # Get count of words! (close enough to Word)
206 | final_count <- sum(stri_count_words(ms_cleaned_list))
207 |
208 | class(final_count) <- c("wordcount", "numeric")
209 |
210 | return(final_count)
211 | }
212 |
213 | print.wordcount <- function(x) {
214 | cat(scales::comma(x), "words in manuscript\n")
215 | }
216 |
--------------------------------------------------------------------------------
/R/funs_notebook.R:
--------------------------------------------------------------------------------
1 | # This is adapted from TJ Mahr's {notestar}
2 | # (https://github.com/tjmahr/notestar/blob/main/R/tar-notebook.R).
3 | #
4 | # He did all the hard work figuring out how to dynamically generate targets
5 | # based on a bunch of files, while also checking for targets dependencies with
6 | # tarchetypes::tar_knitr_deps(), based on this issue in {tarchetypes}:
7 | # https://github.com/ropensci/tarchetypes/issues/23
8 | #
9 | # I just adapted it for an R Markdown website
10 |
11 | notebook_rmd_collate <- function(dir_notebook = "analysis") {
12 | index <- file.path(dir_notebook, "index.Rmd")
13 | posts <- list.files(
14 | path = dir_notebook,
15 | pattern = ".*[^index].Rmd",
16 | full.names = TRUE
17 | )
18 | c(index, posts)
19 | }
20 |
21 | rmd_to_html <- function(x) gsub("[.]Rmd$", ".html", x = x)
22 | html_to_rmd <- function(x) gsub("[.]html$", ".Rmd", x = x)
23 |
24 | lazy_list <- function(...) {
25 | q <- rlang::enexprs(..., .named = TRUE, .check_assign = TRUE)
26 | data <- list()
27 | for (x in seq_along(q)) {
28 | data[names(q[x])] <- list(rlang::eval_tidy(q[[x]], data = data))
29 | }
30 | data
31 | }
32 |
33 | knit_notebook_page <- function(rmd_in, html_out) {
34 | rmarkdown::render_site(rmd_in, encoding = "UTF-8")
35 | html_out
36 | }
37 |
38 | tar_notebook_pages <- function(
39 | dir_notebook = "analysis",
40 | dir_html = "analysis/_site",
41 | yaml_config = "analysis/_site.yml"
42 | ) {
43 |
44 | rmds <- notebook_rmd_collate(dir_notebook)
45 |
46 | values <- lazy_list(
47 | rmd_file = !! rmds,
48 | rmd_page = basename(.data$rmd_file),
49 | sym_rmd_page = rlang::syms(.data$rmd_page),
50 | rmd_deps = lapply(.data$rmd_file, tarchetypes::tar_knitr_deps_expr),
51 | html_page = rmd_to_html(.data$rmd_page),
52 | html_file = file.path(!! dir_html, .data$html_page)
53 | )
54 |
55 | list(
56 | # Add _site.yml as a dependency
57 | # Have to use tar_target_raw() instead of tar_target() so that yaml_config is usable
58 | tar_target_raw("site_yml", yaml_config, format = "file"),
59 |
60 | # Prepare targets for each of the notebook pages
61 | tarchetypes::tar_eval_raw(
62 | quote(
63 | targets::tar_target(rmd_page, c(rmd_file, site_yml), format = "file")
64 | ),
65 | values = values
66 | ),
67 |
68 | tarchetypes::tar_eval_raw(
69 | quote(
70 | targets::tar_target(
71 | html_page,
72 | command = {
73 | rmd_deps
74 | sym_rmd_page
75 | knit_notebook_page(rmd_file, html_file);
76 | html_file
77 | },
78 | format = "file"
79 | )
80 | ),
81 | values = values
82 | )
83 | )
84 | }
85 |
86 | copy_notebook_supporting_files <- function(rmd, ...) {
87 | rmarkdown::render_site(rmd, encoding = "UTF-8")
88 | }
89 |
--------------------------------------------------------------------------------
/R/funs_plots.R:
--------------------------------------------------------------------------------
1 | library(patchwork)
2 | library(scales)
3 |
4 | plot_income <- function(grey = FALSE) {
5 | if (grey) {
6 | color_pairs <- c("grey30", "grey80")
7 | } else {
8 | color_pairs <- clrs_ngo_pairs[[1]]
9 | }
10 |
11 | plot_income_issue <- sim_final %>%
12 | group_by(org_issue, persona_income) %>%
13 | summarize(avg_share = mean(share)) %>%
14 | mutate(facet = "Issue area") %>%
15 | ggplot(aes(y = fct_rev(str_wrap_factor(org_issue, 15)),
16 | x = avg_share, color = fct_rev(persona_income))) +
17 | geom_pointrange(size = 0.75, fatten = 1.5,
18 | aes(xmin = 0, xmax = ..x..), position = position_dodge(width = 0.5)) +
19 | scale_x_continuous(labels = percent_format(accuracy = 1), expand = expansion(add = c(0, 0.002)),
20 | breaks = seq(0, 0.06, 0.02)) +
21 | scale_color_manual(values = color_pairs, guide = FALSE) +
22 | coord_cartesian(xlim = c(0, 0.06)) +
23 | labs(x = "Average donation share", y = NULL, color = NULL) +
24 | facet_wrap(vars(facet)) +
25 | theme_ngo() +
26 | theme(panel.grid.major.y = element_blank())
27 |
28 | plot_income_funding <- sim_final %>%
29 | group_by(org_funding, persona_income) %>%
30 | summarize(avg_share = mean(share)) %>%
31 | mutate(facet = "Funding sources") %>%
32 | ggplot(aes(y = fct_rev(str_wrap_factor(org_funding, 10)),
33 | x = avg_share, color = fct_rev(persona_income))) +
34 | geom_pointrange(size = 0.75, fatten = 1.5,
35 | aes(xmin = 0, xmax = ..x..), position = position_dodge(width = 0.5)) +
36 | scale_x_continuous(labels = percent_format(accuracy = 1), expand = expansion(add = c(0, 0.002))) +
37 | coord_cartesian(xlim = c(0, 0.06)) +
38 | scale_color_manual(values = color_pairs, guide = FALSE) +
39 | labs(x = "Average donation share", y = NULL, color = NULL) +
40 | facet_wrap(vars(facet)) +
41 | theme_ngo() +
42 | theme(panel.grid.major.y = element_blank())
43 |
44 | plot_income_relationship <- sim_final %>%
45 | mutate(persona_income = fct_recode(persona_income,
46 | "< $61,372/year" = "Lower income",
47 | "> $61,372/year" = "Higher income")) %>%
48 | group_by(org_relationship, persona_income) %>%
49 | summarize(avg_share = mean(share)) %>%
50 | mutate(facet = "Relationship with host government") %>%
51 | ggplot(aes(y = fct_rev(str_wrap_factor(org_relationship, 10)),
52 | x = avg_share, color = fct_rev(persona_income))) +
53 | geom_pointrange(size = 0.75, fatten = 1.5,
54 | aes(xmin = 0, xmax = ..x..), position = position_dodge(width = 0.5)) +
55 | scale_x_continuous(labels = percent_format(accuracy = 1), expand = expansion(add = c(0, 0.003))) +
56 | coord_cartesian(xlim = c(0, 0.1)) +
57 | scale_color_manual(values = color_pairs,
58 | guide = guide_legend(reverse = TRUE, nrow = 1,
59 | override.aes = list(size = 0.25,
60 | linetype = 0))) +
61 | labs(x = "Average donation share", y = NULL, color = NULL) +
62 | facet_wrap(vars(str_wrap(facet, 50))) +
63 | theme_ngo() +
64 | theme(panel.grid.major.y = element_blank(),
65 | legend.key.width = unit(0.5, "lines"))
66 |
67 | plot_income_relationship_extreme <- sim_excel_final %>%
68 | mutate(persona_income = fct_recode(persona_income,
69 | "$50,000/year" = "Lower income",
70 | "$100,000/year" = "Higher income"),
71 | org_relationship = fct_recode(org_relationship,
72 | "Under crackdown" = "Crackdown")) %>%
73 | group_by(org_relationship, persona_income) %>%
74 | summarize(avg_share = mean(share)) %>%
75 | mutate(facet = "Relationship with host government") %>%
76 | ggplot(aes(y = fct_rev(str_wrap_factor(org_relationship, 10)),
77 | x = avg_share, color = fct_rev(persona_income))) +
78 | geom_pointrange(size = 0.75, fatten = 1.5, linetype = "21", pch = 2,
79 | aes(xmin = 0, xmax = ..x..), position = position_dodge(width = 0.5)) +
80 | scale_x_continuous(labels = percent_format(accuracy = 1), expand = expansion(add = c(0, 0.002))) +
81 | coord_cartesian(xlim = c(0, 0.1)) +
82 | scale_color_manual(values = color_pairs,
83 | guide = guide_legend(reverse = TRUE, nrow = 1,
84 | override.aes = list(size = 0.25,
85 | linetype = 0))) +
86 | labs(x = "Average donation share", y = NULL, color = NULL) +
87 | facet_wrap(vars(str_wrap(facet, 50))) +
88 | theme_ngo() +
89 | theme(panel.grid.major.y = element_blank(),
90 | legend.key.width = unit(0.5, "lines"))
91 |
92 | plot_income <- ((plot_income_issue + labs(x = NULL)) +
93 | (plot_income_funding + labs(x = NULL))) /
94 | (plot_income_relationship + plot_income_relationship_extreme)
95 |
96 | plot_income
97 | }
98 |
--------------------------------------------------------------------------------
/R/funs_survey.R:
--------------------------------------------------------------------------------
1 | create_sample_summary <- function(survey_results) {
2 | vars_to_summarize <- tribble(
3 | ~category, ~variable, ~clean_name,
4 | "Demographics", "Q5.12", "Gender",
5 | "Demographics", "Q5.17", "Age",
6 | "Demographics", "Q5.13", "Marital status",
7 | "Demographics", "Q5.14", "Education",
8 | "Demographics", "Q5.15", "Income",
9 | "Attitudes toward charity", "Q2.5", "Frequency of donating to charity",
10 | "Attitudes toward charity", "Q2.6", "Amount of donations to charity last year",
11 | "Attitudes toward charity", "Q2.7_f", "Importance of trusting charities",
12 | "Attitudes toward charity", "Q2.8_f", "Level of trust in charities",
13 | "Attitudes toward charity", "Q2.10", "Frequency of volunteering",
14 | "Politics, ideology, and religion", "Q2.1", "Frequency of following national news",
15 | "Politics, ideology, and religion", "Q5.7", "Traveled to a developing country",
16 | "Politics, ideology, and religion", "Q5.1", "Voted in last election",
17 | "Politics, ideology, and religion", "Q5.6_f", "Trust in political institutions and the state",
18 | "Politics, ideology, and religion", "Q5.2_f", "Political ideology",
19 | "Politics, ideology, and religion", "Q5.4", "Involvement in activist causes",
20 | "Politics, ideology, and religion", "Q5.8", "Frequency of attending religious services",
21 | "Politics, ideology, and religion", "Q5.9", "Importance of religion"
22 | )
23 |
24 | summarize_factor <- function(x) {
25 | output <- table(x) %>%
26 | as_tibble() %>%
27 | magrittr::set_colnames(., c("level", "count")) %>%
28 | mutate(level = factor(level, levels = levels(x))) %>%
29 | mutate(prop = count / sum(count),
30 | nice_prop = scales::percent(prop))
31 |
32 | return(list(output))
33 | }
34 |
35 | participant_summary <- survey_results %>%
36 | select(one_of(vars_to_summarize$variable)) %>%
37 | summarize(across(everything(), summarize_factor)) %>%
38 | pivot_longer(cols = everything(), names_to = "variable", values_to = "details") %>%
39 | left_join(vars_to_summarize, by = "variable") %>%
40 | unnest(details) %>%
41 | mutate(level = as.character(level)) %>%
42 | mutate(level = case_when(
43 | variable == "Q2.7_f" & level == "1" ~ "1 (not important)",
44 | variable == "Q2.7_f" & level == "7" ~ "7 (important)",
45 | variable == "Q2.8_f" & level == "1" ~ "1 (no trust)",
46 | variable == "Q2.8_f" & level == "7" ~ "7 (complete trust)",
47 | variable == "Q5.6_f" & level == "1" ~ "1 (no trust)",
48 | variable == "Q5.6_f" & level == "7" ~ "7 (complete trust)",
49 | variable == "Q5.2_f" & level == "1" ~ "1 (extremely liberal)",
50 | variable == "Q5.2_f" & level == "7" ~ "7 (extremely conservative)",
51 | variable == "Q5.15" & level == "Less than median" ~ "Less than 2017 national median ($61,372)",
52 | variable == "Q5.17" & level == "Less than median" ~ "Less than 2017 national median (36)",
53 | TRUE ~ level
54 | )) %>%
55 | mutate(category = fct_inorder(category, ordered = TRUE))
56 |
57 | return(participant_summary)
58 | }
59 |
--------------------------------------------------------------------------------
/R/graphics.R:
--------------------------------------------------------------------------------
1 | # ggplot themes -----------------------------------------------------------
2 |
3 | #' theme_ngo
4 | #'
5 | #' A custom ggplot2 theme used throughout this project
6 | #'
7 | #' @param base_size base font size (default is 11)
8 | #' @param base_family base font family (default is IBM Plex Sans Condensed)
9 | #'
10 | #' @export
11 | #'
12 | #' @examples
13 | #' library(ggplot2)
14 | #'
15 | #' ggplot(mtcars, aes(x = mpg, y = wt)) +
16 | #' geom_point() +
17 | #' theme_ngo()
18 | theme_ngo <- function(base_size = 9, base_family = "IBM Plex Sans Condensed") {
19 | ret <- theme_bw(base_size, base_family) +
20 | theme(plot.title = element_text(size = rel(1.4), face = "bold",
21 | family = "IBM Plex Sans Condensed"),
22 | plot.subtitle = element_text(size = rel(1), face = "plain",
23 | family = "IBM Plex Sans Condensed Light"),
24 | plot.caption = element_text(size = rel(0.8), color = "grey50", face = "plain",
25 | family = "IBM Plex Sans Condensed Light",
26 | margin = margin(t = 10)),
27 | panel.border = element_rect(color = "grey50", fill = NA, size = 0.15),
28 | panel.spacing = unit(1, "lines"),
29 | panel.grid.minor = element_blank(),
30 | strip.text = element_text(size = rel(0.9), hjust = 0,
31 | family = "IBM Plex Sans Condensed", face = "bold"),
32 | strip.background = element_rect(fill = "#ffffff", colour = NA),
33 | axis.ticks = element_blank(),
34 | axis.title = element_text(family = "IBM Plex Sans Condensed SemiBold", face = "plain",
35 | size = rel(0.8)),
36 | axis.title.x = element_text(margin = margin(t = 5)),
37 | axis.text = element_text(family = "IBM Plex Sans Condensed Light", face = "plain"),
38 | legend.key = element_blank(),
39 | legend.text = element_text(size = rel(0.75),
40 | family = "IBM Plex Sans Condensed Light", face = "plain"),
41 | legend.spacing = unit(0.1, "lines"),
42 | legend.box.margin = margin(t = -0.5, unit = "lines"),
43 | legend.margin = margin(t = 0),
44 | legend.position = "bottom")
45 |
46 | ret
47 | }
48 |
49 |
50 | # Colors ------------------------------------------------------------------
51 |
52 | # colors_plasma <- viridisLite::plasma(6, begin = 0, end = 0.85)
53 | # colors_plasma %>% scales::show_col()
54 | #
55 | # colors_viridis <- viridisLite::viridis(6, begin = 0, end = 0.97)
56 | # colors_viridis %>% scales::show_col()
57 |
58 | # colors_viridis <- viridisLite::viridis(4, option = "viridis")
59 |
60 | # Okabe and Ito (2008) colorblind-safe qualitative palette: https://jfly.uni-koeln.de/color/
61 | # See also https://clauswilke.com/dataviz/color-pitfalls.html
62 | clrs_okabe_ito <- list(orange = "#E69F00",
63 | sky_blue = colorspace::lighten("#56B4E9", 0.25),
64 | bluish_green = "#009E73",
65 | yellow = "#F0E442",
66 | blue = colorspace::lighten("#0072B2", 0.25),
67 | vermilion = "#D55E00",
68 | reddish_purple = "#CC79A7",
69 | black = "#000000")
70 |
71 | clrs_ngo_pairs <- list(c(clrs_okabe_ito[[1]], clrs_okabe_ito[[2]]),
72 | c(clrs_okabe_ito[[3]], clrs_okabe_ito[[4]]),
73 | c(clrs_okabe_ito[[5]], clrs_okabe_ito[[6]]),
74 | c(clrs_okabe_ito[[7]], clrs_okabe_ito[[8]]))
75 |
76 |
77 | # Helper functions --------------------------------------------------------
78 |
79 | # Wrap factor levels
80 | # via Hadley: https://github.com/tidyverse/stringr/issues/107#issuecomment-233723948
81 | str_wrap_factor <- function(x, ...) {
82 | levels(x) <- str_wrap(levels(x), ...)
83 | x
84 | }
85 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: github_document
3 | ---
4 |
5 |
6 |
7 | ```{r, echo = FALSE}
8 | knitr::opts_chunk$set(
9 | collapse = TRUE,
10 | comment = "#>",
11 | fig.path = "README-"
12 | )
13 |
14 | Title <- "Who Cares About Crackdowns? Exploring the Role of Trust in Individual Philanthropy"
15 | Authors <- "Suparna Chaudhry, Marc Dotson, and Andrew Heiss"
16 | Year <- "2021"
17 | ```
18 |
19 | # `r Title`
20 |
21 | [Suparna Chaudhry](http://www.suparnachaudhry.com/) • Department of International Affairs • Lewis & Clark College
22 | [Marc Dotson](https://marriottschool.byu.edu/directory/details?id=50683) • Marriott School of Business • Brigham Young University
23 | [Andrew Heiss](https://www.andrewheiss.com/) • Andrew Young School of Policy Studies • Georgia State University
24 |
25 | ---
26 |
27 | [](https://doi.org/10.1111/1758-5899.12984) [](https://doi.org/10.17605/OSF.IO/SM5EW) [](https://doi.org/10.5281/zenodo.4064840)
28 |
29 | > `r Authors`. `r Year`. ["`r Title`,"](https://doi.org/10.17605/OSF.IO/SM5EW) *Global Policy* (forthcoming), doi: [`10.1111/1758-5899.12984`](https://doi.org/10.1111/1758-5899.12984)
30 |
31 | **All this project's materials are free and open:**
32 |
33 | - [Download the data](#data)
34 | - [See the analysis notebook website](https://stats.andrewheiss.com/who-cares-about-crackdowns/)
35 |
36 |   
37 |
38 | ---
39 |
40 | ## Abstract
41 |
42 | The phenomenon of closing civic space has adversely impacted INGO funding. We argue that individual private donors can be important in sustaining the operations of INGOs working in repressive contexts. Individual donors do not use the same performance-based metrics as official aid donors. Rather, trust can be an important component of individual donor support for nonprofits working towards difficult goals. How does trust in charitable organizations influence individuals' preferences to donate, especially when these groups face crackdown? Using a simulated market for philanthropic donations based on data from a nationally representative sample of individuals in the United States who regularly donate to charity, we find that trust in INGOs matters substantially in shaping donor preferences. Donor profiles with high levels of social trust are likely to donate to INGOs with friendly relationships with host governments. This support holds steady if INGOs face criticism or crackdown. In contrast, donor profiles with lower levels of social trust prefer to donate to organizations that do not face criticism or crackdown abroad. The global crackdown on NGOs may thus possibly sour NGOs' least trusting individual donors. Our findings have practical implications for INGOs raising funds from individuals amid closing civic space.
43 |
44 | ---
45 |
46 | This repository contains the data and code for our paper. Our preprint is online here:
47 |
48 | > `r Authors`. `r Year`. "`r Title`". Online at
49 |
50 | The paper is published at *Global Policy*:
51 |
52 | > `r Authors`. `r Year`. ["`r Title`,"](https://doi.org/10.17605/OSF.IO/SM5EW) *Global Policy* (forthcoming), doi: [`10.1111/1758-5899.12984`](https://doi.org/10.1111/1758-5899.12984)
53 |
54 | ## How to download and replicate
55 |
56 | You can either [download the compendium as a ZIP file](/archive/master.zip) or use GitHub to clone or fork the compendium repository (see the green "Clone or download" button at the top of the GitHub page).
57 |
58 | We use the [**renv** package](https://rstudio.github.io/renv/articles/renv.html) to create a stable version-specific library of packages, and we use the [**targets** package](https://docs.ropensci.org/targets/) to manage all file dependencies and run the analysis. To reproduce the findings and re-run the analysis, do the following:
59 |
60 | 1. Download and install these fonts:
61 | - [IBM Plex Sans](https://fonts.google.com/specimen/IBM+Plex+Sans)
62 | - [IBM Plex Sans Condensed](https://fonts.google.com/specimen/IBM+Plex+Sans+Condensed)
63 | - [Lora](https://fonts.google.com/specimen/Lora)
64 | - [Linux Libertine O](https://www.cufonfonts.com/font/linux-libertine-o) (also [here](https://sourceforge.net/projects/linuxlibertine/))
65 | - [Libertinus Math](https://github.com/alerque/libertinus)
66 | - [Source Sans Pro](https://fonts.google.com/specimen/Source+Sans+Pro)
67 | - [InconsolataGo](https://github.com/ryanoasis/nerd-fonts/tree/master/patched-fonts/InconsolataGo)
68 | 2. [Install R](https://cloud.r-project.org/) (and preferably [RStudio](https://www.rstudio.com/products/rstudio/download/#download)).
69 | - If you're using macOS, [install XQuartz too](https://www.xquartz.org/), so that you have access to the Cairo graphics library
70 | 3. Open `whocares.Rproj` to open an [RStudio Project](https://r4ds.had.co.nz/workflow-projects.html).
71 | 4. Make sure you have a working installation of LaTeX:
72 | - *Easy-and-recommended way*: Install the [**tinytex** package](https://yihui.org/tinytex/) by running `install.packages("tinytex")` in the R console, then running `tinytex::install_tinytex()`
73 | - *Easy-but-requires-huge-4+-GB-download way*: Download TeX Live ([macOS](http://www.tug.org/mactex/); [Windows](https://miktex.org/))
74 | 5. If it's not installed already, R *should* try to install the **renv** package when you open the RStudio Project for the first time. If you don't see a message about package installation, install it yourself by running `install.packages("renv")` in the R console.
75 | 6. Run `renv::restore()` in the R console to install all the required packages for this project.
76 | 7. Run `targets::tar_make()` in the R console to automatically download all data files, process the data, run the analysis, and compile the paper and appendix.
77 |
78 | Running `targets::tar_make()` will create several helpful outputs:
79 |
80 | 1. All project data in `data/`
81 | 2. An analysis notebook website in `analysis/_site/index.html`
82 | 3. PDF, HTML, and Word versions of the manuscript in `manuscript/output/`
83 |
84 |
85 | ## Data
86 |
87 | This project includes the following data files:
88 |
89 | - [**`data/raw_data/final_data.rds`**](https://osf.io/n2hwm/): Original results from the Qualtrics survey. This is [hosted at OSF](https://osf.io/n2hwm/) because of its size. Running `targets::tar_make(survey_results_file)` will download the `.rds` file from OSF and place it in `data/raw_data`. The [code for cleaning and processing this data is part of a separate project, "Why Donors Donate"](https://github.com/andrewheiss/why-donors-donate).
90 | - [**`data/derived_data/survey_results.csv`**](data/derived_data/survey_results.csv): CSV version of the survey data.
91 | - [**`data/derived_data/survey_results.yaml`**](data/derived_data/survey_results.yaml): [YAML metadata](https://csvy.org/) describing the syntax of the survey data.
92 | - [**`data/raw_data/posterior_draws/public_political_social_charity_demo.rds`**](https://osf.io/msaz8/): Gamma (Γ) coefficients from our multilevel Bayesian model. This is [hosted at OSF](https://osf.io/msaz8/) because of its size. Running `targets::tar_make(gamma_draws_file)` will download the `.rds` file from OSF and place it in `data/raw_data/posterior_draws`. The [code for running this model is part of a separate project, "Why Donors Donate"](https://github.com/andrewheiss/why-donors-donate).
93 | - [**`data/raw_data/Market Simulator Version 01.xlsx`**](data/raw_data/Market Simulator Version 01.xlsx): An interactive Excel version of the market simulator to help demonstrate the intuition behind all the moving parts of the simulation.
94 | - [**`data/raw_data/data-xextT.csv`**](data/raw_data/data-xextT.csv): Per capita charitable donations in the US (2000–2014), downloaded from the [Datawrapper widget "How much money Americans give away"](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291#xextT) at ["Fewer Americans are giving money to charity but total donations are at record levels anyway"](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291).
95 | - [**`data/raw_data/data-FTjUv.csv`**](data/raw_data/data-FTjUv.csv): Aggregate charitable donations in the US (1977–2017), downloaded from the [Datawrapper widget "Four decades of American charitable giving"](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291#FTjUv) at ["Fewer Americans are giving money to charity but total donations are at record levels anyway"](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291).
96 |
97 |
98 | ## Licenses
99 |
100 | **Text and figures:** All prose and images are licensed under Creative Commons ([CC-BY-4.0](http://creativecommons.org/licenses/by/4.0/)).
101 |
102 | **Code:** All code is licensed under the [MIT License](LICENSE.md).
103 |
104 |
105 | ## Contributions
106 |
107 | We welcome contributions from everyone. Before you get started, please see our [contributor guidelines](CONTRIBUTING.md). Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms.
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Who Cares About Crackdowns? Exploring the Role of Trust in Individual Philanthropy
5 |
6 | [Suparna Chaudhry](http://www.suparnachaudhry.com/) • Department of
7 | International Affairs • Lewis & Clark College
8 | [Marc Dotson](https://marriottschool.byu.edu/directory/details?id=50683)
9 | • Marriott School of Business • Brigham Young University
10 | [Andrew Heiss](https://www.andrewheiss.com/) • Andrew Young School of
11 | Policy Studies • Georgia State University
12 |
13 | ------------------------------------------------------------------------
14 |
15 | [](https://doi.org/10.1111/1758-5899.12984)
17 | [](https://doi.org/10.17605/OSF.IO/SM5EW)
19 | [](https://doi.org/10.5281/zenodo.4064840)
20 |
21 | > Suparna Chaudhry, Marc Dotson, and Andrew Heiss. 2021. [“Who Cares
22 | > About Crackdowns? Exploring the Role of Trust in Individual
23 | > Philanthropy,”](https://doi.org/10.17605/OSF.IO/SM5EW) *Global Policy*
24 | > (forthcoming), doi:
25 | > [`10.1111/1758-5899.12984`](https://doi.org/10.1111/1758-5899.12984)
26 |
27 | **All this project’s materials are free and open:**
28 |
29 | - [Download the data](#data)
30 | - [See the analysis notebook
31 | website](https://stats.andrewheiss.com/who-cares-about-crackdowns/)
32 |
33 |   
35 |
36 | ------------------------------------------------------------------------
37 |
38 | ## Abstract
39 |
40 | The phenomenon of closing civic space has adversely impacted INGO
41 | funding. We argue that individual private donors can be important in
42 | sustaining the operations of INGOs working in repressive contexts.
43 | Individual donors do not use the same performance-based metrics as
44 | official aid donors. Rather, trust can be an important component of
45 | individual donor support for nonprofits working towards difficult goals.
46 | How does trust in charitable organizations influence individuals’
47 | preferences to donate, especially when these groups face crackdown?
48 | Using a simulated market for philanthropic donations based on data from
49 | a nationally representative sample of individuals in the United States
50 | who regularly donate to charity, we find that trust in INGOs matters
51 | substantially in shaping donor preferences. Donor profiles with high
52 | levels of social trust are likely to donate to INGOs with friendly
53 | relationships with host governments. This support holds steady if INGOs
54 | face criticism or crackdown. In contrast, donor profiles with lower
55 | levels of social trust prefer to donate to organizations that do not
56 | face criticism or crackdown abroad. The global crackdown on NGOs may
57 | thus possibly sour NGOs’ least trusting individual donors. Our findings
58 | have practical implications for INGOs raising funds from individuals
59 | amid closing civic space.
60 |
61 | ------------------------------------------------------------------------
62 |
63 | This repository contains the data and code for our paper. Our preprint
64 | is online here:
65 |
66 | > Suparna Chaudhry, Marc Dotson, and Andrew Heiss. 2021. “Who Cares
67 | > About Crackdowns? Exploring the Role of Trust in Individual
68 | > Philanthropy”. Online at
69 |
70 | The paper is published at *Global Policy*:
71 |
72 | > Suparna Chaudhry, Marc Dotson, and Andrew Heiss. 2021. [“Who Cares
73 | > About Crackdowns? Exploring the Role of Trust in Individual
74 | > Philanthropy,”](https://doi.org/10.17605/OSF.IO/SM5EW) *Global Policy*
75 | > (forthcoming), doi:
76 | > [`10.1111/1758-5899.12984`](https://doi.org/10.1111/1758-5899.12984)
77 |
78 | ## How to download and replicate
79 |
80 | You can either [download the compendium as a ZIP
81 | file](/archive/master.zip) or use GitHub to clone or fork the compendium
82 | repository (see the green “Clone or download” button at the top of the
83 | GitHub page).
84 |
85 | We use the [**renv**
86 | package](https://rstudio.github.io/renv/articles/renv.html) to create a
87 | stable version-specific library of packages, and we use the [**targets**
88 | package](https://docs.ropensci.org/targets/) to manage all file
89 | dependencies and run the analysis. To reproduce the findings and re-run
90 | the analysis, do the following:
91 |
92 | 1. Download and install these fonts:
93 | - [IBM Plex Sans](https://fonts.google.com/specimen/IBM+Plex+Sans)
94 | - [IBM Plex Sans
95 | Condensed](https://fonts.google.com/specimen/IBM+Plex+Sans+Condensed)
96 | - [Lora](https://fonts.google.com/specimen/Lora)
97 | - [Linux Libertine
98 | O](https://www.cufonfonts.com/font/linux-libertine-o) (also
99 | [here](https://sourceforge.net/projects/linuxlibertine/))
100 | - [Libertinus Math](https://github.com/alerque/libertinus)
101 | - [Source Sans
102 | Pro](https://fonts.google.com/specimen/Source+Sans+Pro)
103 | - [InconsolataGo](https://github.com/ryanoasis/nerd-fonts/tree/master/patched-fonts/InconsolataGo)
104 | 2. [Install R](https://cloud.r-project.org/) (and preferably
105 | [RStudio](https://www.rstudio.com/products/rstudio/download/#download)).
106 | - If you’re using macOS, [install XQuartz
107 | too](https://www.xquartz.org/), so that you have access to the
108 | Cairo graphics library
109 | 3. Open `whocares.Rproj` to open an [RStudio
110 | Project](https://r4ds.had.co.nz/workflow-projects.html).
111 | 4. Make sure you have a working installation of LaTeX:
112 | - *Easy-and-recommended way*: Install the [**tinytex**
113 | package](https://yihui.org/tinytex/) by running
114 | `install.packages("tinytex")` in the R console, then running
115 | `tinytex::install_tinytex()`
116 | - *Easy-but-requires-huge-4+-GB-download way*: Download TeX Live
117 | ([macOS](http://www.tug.org/mactex/);
118 | [Windows](https://miktex.org/))
119 | 5. If it’s not installed already, R *should* try to install the
120 | **renv** package when you open the RStudio Project for the first
121 | time. If you don’t see a message about package installation, install
122 | it yourself by running `install.packages("renv")` in the R console.
123 | 6. Run `renv::restore()` in the R console to install all the required
124 | packages for this project.
125 | 7. Run `targets::tar_make()` in the R console to automatically download
126 | all data files, process the data, run the analysis, and compile the
127 | paper and appendix.
128 |
129 | Running `targets::tar_make()` will create several helpful outputs:
130 |
131 | 1. All project data in `data/`
132 | 2. An analysis notebook website in `analysis/_site/index.html`
133 | 3. PDF, HTML, and Word versions of the manuscript in
134 | `manuscript/output/`
135 |
136 | ## Data
137 |
138 | This project includes the following data files:
139 |
140 | - [**`data/raw_data/final_data.rds`**](https://osf.io/n2hwm/):
141 | Original results from the Qualtrics survey. This is [hosted at
142 | OSF](https://osf.io/n2hwm/) because of its size. Running
143 | `targets::tar_make(survey_results_file)` will download the `.rds`
144 | file from OSF and place it in `data/raw_data`. The [code for
145 | cleaning and processing this data is part of a separate project,
146 | “Why Donors
147 | Donate”](https://github.com/andrewheiss/why-donors-donate).
148 | - [**`data/derived_data/survey_results.csv`**](data/derived_data/survey_results.csv):
149 | CSV version of the survey data.
150 | - [**`data/derived_data/survey_results.yaml`**](data/derived_data/survey_results.yaml):
151 | [YAML metadata](https://csvy.org/) describing the syntax of the
152 | survey data.
153 | - [**`data/raw_data/posterior_draws/public_political_social_charity_demo.rds`**](https://osf.io/msaz8/):
154 | Gamma (Γ) coefficients from our multilevel Bayesian model. This is
155 | [hosted at OSF](https://osf.io/msaz8/) because of its size. Running
156 | `targets::tar_make(gamma_draws_file)` will download the `.rds` file
157 | from OSF and place it in `data/raw_data/posterior_draws`. The [code
158 | for running this model is part of a separate project, “Why Donors
159 | Donate”](https://github.com/andrewheiss/why-donors-donate).
160 | - [**`data/raw_data/Market Simulator Version 01.xlsx`**](data/raw_data/Market%20Simulator%20Version%2001.xlsx):
161 | An interactive Excel version of the market simulator to help
162 | demonstrate the intuition behind all the moving parts of the
163 | simulation.
164 | - [**`data/raw_data/data-xextT.csv`**](data/raw_data/data-xextT.csv):
165 | Per capita charitable donations in the US (2000–2014), downloaded
166 | from the [Datawrapper widget “How much money Americans give
167 | away”](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291#xextT)
168 | at [“Fewer Americans are giving money to charity but total donations
169 | are at record levels
170 | anyway”](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291).
171 | - [**`data/raw_data/data-FTjUv.csv`**](data/raw_data/data-FTjUv.csv):
172 | Aggregate charitable donations in the US (1977–2017), downloaded
173 | from the [Datawrapper widget “Four decades of American charitable
174 | giving”](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291#FTjUv)
175 | at [“Fewer Americans are giving money to charity but total donations
176 | are at record levels
177 | anyway”](https://theconversation.com/fewer-americans-are-giving-money-to-charity-but-total-donations-are-at-record-levels-anyway-98291).
178 |
179 | ## Licenses
180 |
181 | **Text and figures:** All prose and images are licensed under Creative
182 | Commons ([CC-BY-4.0](http://creativecommons.org/licenses/by/4.0/)).
183 |
184 | **Code:** All code is licensed under the [MIT License](LICENSE.md).
185 |
186 | ## Contributions
187 |
188 | We welcome contributions from everyone. Before you get started, please
189 | see our [contributor guidelines](CONTRIBUTING.md). Please note that this
190 | project is released with a [Contributor Code of Conduct](CONDUCT.md). By
191 | participating in this project you agree to abide by its terms.
192 |
--------------------------------------------------------------------------------
/_packages.R:
--------------------------------------------------------------------------------
1 | # Generated by targets::tar_renv(): do not edit by hand
2 | library(bs4Dash)
3 | library(clustermq)
4 | library(fs)
5 | library(future)
6 | library(gt)
7 | library(here)
8 | library(pingr)
9 | library(rstudioapi)
10 | library(scales)
11 | library(shiny)
12 | library(shinycssloaders)
13 | library(shinyWidgets)
14 | library(tidyverse)
15 | library(visNetwork)
16 | library(withr)
17 |
--------------------------------------------------------------------------------
/_targets.R:
--------------------------------------------------------------------------------
1 | library(targets)
2 | library(tarchetypes)
3 | library(tibble)
4 |
5 | # General variables
6 | csl <- "pandoc/csl/apa.csl"
7 | bibstyle <- "bibstyle-apa"
8 |
9 | options(tidyverse.quiet = TRUE,
10 | dplyr.summarise.inform = FALSE)
11 |
12 | set.seed(1746) # From random.org
13 |
14 | tar_option_set(packages = c("tidyverse", "here", "fs", "scales", "withr"))
15 |
16 | source("R/funs_data-generation.R")
17 | source("R/funs_survey.R")
18 | source("R/funs_notebook.R")
19 | source("R/funs_knitting.R")
20 |
21 | # here::here() returns an absolute path, which then gets stored in tar_meta and
22 | # becomes computer-specific (i.e. /Users/andrew/Research/blah/thing.Rmd).
23 | # There's no way to get a relative path directly out of here::here(), but
24 | # fs::path_rel() works fine with it (see
25 | # https://github.com/r-lib/here/issues/36#issuecomment-530894167)
26 | here_rel <- function(...) {fs::path_rel(here::here(...))}
27 |
28 | # Pipeline
29 | list(
30 | # Define raw data files
31 | tar_target(excel_simulator_file,
32 | here_rel("data", "raw_data", "Market Simulator Version 01.xlsx"),
33 | format = "file"),
34 | tar_target(gamma_draws_file,
35 | get_from_osf(osf_url = "https://osf.io/msaz8/",
36 | out_dir = here_rel("data", "raw_data", "posterior_draws")),
37 | format = "file"),
38 | tar_target(survey_results_file,
39 | get_from_osf(osf_url = "https://osf.io/n2hwm/",
40 | out_dir = here_rel("data", "raw_data")),
41 | format = "file"),
42 | tar_target(giving_aggregate_file,
43 | here_rel("data", "raw_data", "data-FTjUv.csv")),
44 | tar_target(giving_per_capita_file,
45 | here_rel("data", "raw_data", "data-xextT.csv")),
46 |
47 | # Define helper functions
48 | tar_target(plot_funs, here_rel("R", "graphics.R"), format = "file"),
49 |
50 | # Process and clean data
51 | tar_target(sim_excel_final, process_excel_simulation(excel_simulator_file)),
52 | tar_target(gammas, load_gammas(gamma_draws_file)),
53 | tar_target(personas, build_personas()),
54 | tar_target(orgs, build_orgs()),
55 | tar_target(market_shares, create_predicted_market_shares(gammas, personas, orgs)),
56 | tar_target(sim_final, create_simulation(market_shares, personas, orgs)),
57 | tar_target(survey_results, read_rds(survey_results_file)),
58 | tar_target(survey_csvy,
59 | save_csvy(survey_results,
60 | here_rel("data", "derived_data", "survey_results.csv"),
61 | here_rel("data", "derived_data", "survey_results.yaml")),
62 | format = "file"),
63 | tar_target(participant_summary, create_sample_summary(survey_results)),
64 | tar_target(giving_aggregate, {
65 | read_csv(giving_aggregate_file, col_types = cols()) %>%
66 | mutate(total = `Total donations` * 1000000000)
67 | }),
68 | tar_target(giving_per_capita, read_csv(giving_per_capita_file, col_types = cols())),
69 |
70 | # Render the analysis notebook
71 | tar_notebook_pages(),
72 |
73 | # This is only here to trigger a re-build of the R Markdown website's
74 | # supporting files in `_site`, which copies the files in `output` to
75 | # `_site/output`. I unfortunately haven't found a way to make it so that the
76 | # site building occurs independently of `rmarkdown::render()`, so this is the
77 | # workaround: re-knit index.Rmd
78 | tar_target(supporting_files,
79 | copy_notebook_supporting_files(here_rel("analysis", "index.Rmd"),
80 | main_manuscript, html,
81 | appendix, app_html)),
82 |
83 | # tarchetypes::tar_render() automatically detects target dependencies in Rmd
84 | # files and knits them, but there's no easy way to pass a custom rendering
85 | # script like bookdown::html_document2(), so two things happen here:
86 | # 1. Set a file-based target with tar_target_raw() and use tar_knitr_deps()
87 | # to detect the target dependencies in the Rmd file
88 | # 2. Use a bunch of other file-based targets to actually render the document
89 | # through different custom functions
90 | tar_target(bib_file,
91 | here_rel("manuscript", "bibliography.bib"),
92 | format = "file"),
93 |
94 | tar_target_raw("main_manuscript", here_rel("manuscript", "manuscript.Rmd"),
95 | format = "file",
96 | deps = c("bib_file",
97 | tar_knitr_deps(here_rel("manuscript", "manuscript.Rmd")))),
98 | tar_target(html,
99 | render_html(
100 | input = main_manuscript,
101 | output = here_rel("manuscript", "output", "manuscript.html"),
102 | csl = csl,
103 | bib_file,
104 | support_folder = "output/html-support"),
105 | format = "file"),
106 | tar_target(pdf,
107 | render_pdf(
108 | input = main_manuscript,
109 | output = here_rel("manuscript", "output", "manuscript.pdf"),
110 | bibstyle = bibstyle,
111 | bib_file),
112 | format = "file"),
113 | tar_target(ms_pdf,
114 | render_pdf_ms(
115 | input = main_manuscript,
116 | output = here_rel("manuscript", "output", "manuscript-ms.pdf"),
117 | bibstyle = bibstyle,
118 | bib_file),
119 | format = "file"),
120 | tar_target(docx,
121 | render_docx(
122 | input = main_manuscript,
123 | output = here_rel("manuscript", "output", "manuscript.docx"),
124 | csl = csl,
125 | bib_file),
126 | format = "file"),
127 | tar_target(bib,
128 | extract_bib(
129 | input_rmd = main_manuscript,
130 | input_bib = bib_file,
131 | output = here_rel("manuscript", "output", "extracted-citations.bib")),
132 | format = "file"),
133 |
134 | # Appendix stuff
135 | tar_target_raw("appendix", here_rel("manuscript", "appendix.Rmd"),
136 | format = "file",
137 | deps = c("bib_file",
138 | tar_knitr_deps(here_rel("manuscript", "appendix.Rmd")))),
139 | tar_target(app_html,
140 | render_html(
141 | input = appendix,
142 | output = here_rel("manuscript", "output", "appendix.html"),
143 | csl = csl,
144 | bib_file,
145 | support_folder = "output/html-support"),
146 | format = "file"),
147 | tar_target(app_pdf,
148 | render_pdf(
149 | input = appendix,
150 | output = here_rel("manuscript", "output", "appendix.pdf"),
151 | bibstyle = bibstyle,
152 | bib_file),
153 | format = "file"),
154 | tar_target(app_ms_pdf,
155 | render_pdf_ms(
156 | input = appendix,
157 | output = here_rel("manuscript", "output", "appendix-ms.pdf"),
158 | bibstyle = bibstyle,
159 | bib_file),
160 | format = "file"),
161 | tar_target(app_docx,
162 | render_docx(
163 | input = appendix,
164 | output = here_rel("manuscript", "output", "appendix.docx"),
165 | csl = csl,
166 | bib_file),
167 | format = "file"),
168 |
169 | # Always show a word count
170 | tar_target(word_count, count_words(html)),
171 | tar_force(show_word_count, print(word_count), TRUE),
172 |
173 | # Knit the README
174 | tar_render(readme, here_rel("README.Rmd"))
175 | )
176 |
--------------------------------------------------------------------------------
/analysis/01_clean-generate-data.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Generate and clean simulated data"
3 | author: "Suparna Chaudhry, Marc Dotson, and Andrew Heiss"
4 | date: "Last run: `r format(Sys.time(), '%F')`"
5 | output:
6 | html_document:
7 | code_folding: hide
8 | editor_options:
9 | chunk_output_type: console
10 | ---
11 |
12 | # `targets` pipeline
13 |
14 | Here's the general process for building and running this analysis. This is all done with [the magical **`targets`** package](https://docs.ropensci.org/targets/), which orchestrates all the dependencies automatically.
15 |
16 | ```{r show-targets-pipeline, echo=FALSE}
17 | withr::with_dir(here::here(), {
18 | targets::tar_glimpse()
19 | })
20 | ```
21 |
22 | \
23 |
24 |
25 | # Actual code
26 |
27 | All the simulation and data processing is handled with dataset-specific functions that live in `R/funs_data-generation.R`, which **`targets`** then runs as needed. For the sake of transparency, here's that code:
28 |
29 | ```{r, code=xfun::read_utf8(here::here("R", "funs_data-generation.R")), eval=FALSE, class.source="fold-show"}
30 | ```
31 |
32 | \
33 |
34 |
35 | # Original computing environment
36 |
37 |
38 |
39 |
, but the Bootstrap template still looks for classes named section. */
33 | section h1, section h2, section h3, section h4, section h5, section h6 {
34 | padding-top: 50px;
35 | margin-top: -50px;
36 | }
37 |
38 | /* These overrides are necessary because of the fixed navbar in Bootstrap. The template inserts CSS in the that adds top-margin: -50px and top-padding: 50px to allow anchored links to show up correctly. But doing that also kills the space before the headings, which is dumb. So here I add 21px/10.5px (the original amounts) of top padding for each heading, on top of the 65px for anchor jumping. */
39 | section h1, section h2, section h3 {
40 | padding-top: 71px !important;
41 | }
42 |
43 | section h4, section h5, section h6 {
44 | padding-top: 60.5px !important;
45 | }
46 |
47 | /* Don't add first line indent to TOC entries */
48 | .tocify-header {
49 | text-indent: initial;
50 | }
51 |
--------------------------------------------------------------------------------
/analysis/html/footer.html:
--------------------------------------------------------------------------------
1 |