Metadata-Version: 2.5
Name: mmsscode
Version: 0.4.0
Summary: Local multi-model semantic-similarity coding of open-ended answers.
Author: mmsscode contributors
License: MIT
License-File: LICENSE
Requires-Python: <3.13,>=3.11
Requires-Dist: huggingface-hub>=0.24
Requires-Dist: numpy<3,>=1.26
Requires-Dist: pandas<3,>=2.2
Requires-Dist: pyreadstat>=1.2
Requires-Dist: safetensors>=0.4
Requires-Dist: sentencepiece>=0.2
Requires-Dist: torch>=2.6
Requires-Dist: transformers<5,>=4.51
Provides-Extra: semantic
Requires-Dist: chonkie[semantic,st]<2,>=1.6; extra == 'semantic'
Provides-Extra: test
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# mmsscode

`mmsscode` is a local, semi-automated tool for coding open-ended survey responses. It compares responses with a human-defined codebook using several embedding models. It requires neither an API key nor generative AI, and it leaves uncertain cases for human review.

The workflow is designed for an auditable social-science process: define a codebook, manually code informative examples, validate the procedure, and apply it to the remaining responses.

The main workflow stays intentionally small. Advanced sampling, conformal
screening, segmentation, and aggregation policies are optional; a basic project
only needs the commands shown below.

## Installation

Install the complete package from PyPI:

```powershell
python -m pip install mmsscode
```

mmsscode supports Python 3.11 and 3.12. The first model download can be large; downloaded models remain available locally for later offline analyses.

For optional local semantic segmentation, install the Chonkie extra:

```powershell
python -m pip install "mmsscode[semantic]"
```

## Quick start in an IDE

Create a small Python script and run each command separately in an IDE such as VS Code or Spyder:

```python
import mmsscode

PROJECT = r"C:\projects\my_analysis"

mmsscode.create(PROJECT)
mmsscode.set_models(
    PROJECT,
    directory=r"D:\mmsscode_models",
    models=["simcse", "gte", "bge_m3"],
)
mmsscode.download_models(PROJECT)

mmsscode.set_data(PROJECT, path=r"C:\data\responses.csv")
mmsscode.prepare(
    PROJECT,
    text_columns={"open_answer_1": "code_1", "open_answer_2": "code_2"},
    id_column="id",
)
```

`prepare()` creates `codebook.csv` and `manual_codes.csv` in the project directory. When the input contains no manual codes, it randomly selects at most 500 source rows. Complete the codebook, then manually enter `manual_code_id` values for as many selected texts as needed to reach theoretical saturation. The remaining rows may stay blank.

Optional `coder_id`, `confidence`, `coding_status`, and
`adjudicated_code_id` fields can be left blank in a simple one-coder project.
An adjudicated code takes precedence over the original manual code. To expand
an existing sample without losing completed annotations, run
`mmsscode.extend_sample(PROJECT, additional_rows=100)`. After an analysis,
`strategy="review"` preferentially adds unresolved source rows.

After manual coding, run the remaining steps independently:

```python
mmsscode.validate(PROJECT)  # optional repeated holdout report
mmsscode.analyze(PROJECT)   # code the full dataset and write audit files
```

Validation keeps all texts belonging to the same `id_column` value together
and tries to retain every sufficiently represented code in both training and
test data. Per-code metrics, a confusion table, and manual-example coverage are
written automatically.

The project settings, sample seed, and selected source rows are stored in `mmsscode-project.json`. Re-running `prepare()` preserves the existing manual-coding template by default.

## Interactive setup

If you prefer prompts instead of writing the initial commands, run:

```python
import mmsscode

mmsscode.setup(r"C:\projects\my_analysis")
```

The wizard stores the same project settings and creates the codebook and manual-coding template. Later, use `download_models`, `validate`, and `analyze` with the same project directory.

## Existing manual codes

For non-segmented responses, existing manual codes can be read directly from the input data:

```python
mmsscode.prepare(
    PROJECT,
    text_columns={"open_answer": "code"},
    id_column="id",
    manual_code_columns={"open_answer": "manual_code"},
)
```

Non-empty `manual_code` values become the training examples. When segmentation is enabled, use `manual_codes.csv` instead, because a single response can produce more than one segment.

## Local semantic segmentation

By default, a response is one coding unit. The legacy `segmentation_enabled=True` option splits text at simple separators and remains available for compatibility. For thematic units, mmsscode can use the optional local [Chonkie](https://docs.chonkie.ai/oss/chunkers/semantic-chunker) semantic chunker. It compares sentence meanings with a local embedding model and groups adjacent, semantically related sentences into coding units.

```python
mmsscode.set_text_processing(
    PROJECT,
    segmentation_method="semantic",
    semantic_model="bge_m3",
    similarity_threshold=0.75,
    max_tokens=256,
)

mmsscode.prepare(
    PROJECT,
    text_columns={"open_answer_1": "code_1"},
    id_column="id",
)

```

The semantic model must be included in `set_models()` and downloaded with `download_models()` first. `bge_m3` is the recommended multilingual choice, including for Czech text. A higher `similarity_threshold` produces smaller, more narrowly focused segments; `max_tokens` prevents overly long segments. This performs semantic segmentation, not semantic-role labeling or automatic interpretation of a respondent's intention.

## Conservative automatic coding

The default `strict` rule accepts a code only when every selected model and both distance metrics choose the same nearest manually coded example. For a stricter option, enable class-conditional conformal prediction:

```python
mmsscode.set_coding(
    PROJECT,
    mode="conformal",
    conformal_alpha=0.10,
    conformal_calibration_fraction=0.50,
    conformal_min_examples=20,
    conformal_seed=42,
)
```

This holds out a reproducible part of the manually coded examples for each code. For every model, mmsscode forms a statistical prediction set of code IDs based on how close the text is to that code's reference examples. It accepts an automatic result only if the existing nearest-example rule agrees **and** every model's prediction set contains exactly that one code. An empty or multi-code set is sent to `review_segments.csv` instead.

`conformal_alpha=0.10` corresponds to 90% class-conditional coverage under the usual exchangeability assumption. It is an error level, not a similarity threshold; do not increase it merely to make results look stricter. The default of 20 examples per code and a 50% calibration split provides enough calibration cases for that value. Leave the default `strict` mode when manual examples are sparse.

For segmented text, resolved codes are exported while the source row is marked
`partial` if another segment needs review. Advanced users can withhold all
row-level codes until every segment is resolved:

```python
mmsscode.set_coding(PROJECT, aggregation_policy="require_complete")
```

## Outputs

`analyze()` writes:

- `coded_data.csv` and, when applicable, `coded_data.sav`: input data with assigned codes;
- `segment_audit.csv`: model proposals, scores, conformal prediction sets and p-values when enabled, and decision reasons;
- `review_segments.csv`: responses that need manual review;
- `aggregation_report.csv`: segment-to-source-row aggregation results;
- `training_code_coverage.csv`: reference-example counts for every code;
- `run_manifest.json`: configuration, input hashes, model fingerprints, and output hashes.

`validate()` additionally writes `validation_summary.csv`,
`validation_detail.csv`, `validation_by_code.csv`,
`validation_confusion.csv`, `manual_code_coverage.csv`, and
`validation_manifest.json`.

## Further documentation

See [DOCUMENTATION.md](DOCUMENTATION.md) in the source distribution for the complete workflow, segmentation guidance, validation interpretation, and reproducibility notes.
