Getting Started with psreplicate

library(psreplicate)

psreplicate is an R client for the Political Science Replication Index: a searchable, tagged index of replication packages crawled monthly from 34 flagship political science journals’ Harvard Dataverse collections. This vignette walks through the package’s functions in the order you’d typically reach for them.

Don’t know where to start? list_functions()

list_functions()
#> # A tibble: 18 × 2
#>    function_name       description                                              
#>    <chr>               <chr>                                                    
#>  1 search_replications Search/filter by query, journal, method, data_type, and …
#>  2 search_by_title     Search by title text only.                               
#>  3 search_by_abstract  Search by abstract/description text only.                
#>  4 search_by_author    Search by author name only.                              
#>  5 search_by_journal   Get all records from one or more journals.               
#>  6 search_by_method    Get all records tagged with a given method.              
#>  7 search_by_data_type Get all records tagged with a given data type.           
#>  8 search_by_year      Get all records published in given year(s).              
#>  9 dataset_info        Look up a single record by its DOI.                      
#> 10 browse_dataset      Open a dataset's page in your web browser.               
#> 11 list_dataset_files  List a dataset's files before downloading (needs the dat…
#> 12 download_dataset    Download a dataset's files to disk (needs the dataverse …
#> 13 load_dataset_file   Load one file straight into R, no disk (needs the datave…
#> 14 list_journals       List journals covered, with record counts.               
#> 15 list_methods        List method tags in use, with record counts.             
#> 16 list_data_types     List data-type tags in use, with record counts.          
#> 17 list_functions      This list.                                               
#> 18 refresh_index       Force a re-download of the index (otherwise refreshes da…

That’s every exported function in the package, in one table. The rest of this vignette just walks through the same list with worked examples.

Searching everything at once

search_replications() is the broad, do-everything search: free text across title/abstract/authors, plus optional filters for journal, method, data type, and year, all combined with AND.

search_replications(query = "regression discontinuity", journal = "AJPS")
#> # A tibble: 7 × 9
#>   journal_short title          doi   url   authors  year description method_tags
#>   <chr>         <chr>          <chr> <chr> <chr>   <int> <chr>       <list>     
#> 1 AJPS          Replication D… doi:… http… Kosec,…  2022 Could perc… <chr [2]>  
#> 2 AJPS          Replication D… doi:… http… Marsha…  2022 Politician… <chr [1]>  
#> 3 AJPS          Replication D… doi:… http… Gulzar…  2020 Over 40% o… <chr [1]>  
#> 4 AJPS          Replication D… doi:… http… Kogan,…  2020 We employ … <chr [1]>  
#> 5 AJPS          Replication D… doi:… http… Grembi…  2017 In many co… <chr [1]>  
#> 6 AJPS          Replication D… doi:… http… Coppoc…  2015 Field expe… <chr [2]>  
#> 7 AJPS          Replication d… doi:… http… Eggers…  2015 The regres… <chr [1]>  
#> # ℹ 1 more variable: data_type_tags <list>
search_replications(method = "Survey Experiment", year = 2020:2024)
#> # A tibble: 456 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          "Replication… doi:… http… Daur, …  2024 "Mainstrea… <chr [1]>  
#>  2 APSR          "Replication… doi:… http… Eady, …  2024 "Politicia… <chr [1]>  
#>  3 APSR          "Replication… doi:… http… Goldbe…  2024 "Supplemen… <chr [1]>  
#>  4 APSR          "Replication… doi:… http… Wimmer…  2024 "Introduci… <chr [1]>  
#>  5 APSR          "Replication… doi:… http… Rathbu…  2024 "Why are d… <chr [1]>  
#>  6 APSR          "Replication… doi:… http… Alaria…  2024 "Expanding… <chr [1]>  
#>  7 APSR          "Replication… doi:… http… Schiff…  2024 "This is r… <chr [1]>  
#>  8 APSR          "Replication… doi:… http… Lee, S…  2023 "What inte… <chr [1]>  
#>  9 APSR          "Replication… doi:… http… Jimene…  2023 "Local gov… <chr [1]>  
#> 10 APSR          "Replication… doi:… http… Noh, Y…  2023 "Gender qu… <chr [1]>  
#> # ℹ 446 more rows
#> # ℹ 1 more variable: data_type_tags <list>

Searching one field at a time

If you already know which field you’re searching, the single-field functions skip the guesswork of what search_replications()’s query argument actually matches against:

search_by_title("regression discontinuity")   # title only
#> # A tibble: 17 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 AJPS          "Replication… doi:… http… Marsha…  2022 "Politicia… <chr [1]>  
#>  2 AJPS          "Replication… doi:… http… Grembi…  2017 "In many c… <chr [1]>  
#>  3 AJPS          "Replication… doi:… http… Eggers…  2015 "The regre… <chr [1]>  
#>  4 JOP           "Replication… doi:… http… Holbei…  2019 "This cont… <chr [1]>  
#>  5 JOP           "Replication… doi:… http… Cattan…  2016 "Replicati… <chr [1]>  
#>  6 BJPS          "Replication… doi:… http… White,…  2023 "This is t… <chr [1]>  
#>  7 BJPS          "Replication… doi:… http… Abou-C…  2018 "This arti… <chr [1]>  
#>  8 PSRM          "Replication… doi:… http… Turgeo…  2021 "We study … <chr [1]>  
#>  9 PSRM          "Replication… doi:… http… De Mag…  2019 "Regressio… <chr [1]>  
#> 10 CPS           "Replication… doi:… http… Singh,…  2021 "Replicati… <chr [2]>  
#> 11 LSQ           "Replication… doi:… http… Pereir…  2022 "Replicati… <chr [1]>  
#> 12 PAN           "Replication… doi:… http… De Mag…  2024 "Replicati… <chr [1]>  
#> 13 PAN           "Replication… doi:… http… Miller…  2020 "This lett… <chr [1]>  
#> 14 PAN           "Replication… doi:… http… Hartma…  2020 "Regressio… <chr [1]>  
#> 15 PAN           "Replication… doi:… http… Choi, …  2018 "In regres… <chr [1]>  
#> 16 RP            "Replication… doi:… http… Drew S…  2023 "Replicati… <chr [1]>  
#> 17 RP            "Replication… doi:… http… Song, …  2019 "This is t… <chr [1]>  
#> # ℹ 1 more variable: data_type_tags <list>
search_by_abstract("difference-in-differences") # abstract/description only
#> # A tibble: 14 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          "Replication… doi:… http… Hassel…  2024 Recent wor… <chr [1]>  
#>  2 APSR          "Replication… doi:… http… García…  2021 How do cit… <chr [1]>  
#>  3 AJPS          "Replication… doi:… http… Colner…  2024 Does the i… <chr [1]>  
#>  4 AJPS          "Replication… doi:… http… grier,…  2024 We use dou… <chr [1]>  
#>  5 AJPS          "Replication… doi:… http… Glynn,…  2017 We develop… <chr [1]>  
#>  6 JOP           "Replication… doi:… http… Pahont…  2024 How does e… <chr [1]>  
#>  7 BJPS          "Replication… doi:… http… Giani,…  2025 Globalisat… <chr [1]>  
#>  8 PSRM          "Replication… doi:… http… Yoder,…  2020 We use nat… <chr [1]>  
#>  9 PAN           "Replication… doi:… http… Yamauc…  2026 Difference… <chr [1]>  
#> 10 PAN           "Replication… doi:… http… Egami,…  2022 While a di… <chr [1]>  
#> 11 PoP           "Replication… doi:… http… Lueder…  2025 What are t… <chr [1]>  
#> 12 JPP           "Environment… doi:… http… Xu, H.…  2024 Based on m… <chr [1]>  
#> 13 RP            "Replication… doi:… http… Lipkov…  2025 Voter iden… <chr [1]>  
#> 14 RP            "Replication… doi:… http… Garsid…  2022 Can first-… <chr [1]>  
#> # ℹ 1 more variable: data_type_tags <list>
search_by_author("Wantchekon")                  # author names only
#> # A tibble: 3 × 9
#>   journal_short title          doi   url   authors  year description method_tags
#>   <chr>         <chr>          <chr> <chr> <chr>   <int> <chr>       <list>     
#> 1 APSR          Replication D… doi:… http… Wantch…  2026 Data and c… <chr [0]>  
#> 2 AJPS          Replication D… doi:… http… Garcia…  2023 We show th… <chr [2]>  
#> 3 AJPS          Replication D… doi:… http… López-…  2020 In a rando… <chr [1]>  
#> # ℹ 1 more variable: data_type_tags <list>

And the same idea for the structured fields:

search_by_journal(c("AJPS", "APSR"))
#> # A tibble: 1,622 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          Replication … doi:… http… Dohert…  2026 "ABSTRACT:… <chr [0]>  
#>  2 APSR          Voter Regist… doi:… http… Bracon…  2026 "A large-s… <chr [0]>  
#>  3 APSR          Replication … doi:… http… Allie,…  2026 "Liberal d… <chr [1]>  
#>  4 APSR          Replication … doi:… http… Alexan…  2026 "Existing … <chr [0]>  
#>  5 APSR          Replication … doi:… http… Queral…  2026 "These mat… <chr [0]>  
#>  6 APSR          Replication … doi:… http… Kistne…  2026 "Social me… <chr [0]>  
#>  7 APSR          Replication … doi:… http… Prior,…  2026 "Timing of… <chr [0]>  
#>  8 APSR          Replication … doi:… http… McCart…  2026 "Political… <chr [0]>  
#>  9 APSR          Replication … doi:… http… Bolte,…  2026 "How does … <chr [1]>  
#> 10 APSR          Replication … doi:… http… Turnbu…  2026 "Replicati… <chr [0]>  
#> # ℹ 1,612 more rows
#> # ℹ 1 more variable: data_type_tags <list>
search_by_method("Field Experiment")
#> # A tibble: 228 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          Replication … doi:… http… Bolte,…  2026 "How does … <chr [1]>  
#>  2 APSR          Preventing R… doi:… http… Blair,…  2026 "Replicati… <chr [1]>  
#>  3 APSR          Replication … doi:… http… Ahuja,…  2026 "How do el… <chr [1]>  
#>  4 APSR          Replication … doi:… http… Kao, J…  2026 "Replicati… <chr [1]>  
#>  5 APSR          Replication … doi:… http… AMAR, …  2025 "Misinform… <chr [1]>  
#>  6 APSR          Replication … doi:… http… Kruks-…  2025 "This pack… <chr [1]>  
#>  7 APSR          Replication … doi:… http… Martin…  2025 "Understan… <chr [1]>  
#>  8 APSR          Replication … doi:… http… Larson…  2024 "Intervent… <chr [2]>  
#>  9 APSR          Replication … doi:… http… Master…  2023 "Without f… <chr [2]>  
#> 10 APSR          Replication … doi:… http… Bernha…  2023 "This data… <chr [1]>  
#> # ℹ 218 more rows
#> # ℹ 1 more variable: data_type_tags <list>
search_by_data_type("Elections / Voting")
#> # A tibble: 2,355 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          Replication … doi:… http… Dohert…  2026 ABSTRACT: … <chr [0]>  
#>  2 APSR          Voter Regist… doi:… http… Bracon…  2026 A large-sc… <chr [0]>  
#>  3 APSR          Replication … doi:… http… Allie,…  2026 Liberal de… <chr [1]>  
#>  4 APSR          Replication … doi:… http… Alexan…  2026 Existing a… <chr [0]>  
#>  5 APSR          Replication … doi:… http… Prior,…  2026 Timing of … <chr [0]>  
#>  6 APSR          Replication … doi:… http… Wantch…  2026 Data and c… <chr [0]>  
#>  7 APSR          Replication … doi:… http… Clinto…  2026 This inclu… <chr [0]>  
#>  8 APSR          Replication … doi:… http… Horiuc…  2026 In a recen… <chr [0]>  
#>  9 APSR          Replication … doi:… http… Müller…  2026 Ethnicity … <chr [0]>  
#> 10 APSR          Replication … doi:… http… Bowles…  2026 How do can… <chr [0]>  
#> # ℹ 2,345 more rows
#> # ℹ 1 more variable: data_type_tags <list>
search_by_year(2015:2020)
#> # A tibble: 3,887 × 9
#>    journal_short title         doi   url   authors  year description method_tags
#>    <chr>         <chr>         <chr> <chr> <chr>   <int> <chr>       <list>     
#>  1 APSR          "Replication… doi:… http… Fouirn…  2020 "Replicati… <chr [0]>  
#>  2 APSR          "Replication… doi:… http… Lo, Ad…  2020 "Replicati… <chr [1]>  
#>  3 APSR          "Replication… doi:… http… Suryan…  2020 "Replicati… <chr [0]>  
#>  4 APSR          "APSA Princi… doi:… http… APSA C…  2020 "On April … <chr [0]>  
#>  5 APSR          "Replication… doi:… http… Tertyt…  2020 "We post t… <chr [0]>  
#>  6 APSR          "Replication… doi:… http… Walter…  2020 "The repli… <chr [0]>  
#>  7 APSR          "Replication… doi:… http… Moskow…  2020 "Replicati… <chr [0]>  
#>  8 APSR          "Replication… doi:… http… Haas, …  2020 "Does poli… <chr [0]>  
#>  9 APSR          "Replication… doi:… http… Landwe…  2020 "Depressio… <chr [0]>  
#> 10 APSR          "Replication… doi:… http… Zubek,…  2020 "Identifyi… <chr [0]>  
#> # ℹ 3,877 more rows
#> # ℹ 1 more variable: data_type_tags <list>

