Methods
Every number on this site comes out of a numbered script run against one data file, and each script leaves behind a note recording what it found rather than only what it did. This page sets out how, where the source material comes from, and what still needs a person to check it.
The corpus
UN Security Council Debates (Schoenfeld, Eckhard, Patz, van Meegdenburg & Pires), Harvard Dataverse v6.1, released CC0 into the public domain. 106,302 speeches from 6,582 meeting records, 66,392,703 words, 6 January 1992 to 30 December 2023. A fresh copy of the repository and two scripts rebuild the working table from that DOI; none of the files derived from it are stored in the repository.
How every number was made
The scripts are numbered, each reads the output of the one before it, and each can be re-run from scratch without changing the result. A step that cannot prove its own output is correct stops with an error rather than leaving a plausible-looking file behind. Verified below means the step's own checks pass automatically every time the code changes. It does not mean a person has read the result.
| Step | What it establishes | File it leaves | State |
|---|---|---|---|
01_build_parquet.py | Joins the three published files into one table and checks it. Row counts, word totals, join completeness and date parsing are all checked against the corpus codebook. Two faults in the published files that the codebook does not mention — a character-encoding trap and 36 rows split in two by a stray line break — are repaired here. | speeches.parquet | Verified |
02_normalise.py | Settles on one spelling per speaker, works out who held a Council seat in each year, finds the form of address that opens each speech, and recovers the language it was delivered in. Refuses to run if a speaker is missing from the name table, or if a Council year does not hold exactly five permanent and ten elected seats. | speeches_normalised | Verified |
03_lexicon.py | Counts every word on the list in the body of every speech. Each search pattern has to match the examples declared alongside it, and a fast plain-text filter runs before the exact count. A sample of 200 rows is drawn for hand-checking, spread across occurrences, speeches, individual words and periods. A looser pattern that tolerates scanning errors is reported separately; it adds one speech in 106,302. | speeches_flagged | 0 / 200 audited |
04_series.py | Works out rates by year and by quarter, breaks them down by speaker and by debate, tests for changes in the rate, and attaches the reference dates. The share of speeches is modelled as a series of coin flips; the count of occurrences is modelled against the number of words spoken. The test for a change in rate is repeated in full on simulated data where no change exists, and the threshold is tightened to account for three tests being run. A second, exploratory change-point method is kept visible but is never a result. | series/*.json | Verified |
05_lexical.py | Finds the words that sit near each term, the words that mark out a speech using the term against a comparable speech that does not, and a map of which terms share a speech. Each speech using the term is paired with one from the same year, debate and speaker group. Twenty consecutive random draws show how much the pairing itself moves the answer, and the unpaired comparison is kept only for contrast. Overlapping context windows are merged, and a phrase is never counted as evidence of association with a word already inside it. | lexical/*.json | Verified |
06_embed.py | Turns every speech into a list of numbers a machine can compare. Runs on a university cluster rather than in the fixed environment the rest of the pipeline uses, and nothing on this site reads its output. | embeddings | Built, not adopted |
07_topics.py | Compares two ways of grouping speeches by theme — evidence towards a decision rather than a result. Waits on a research question that the neighbouring-word and agenda evidence cannot already answer, and on a person judging whether the groups mean anything. | topics/*.json | Built, not adopted |
08_kwic.py | Builds the concordance: each occurrence with 150 characters either side, plus the sentence around it. Writes nothing at all if a word's line count disagrees with the count of occurrences from step 03. All 22 words reproduce exactly. | kwic/*.json | Verified |
09_export_speeches.py | One file per meeting record, holding the speech text and the position of every match in it. Both the number of speeches and the number of match positions are checked against the source table before anything is written. | speeches/*.json | Verified |
10_lemmatise.py | Groups inflected forms together, so that crime and crimes stop occupying two rows of one table. Would move figures already published on this site, so it waits on the hand-check of the word list described below. | lemmas.parquet | Built, not adopted |
Choices that shape what you see
Rates, not counts
The Council held more than seven times as many speeches in 2023 as in 1992. A raw count plotted over those years is therefore mostly a picture of that growth, which is why every series on this site opens on a rate and offers the raw count as an alternative rather than the other way round.
The word list is a proposal, not a result
genocid* matches
3.08% of speeches. What counts as discussing genocide is a harder question. The six registers the word list is sorted into — the core word, legal, preventive,
commemorative, contentious and accountability language — are one way of grouping this vocabulary,
written down in config/lexicon.yml and open to disagreement. They are a starting point
for the analysis, not something it discovered.
Change points
One test is run per series, and it asks a single question: is this line better described by one steady rate or by two? The share of speeches is modelled as a series of coin flips; the count of occurrences is modelled against the number of words spoken, so a year in which the Council said more is expected to contain more of everything. The whole search is then repeated on 2,000 simulated series in which the rate never changes, which is what turns a split into a p-value. A result counts only below 1.67%, a threshold already tightened to allow for several tests being run at once (Bonferroni across 3 planned rate tests).
The test allows for how many speeches each year held and repeats its whole search under a no-change model, but finding a split does not prove that anything changed abruptly: a series that rises gradually will also produce a best two-rate split somewhere. Each year is treated as independent of the last, the way speeches cluster into meetings is not modelled, and the intervals assume the split fell where the search put it. Read the size of the change alongside the plotted series and the concordance evidence, and do not read the date as a cause.
A second change-point method is also run and kept visible as a diagnostic. This method shuffles the same yearly values into a new order to see how unusual the real ordering is, which detects a step up or down but not a gradual trend: a series that rises smoothly will return a break at its midpoint regardless. Read these candidates against the plotted series rather than in place of it.
What distinguishes a speech, and what it is compared against
Set a speech that uses genocide against the rest of the corpus and the words that come back describe the occasion — the debate, the year, the region — rather than the concept. Each such speech is therefore paired with one that does not use the word but shares its year, agenda item and speaker group. 3,104 of 3,273 speeches found a partner (94.84%). Where no partner existed, the 100 groups concerned are left short rather than filled from elsewhere, which would have pulled the comparison towards whichever debates happened to have spare speeches. Both sides of the comparison therefore rest on the same 3,104 pairs. Because the partner is drawn at random, the whole pairing is repeated across 20 consecutive draws, and the range those draws produced is reported beside each word.
Where one sentence ends and the next begins
The sentence is the unit this site offers for quotation, so sentence boundaries are found by
explicit rules written for this kind of document — Mr., para., No., U.S., S/PV.3453, resolution 955 (1994). and the initials in a name — rather than by a
general-purpose tool. Across 79,569 concordance lines,
1,622 (2.04%) run past 500 characters, which usually
means either that the scan of the original page ran two sentences together or that the rules
missed a boundary. They are kept whole rather than trimmed, and counted here.
Limits and open validation
- The reference dates are context, not causes. Each one links to the official record used to verify it. A date falling near a change in the chart is not evidence that it produced the change.
- Nobody has yet checked the word list by hand. A fixed sample of 200 matches — drawn so that it covers individual occurrences and whole speeches, and spread across words and periods — has been generated and is waiting, and 0 of the 200 currently carry a human verdict. Until they do, no count on this site has a measured error rate, and none can be guessed at: recording an automatic review as a human one is exactly the mistake the check exists to prevent. Changing any search pattern cancels the verdicts for that word and restarts its sample.
- At least two speeches in five are translations. The record states a non-English delivery language for 40.2% of speeches. Where an in-person speech carries no such marker, it is read as English by the convention of the document series; 5,072 speeches delivered by video link stay unknown, because that format carries no marker either way. What is measured throughout is the English verbatim record rather than the room it was written from.
- Speaker attribution is weaker for 4.9% of speeches. These are the speeches
that begin without the usual opening formula —
The President:orMr. Smith (United Kingdom):— and are read as a continuation of the speech before them.
What is not on this site
Three further steps exist in the repository and feed nothing here. Step 06 turns every speech
into a list of numbers a machine can compare, step 07 tries two ways of grouping speeches by
theme, and step 10 groups inflected forms together so that crime and crimes stop occupying two rows of one table. Each needs either a graphics card or software
the fixed environment cannot carry, so they run separately on a university cluster, and the data this
site loads is built without them.
They are held back on purpose rather than left unfinished. Grouping speeches by theme waits on a research question that the neighbouring-word evidence and the agenda labels cannot already answer, and on a person judging whether the groups mean anything. Grouping inflected forms would move figures already published here, so it waits on the hand-check described above. Both are documented in the roadmap, along with the conditions they have to meet first. Nothing on this page or in any chart comes from them.
Reproducing this
The repository holds the data pipeline, the analysis scripts and this
website. A code linter, a fixed set of Python dependencies and 847 unit tests — 595
covering the pipeline, 252 covering the code this website calculates with — run on every
change. Those tests cover the hand-edited files in config/ too, so a wrong country name or a mistyped Council term fails automatically rather
than halfway through somebody's run. The site is rebuilt from the Dataverse DOI by an automated workflow
rather than uploaded from a desktop, so what you are reading was produced by the pipeline in this
repository and not by a copy of it that once existed on somebody's laptop.
Licences. The corpus is CC0, released by its depositors. The code is MIT. The tables, figures and generated notes this project produces — including everything drawn on this site — are CC BY 4.0, because the selection, arrangement and calculation are this project's contribution rather than the United Nations'. Speech text quoted from the record stays CC0 in whatever form it reaches you. Cite this site using CITATION.cff, and cite the corpus as well.
The files behind this page were generated by word-list version 2, 2026-08-13T07:24:55Z.