Skip to content

Repository files navigation

CMAP QDPX Converter

DOI

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.

Quick Start

  1. 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
  2. Clone this repository and enter it:
    git clone https://github.com/Computational-Ethnography-Lab/cmap_qdpx_converter.git
    cd cmap_qdpx_converter
  3. 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 (or py) instead of python3.

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_demo

This 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).

Configuration

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.

Output Format

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.

Reverse conversion (CSV -> QDPX)

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 text required and codes optional, so an uncoded row (text with empty codes) 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 MemberCode are 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 in data_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 every guid/targetGUID attribute (the form a maximal-conformance test file confirmed imports cleanly into ATLAS.ti).
  • The original CSV start_position/end_position values do not come back unchanged: they decide placement (overlap/conflict/fallback) during reconstruction, but the emitted text is joined in CMAP-canonical form (\n\n between 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_group cannot 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 lxml and network access are both available, and skips otherwise.

Document Groups (data_group)

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.

Opening the CSV in Excel or Numbers

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).

Troubleshooting

  • 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 .qdpx archive, not a bare .qde or .docx.

Requirements

  • Python 3.8+ (standard library only — no pip install, no third-party packages). Developed and tested on Python 3.11–3.13. On Windows, use python (or py) in place of python3.

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.

Citation

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

License

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.

Format Specification

QDPX follows the REFI-QDA standard: https://www.qdasoftware.org/refi-qda-codebook/

About

A simple program to convert .qdpx (qualitative research) files into csv format for computational social science analysis, including use with CMAP visualization toolkit.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages