Tool-agnostic inter-coder reliability and disagreement adjudication for qualitative coding.
AgreeKit reads coding from a tidy CSV or from REFI-QDA .qdpx exports, computes
the inter-coder reliability statistics reviewers expect (percent agreement,
Cohen's and Fleiss' kappa, and Krippendorff's alpha, overall and per code), lists
the units where coders disagree so you can adjudicate them, and writes a
publication ready report with a methods paragraph you can paste into a paper.
It is tool agnostic: any coding that can be shaped into a tidy CSV works, and it
reads .qdpx files directly. It runs fully offline with no paid services.
The reliability statistics exist as low level libraries (the maintained
krippendorff package, textbook kappa formulas), but the workflow around them is
tedious glue that nobody packaged: reading arbitrary coding exports, unitizing
highlight style span coding, computing the right coefficients, listing
disagreements, and adjudicating them. CAQDAS tools that include an agreement
report lock you into their own project format. AgreeKit is the standalone, tool
agnostic option. See ../OPPORTUNITIES.md for the evidence.
Core:
- Read coding from a tidy long CSV (unit, coder, code, and optional start/end for
spans) with configurable column names, or from one REFI-QDA
.qdpxper coder. - Unitize span coding into atomic intervals (or per character) so highlight style coding becomes comparable across coders. Unit based coding is used directly.
- Compute overall reliability: percent agreement, Cohen's kappa (two coders), Fleiss' kappa (any number of coders, each unit rated by all), and Krippendorff's alpha (any number of coders, handles missing data).
- Compute per code presence agreement (alpha and percent agreement for each code treated as present or absent), with prevalence.
- List the units where coders disagree, export them to a CSV for adjudication, and reconcile the filled in decisions into a single resolved coding.
- Render a report as HTML, Markdown, or JSON, including a ready to paste methods paragraph with the coefficient values.
Requires Python 3.10 or newer (developed and tested on 3.14).
python -m venv .venv
# Windows PowerShell: .venv\Scripts\Activate.ps1
# Git Bash / Linux / macOS: source .venv/bin/activate
pip install -e .Exact pinned environment:
pip install -r requirements-lock.txt
pip install -e . --no-depsDevelopment tools: pip install -e ".[dev]" then pytest.
Your CSV needs a coder column, a code column, and either a unit column (unit based coding) or start and end columns with a document column (span based coding):
unit,coder,code
r1,alice,Positive
r1,bob,Positive
r2,alice,Negative
r2,bob,Neutralagreekit report coding.csv --md report.md --json report.jsonPrints the methods paragraph and writes the reports. Example paragraph:
2 coders independently coded 6 units (6 rated by all coders). Inter-coder
reliability was assessed as follows: Krippendorff's alpha (nominal) was 0.766;
percent agreement was 83.3%; Cohen's kappa was 0.750; Fleiss' kappa was 0.745.
1 units required adjudication.
Custom column names and span data:
agreekit report spans.csv --coder-col rater --code-col label \
--document-col doc --start-col begin --end-col finish --granularity atomicagreekit report --qdpx alice.qdpx --qdpx bob.qdpx --coders alice,bob --out report.html# 1. Export the units where coders disagree
agreekit disagreements coding.csv --out disagreements.csv
# 2. Open disagreements.csv and fill the empty "final" column with the agreed code
# 3. Reconcile into a single resolved coding
agreekit adjudicate coding.csv --resolved disagreements.csv --out reconciled.csvRun python -m agreekit ... if you prefer not to install the console script.
Column names and unitization settings come from defaults, an optional TOML file
(--config), or CLI flags. AgreeKit reads no environment variables and makes no
network calls. Example TOML:
[agreekit]
coder_col = "rater"
code_col = "label"
granularity = "atomic" # auto, atomic, char, or unitWhen coders highlight character ranges, there is no shared list of units to
compare. AgreeKit builds one: it collects every distinct span boundary in a
document and forms the atomic intervals between them; for each interval, each
coder's code is the span that covers it, or the uncoded category if none does.
This turns overlapping, unaligned highlights into a units by coders matrix that
the standard coefficients can score. Use --granularity char for a finer, more
conservative per character unitization.
pip install -e ".[dev]"
pytestThe suite (33 tests) checks the statistics against known textbook values, builds
its own .qdpx fixtures in memory, and runs fully offline. It covers the stats,
unitization (unit, span, and char), the CSV and .qdpx readers, adjudication,
the report renderers, the CLI, and the security properties below.
models.pysegments, the units matrix, metrics, and the report.config.pycolumn names and unitization settings.readers.pythe tidy CSV reader and the per coder.qdpxreader.qdpx_reader.pya small hardened REFI-QDA span reader.unitize.pybuilding the units matrix from segments.stats.pypercent agreement, Cohen's and Fleiss' kappa, Krippendorff's alpha.adjudicate.pydisagreement export, reconciliation.report.pyHTML, Markdown, and JSON rendering plus the methods paragraph.safe.pyformula injection safe CSV writing.cli.pythe Typer command line interface.
- XXE and entity expansion are blocked.
.qdpxXML is parsed withdefusedxml, which refuses external entities and entity expansion, so a malicious.qdpxcannot read local files or blow up memory. There is a test that an external entity payload is rejected. - Archive limits. A
.qdpxis checked for entry count and uncompressed size before it is read, guarding against zip bombs. - Spreadsheet formula injection is prevented. Code labels and unit text are user controlled, so every cell written to the disagreements and reconciled CSVs is sanitized (dangerous leading characters are quoted). There is a test for this.
- Report output is escaped. The HTML report is rendered with Jinja2 autoescaping, so a code label containing markup cannot inject script into the report. There is a test for this.
- Bounded input and no unsafe execution. Input files over a configurable size
limit are refused. There is no
eval, noexec, nopickle, and noyaml.load. Configuration is read with the standard librarytomllib. No network calls are made.
MIT. See LICENSE.
Reliability coefficients are summaries, not verdicts. Interpret them in the context of your coding scheme and the number of categories, and report the measure you used alongside the value.