Finding out what values are even valid

The catch with search_by_method(), search_by_data_type(), and search_by_journal() is that you have to already know a valid tag or journal code to pass in. These three functions list exactly what’s in the data right now, most common first:

list_journals()
#> # A tibble: 34 × 3
#>    journal_short journal_name                          n_records
#>    <chr>         <chr>                                     <int>
#>  1 AJPS          American Journal of Political Science       845
#>  2 APSR          American Political Science Review           777
#>  3 BJPS          British Journal of Political Science        776
#>  4 BPSR          Brazilian Political Science Review           78
#>  5 CPS           Comparative Political Studies               430
#>  6 DADOS         Revista DADOS                                91
#>  7 FPA           Foreign Policy Analysis                     272
#>  8 II            International Interactions                   25
#>  9 IO            International Organization                  199
#> 10 IPSR          Italian Political Science Review            146
#> # ℹ 24 more rows
list_methods()
#> # A tibble: 16 × 2
#>    method                           n_records
#>    <chr>                                <int>
#>  1 Survey Experiment                      912
#>  2 Field Experiment                       228
#>  3 Difference-in-Differences              150
#>  4 Bayesian Methods                       134
#>  5 Regression Discontinuity               120
#>  6 Machine Learning                       113
#>  7 Network Analysis                       113
#>  8 Case Study / Process Tracing            99
#>  9 Instrumental Variable                   88
#> 10 Formal Model / Game Theory              70
#> 11 Survival / Duration Model               52
#> 12 Spatial Analysis                        51
#> 13 Multilevel / Hierarchical Model         43
#> 14 Meta-Analysis                           37
#> 15 Qualitative Comparative Analysis        25
#> 16 Structural Equation Modeling             6
list_data_types()
#> # A tibble: 10 × 2
#>    data_type                           n_records
#>    <chr>                                   <int>
#>  1 Elections / Voting                       2355
#>  2 Elite / Legislator Data                  1361
#>  3 Public Opinion                           1343
#>  4 Experimental Data                        1248
#>  5 Survey Data                               345
#>  6 Cross-National / Country-Level            255
#>  7 Panel Data                                214
#>  8 Text / Corpus                             148
#>  9 Text as Data / NLP                        101
#> 10 Administrative / Government Records        82

Looking up and opening a specific dataset

Once you have a doi from a search result, dataset_info() pulls just that one record, and browse_dataset() does the same but opens the dataset’s page directly in your browser instead of returning a tibble:

