Convert QDPX files from qualitative data analysis (QDA) software into CMAP schema CSV, ready for the CMAP Visualization Toolkit. This is pilot research software, not a production product.
QDPX is the REFI-QDA standard exchange format supported by ATLAS.ti, NVivo, MAXQDA, and other QDA applications.
- Export a REFI-QDA project from your QDA software:
- ATLAS.ti:
Project>Export>REFI-QDA Project (.qdpx) - NVivo:
File>Export>Export Project> REFI-QDA format - MAXQDA:
Project>Export>REFI-QDA Project
- ATLAS.ti:
- Clone this repository and enter it:
git clone https://github.com/Computational-Ethnography-Lab/cmap_qdpx_converter.git cd cmap_qdpx_converter - Run the converter with a real project name (no install needed — Python 3.8+; developed and tested on 3.11–3.13):
python3 qda_converter.py /path/to/your_file.qdpx -p my_study
Windows: use
python(orpy) instead ofpython3.
The output your_file_converted.csv is written beside the input. Keep data local: for non-public transcripts, run on your own machine rather than uploading to a shared or cloud environment.
Worked example (bundled synthetic file, safe to share):
python3 qda_converter.py "synth_data_joy&age.qdpx" -p synth_demoThis yields 288 coded segments across 32 sources, with data_group populated from the file's document sets.
Next step: load the resulting CSV directly into the CMAP Visualization Toolkit — no transformation needed (see Output Format).
Edit the constants at the top of qda_converter.py:
| Setting | Purpose |
|---|---|
DEFAULT_PROJECT_NAME |
Placeholder name; passing it (or omitting -p) prints a warning. |
EXCLUDE_SETS |
Document sets to omit from data_group (default: none). Add admin/catch-all sets, e.g. ["z_all"]. Names match exactly and case-sensitively. |
CSV_ENCODING_BOM |
Write a UTF-8 BOM for Excel. Default False — a BOM breaks the toolkit. Also available as --excel-friendly. |
Command-line flags: -p/--project, -o/--output, --keep-temp (retain the extracted folder), --excel-friendly, --version.
Planned (future release): a fuller CLI exposing --exclude-set (repeatable) and related knobs layered over these constants.
The CSV has 13 columns:
| Column | Description |
|---|---|
project |
Project identifier (from -p). |
number |
Sequential segment number (float string). |
reference |
1-based segment reference. |
text |
Extracted coded text segment. |
document |
Source document name. |
old_codes |
Reserved ([]; no prior-codes concept in QDPX). |
start_position |
Start character position in the source. |
end_position |
End character position in the source. |
data_group |
Document groups for the source (string-formatted list). |
text_length |
Character count of text. |
word_count |
Word count of text. |
doc_id |
project-prefixed segment id. |
codes |
Applied codes (string-formatted list). |
codes and data_group are Python-literal lists (e.g. ['DATA:: Interviews', 'DEMOG:: Older Adults']), matching the toolkit's expected format.
These 13 columns are the canonical CMAP schema. The output passes strict CMapsSchema validation (when the schema is available) and loads directly into the CMAP Visualization Toolkit and other CMAP programs with no transformation — a bare pd.read_csv (no BOM), groupby('project'), and literal-eval faceting all work as-is.
cmap_to_qdpx.py runs the inverse path: a CMAP-schema CSV back into a valid .qdpx (a project.qde plus one sources/*.txt per source) that re-ingests cleanly into this converter and into REFI-QDA tools — ATLAS.ti, NVivo, MAXQDA. It is a direct data-frame translation of the CSV: every row's text and its codes carry through unchanged. The CSV's own start_position/end_position decide placement (which rows overlap, which conflict, which fall back), but the emitted source text is reconstructed in CMAP-canonical form, not as a literal replay of the original positions: distinct rows/segments are joined by two hard returns (\n\n), a paragraph break within a single row's own text (\n) is preserved verbatim, and overlapping/nested selections continue to share the same underlying text rather than duplicating it. Every selection's final position in the archive is recomputed against that reconstructed text, so the original CSV positions don't come back unchanged — the text, codes, and grouping do. The archive also carries an explicit REFI-QDA schema identifier, bare-uppercase GUIDs, and a Users/creating-user attribution block for cross-tool import conformance. Standard library only, Python 3.8+.
python3 cmap_to_qdpx.py synth_demo.csv -o rebuilt.qdpx-o/--output is optional and defaults to <csv_stem>.qdpx beside the input. Add --force to overwrite an existing target (never the input CSV); add --drop-unknown-columns to drop a column that is neither a CMAP schema column nor a known derived column when it carries a nonempty value (otherwise such a column is rejected); --version prints the version. On completion the tool re-opens the rebuilt archive and prints a validation summary (sources / codes / sets / selections). Forwarding the bundled synth_data_joy&age.qdpx to a CSV (-p synth_demo) and back yields a 288-segment / 32-source rebuild.
The CSV's start_position/end_position decide placement, not the emitted text. The converter uses these positions for each segment to decide where it sits relative to every other row in the same source — every row placed at its original position agrees exactly with every other row that also claims an overlapping position. A row whose original position is missing, negative, inverted, mismatched against its text's length, larger than an allowed size limit, or in genuine conflict with another row's original position falls back to a recomputed placement instead; that segment's text is preserved verbatim either way, and the fallback is counted per source in the run log. The rebuilt source text then joins distinct rows/segments with two hard returns (\n\n) and keeps a paragraph break inside a single row's own text (\n) intact; overlapping/nested selections continue to share the same underlying text, never duplicated. Every selection's final start and end position in the archive is recomputed against that joined text — the original CSV positions don't come back unchanged, and the rebuilt source reads like a real transcript, rows separated by blank lines, rather than text padded out to preserve the old position numbers.
The converter preserves the verbatim text of every segment — coded or not — along with all code names, per-code coding counts, and source-set (data_group) memberships. A forward -> reverse -> forward round-trip preserves the (document, text, codes, data_group) grouping and the 288-row count on the demo. Two selections that share an identical span, text, and codes are both kept as distinct selections.
For import conformance, the rebuilt archive carries an xsi:schemaLocation on the root <Project> pointing at the REFI-QDA v1.0 XSD, every guid/targetGUID attribute in bare uppercase hex, and a <Users><User> attribution entry (with creatingUserGUID/creatingUser and a fixed creationDateTime on Project, every TextSource, and every Coding) — the shape a maximal-conformance REFI-QDA test file confirmed imports cleanly into ATLAS.ti.
Not representable from the CSV:
- Every row in the CSV is included in full: the schema makes
textrequired andcodesoptional, so an uncoded row (text with emptycodes) is rebuilt as source text with no coding, exactly like a coded row — no row is dropped. The only transcript text that can be absent is text that was never captured as a row at all: regions of the original document that lay outside every selection. The forward export writes one row per selection, not for the gaps between selections, so any never-selected text is not in the CSV and cannot be rebuilt. - Code-membership sets and
MemberCodeare not recoverable: the CMAP CSV schema has no column for them (a forward-pass loss). On the demo the original project holds 12 Sets; the forward CSV carries the 7 document-set labels indata_group(the 5 code-sets are a forward-pass loss, having no CSV column), and all 7 reconstruct as document Sets. - No rich text or
.docx; no code colors, descriptions, or hierarchy. - GUIDs are deterministically regenerated (
uuid5), not the originals — emitted as bare uppercase hex on everyguid/targetGUIDattribute (the form a maximal-conformance test file confirmed imports cleanly into ATLAS.ti). - The original CSV
start_position/end_positionvalues do not come back unchanged: they decide placement (overlap/conflict/fallback) during reconstruction, but the emitted text is joined in CMAP-canonical form (\n\nbetween rows/segments) and every selection's final position is recomputed against that joined text. - Two original sources that share the same name and the same
data_groupcannot be told apart from the CSV (there is no per-source GUID column), so their segments group under one(document, data_group)key and merge into a single rebuilt source. Give same-named sources distinct groups if the boundary must be preserved. - The tool loads the entire CSV into memory to re-aggregate it; a very large export may need a machine with adequate RAM.
The forward reader takes project from its own -p/project_name argument at conversion time, not from the rebuilt archive's <Project name> attribute, so a round-trip of the project value requires re-ingesting the rebuilt archive with -p <the CSV's project value> explicitly.
The tool never overwrites the input CSV, and never overwrites an existing target without --force; it runs entirely on your machine, makes no network calls, and repeated runs on the same CSV produce the same output.
Authoritative REFI-QDA XSD validation of the rebuilt archive is not run by the converter itself (the rebuild is validated for well-formedness and re-ingestibility only); the test suite runs it opportunistically against the REFI-QDA v1.0 XSD when
lxmland network access are both available, and skips otherwise.
data_group is built from REFI-QDA document sets — <Set> elements whose members are sources. Code-sets (sets of codes) are ignored. A source in several sets gets a multi-value list.
- ATLAS.ti (verified): document groups export as sets directly.
- NVivo (inferred): user-created Sets and Case Classifications export; folders do not export to REFI-QDA at all, so a folder-only grouping will not appear — create a Set first.
- MAXQDA (inferred): document groups export as sets; document variables map through the attribute path.
The toolkit's facet filter matches data_group values exactly and case-sensitively against the literal set name (for example DATA:: Interviews), so keep set names stable in your QDA project.
The file is clean UTF-8. If curly quotes appear as ‚Äô or similar, the spreadsheet opened it as a legacy encoding — this is a display artifact, not corruption. Import as UTF-8 instead: in Excel, Data > From Text/CSV > choose 65001: Unicode (UTF-8). If you only ever double-click into Excel, run with --excel-friendly (writes a BOM; do not feed that file to the toolkit).
- 0 segments / conversion stops: the file may not be a REFI-QDA export (the root namespace must be
urn:QDA-XML:project:1.0), or it has no coded quotations. The converter fails loudly rather than writing an empty file. - Empty
data_group: the project has no document sets, or all sets are code-sets. In ATLAS.ti, create document groups and re-export. - Wrong file type: pass a
.qdpxarchive, not a bare.qdeor.docx.
- Python 3.8+ (standard library only — no
pip install, no third-party packages). Developed and tested on Python 3.11–3.13. On Windows, usepython(orpy) in place ofpython3.
The converter is dependency-free. The optional strict-schema validation step uses the CMAP Visualization Toolkit's own environment when you run it there, but the converter itself never requires pandas or any external package.
The converter runs entirely on your machine and makes no network calls — safe for sensitive or non-public transcripts. QDPX archives are extracted with path-traversal protection. Report security concerns to the Computational Ethnography Lab via the repository issue tracker.
If you use this software, please cite:
This software:
Abramson, C. M. (2026). CMAP QDPX Converter (Version 1.5.0) [Computer software]. Computational Ethnography Lab. https://github.com/Computational-Ethnography-Lab/cmap_qdpx_converter. https://doi.org/10.5281/zenodo.21940242
Underlying schema methodology:
Abramson, Corey M., Tara Prendergast, Zhuofan Li, and Daniel Dohan. 2026. "Qualitative Research in an Era of Artificial Intelligence: A Pragmatic Approach to Data Analysis, Workflow, and Computation." Annual Review of Sociology 52(1):35–61. https://doi.org/10.1146/annurev-soc-011824-104836
CMAP Visualization Toolkit:
Abramson, C. M., & Nian, Y. (2025). CMAP Visualization Toolkit. https://doi.org/10.5281/zenodo.17162829
Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0). Free for academic and non-commercial use with attribution; commercial use requires prior written permission from the copyright holder. See LICENSE.
QDPX follows the REFI-QDA standard: https://www.qdasoftware.org/refi-qda-codebook/