Build, document and evaluate text databases for social science research.
coRpusdb is an R package of shared building blocks for research projects that collect texts into a DuckDB database (journal articles, central bank and EU documents, digitised archives), and for the projects that read these databases. It was extracted from functions that the author’s database projects kept rewriting. It also writes into code a few principles of how such databases should be built:
-
Data are taken, not given. Every record says where it comes from (
doc_id = "<source>:<source_id>"), raw records are kept as the source returned them, and every write is logged. - Raw is kept, derived is versioned. Raw tables are only appended to; cleaned and derived outputs carry a stamp of the method and version that produced them, and stale outputs are refused.
- Writes are transactions that replace what they rewrite, so that a failed run changes nothing and a rerun creates no duplicates.
- One writer, many readers. Readers open the database read-only and fall back on a snapshot while a collection runs.
- The schema documents itself, and is checked against the database and the README.
- Uncertain steps are measured: stratified samples, blind judge files, Cohen’s kappa, precision and recall with Wilson intervals.
-
Memory is a constraint, not an afterthought. DuckDB’s memory is capped, large results are streamed, checks and samples run in SQL, and the functions are benchmarked on tables of 10 million texts (
bench/benchmark_memory.R).
These principles draw on the literature on corpora and quantitative sources, in particular Lemercier and Zalc’s Quantitative Methods in the Humanities (2019), Grimmer, Roberts and Stewart’s Text as Data (2022), McEnery and Hardie’s Corpus Linguistics (2012), Salganik’s Bit by Bit (2018) and Guldi’s The Dangerous Art of Text Mining (2023). The article Building a text database as a research practice explains how.
Installation
# install.packages("pak")
pak::pak("git::https://codeberg.org/agoutsmedt/coRpusdb.git")
# or
remotes::install_git("https://codeberg.org/agoutsmedt/coRpusdb.git")What it contains
A short example
library(coRpusdb)
schema <- list(document = "
CREATE TABLE IF NOT EXISTS document (
doc_id VARCHAR, -- <source>:<source_id>
title VARCHAR, -- title as published
year INTEGER -- year of publication
)")
con <- db_connect("speeches.duckdb")
create_schema(con, schema)
replace_documents(
con,
list(document = data.frame(doc_id = make_doc_id("ecb", "sp240101"),
title = "The euro at 25", year = 2024L)),
source = "ecb", script = "collecting_ecb.R"
)
cat(schema_markdown(schema_dictionary(schema))) # data dictionary for the README
db_disconnect(con)
# Downstream projects read without holding the lock:
db_query("speeches.duckdb", "SELECT * FROM document WHERE year = ?", params = list(2024L))Articles
- Building a text database as a research practice: the principles, where they come from, and what the package leaves to the researcher.
- From raw records to a documented database: a worked example, from raw records to a checked, documented and shareable database.
- Large databases: memory and speed: benchmarks of time and peak memory, and the rules that follow.
- Measuring an uncertain step: the two-stage evaluation of a pipeline step, from sample size to precision and recall.
Related packages
coRpusdb stores, documents and evaluates. Collecting texts politely belongs to a companion scraping package (in preparation), and analysing them to mintR (text mining and structural topic models).