hit <- search_by_title("Rebel Victory and Authoritarian")
dataset_info(hit$doi[1])
#> # A tibble: 1 × 9
#>   journal_short title          doi   url   authors  year description method_tags
#>   <chr>         <chr>          <chr> <chr> <chr>   <int> <chr>       <list>     
#> 1 APSR          Replication D… doi:… http… Clarke…  2026 "This prov… <chr [0]>  
#> # ℹ 1 more variable: data_type_tags <list>
browse_dataset(hit$doi[1])

Downloading the actual data files

Everything so far is metadata - title, abstract, authors, tags, a link. list_dataset_files() and download_dataset() get the real files (the .dta/.csv/.R/etc. that make up the replication package), so you don’t have to leave R to go download them by hand. Both require the dataverse package (install.packages("dataverse")), which does the actual work of talking to Harvard Dataverse’s file API - these are thin convenience wrappers around it, scoped to this index’s DOIs.

tryCatch(
  list_dataset_files(hit$doi[1]),
  error = function(e) message("Couldn't reach Dataverse: ", conditionMessage(e))
)
#> # A tibble: 64 × 3
#>    filename                                                filesize content_type
#>    <chr>                                                      <int> <chr>       
#>  1 alesina_frac.tab                                            7996 text/tab-se…
#>  2 allwars_covar.tab                                         105466 text/tab-se…
#>  3 area.do                                                      275 text/x-stat…
#>  4 area.tab                                                    4505 text/tab-se…
#>  5 btscs.ado                                                   6316 application…
#>  6 civilreg.do                                                 1294 text/x-stat…
#>  7 ClarkeMengPaine_Rebel Victory_Appendix A (Online Appen…   231647 application…
#>  8 ClarkeMengPaine_Rebel Victory_Appendix B (Codebook).pdf   620019 application…
#>  9 cmp_allauth.tab                                            60834 text/tab-se…
#> 10 cmp_allauth_tscs.tab                                     1093178 text/tab-se…
#> # ℹ 54 more rows
# downloads every file into a new temporary directory and returns the paths
paths <- download_dataset(hit$doi[1])

# or into a specific directory, and only specific files
download_dataset(hit$doi[1], dest = "replication_data", files = "README.rtf")

If you just want one file’s contents, load_dataset_file() skips the disk entirely: a tabular file (.tab/.dta/.csv/etc.) comes back as a tibble, an .rds file is deserialized with readRDS() as whatever R object it holds, a plain-text file (a script, a README) comes back as lines of text, and anything else (PDFs, images) comes back as raw bytes.

tryCatch(
  load_dataset_file(hit$doi[1], "area.tab"),
  error = function(e) message("Couldn't reach Dataverse: ", conditionMessage(e))
)
#> Downloading ingested version of data with readr::read_tsv. To download the original version and remove this message, set original = TRUE.
#> Rows: 188 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: "\t"
#> chr (1): country
#> dbl (2): ccode, area
#> 
#> ℹ 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.
#> # A tibble: 188 × 3
#>    ccode country                area
#>    <dbl> <chr>                 <dbl>
#>  1     2 United States       9158960
#>  2    20 Canada              9093510
#>  3    31 Bahamas, The          10010
#>  4    40 Cuba                 107400
#>  5    41 Haiti                 27560
#>  6    42 Dominican Republic    48320
#>  7    51 Jamaica               10830
#>  8    52 Trinidad and Tobago    5130
#>  9    53 Barbados                430
#> 10    54 Dominica                750
#> # ℹ 178 more rows

Keeping the index fresh

The index is downloaded once and cached locally (see tools::R_user_dir("psreplicate", "cache")), then auto-refreshed once the cache is more than a day old. Force an update sooner with:

refresh_index(force = TRUE)

Caveats worth knowing

  • Method and data-type tags are generated by keyword matching, not hand-coded. A classifier will miss paraphrased methods and can over-match on common words - use tags to narrow a search, then verify by reading the abstract or the dataset itself.
  • Not every political science journal is indexed. Only journals with a dedicated Harvard Dataverse collection are crawlable; notably, the Journal of Conflict Resolution and Journal of Peace Research are not (see the site’s README for where their replication data actually lives).
  • The index refreshes monthly. list_journals(), list_methods(), and search results all reflect whatever was live the last time your local cache refreshed.