rasch-per 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rasch_per-0.1.0/.github/workflows/ci.yml +39 -0
- rasch_per-0.1.0/.github/workflows/docs.yml +45 -0
- rasch_per-0.1.0/.github/workflows/publish.yml +60 -0
- rasch_per-0.1.0/.gitignore +29 -0
- rasch_per-0.1.0/.pre-commit-config.yaml +14 -0
- rasch_per-0.1.0/.readthedocs.yaml +16 -0
- rasch_per-0.1.0/.validation.sh +29 -0
- rasch_per-0.1.0/CHANGELOG.md +12 -0
- rasch_per-0.1.0/CITATION.cff +25 -0
- rasch_per-0.1.0/CONTRIBUTING.md +58 -0
- rasch_per-0.1.0/GRILL.md +20 -0
- rasch_per-0.1.0/LICENSE +21 -0
- rasch_per-0.1.0/PKG-INFO +191 -0
- rasch_per-0.1.0/README.md +147 -0
- rasch_per-0.1.0/docsrc/api.md +38 -0
- rasch_per-0.1.0/docsrc/cli.md +64 -0
- rasch_per-0.1.0/docsrc/contributing.md +51 -0
- rasch_per-0.1.0/docsrc/index.md +50 -0
- rasch_per-0.1.0/docsrc/methodology.md +111 -0
- rasch_per-0.1.0/docsrc/quickstart.md +73 -0
- rasch_per-0.1.0/docsrc/report.md +43 -0
- rasch_per-0.1.0/examples/notebooks/quickstart.ipynb +109 -0
- rasch_per-0.1.0/examples/notebooks/report_walkthrough.ipynb +109 -0
- rasch_per-0.1.0/images/dif_contrasts.png +0 -0
- rasch_per-0.1.0/images/icc.png +0 -0
- rasch_per-0.1.0/images/item_difficulty.png +0 -0
- rasch_per-0.1.0/images/item_discrimination.png +0 -0
- rasch_per-0.1.0/images/test_information.png +0 -0
- rasch_per-0.1.0/images/wright_map.png +0 -0
- rasch_per-0.1.0/mkdocs.yml +31 -0
- rasch_per-0.1.0/pyproject.toml +89 -0
- rasch_per-0.1.0/scripts/cfa_extra.py +52 -0
- rasch_per-0.1.0/scripts/export_pdf.py +38 -0
- rasch_per-0.1.0/scripts/stocking_lord.py +74 -0
- rasch_per-0.1.0/scripts/validate_against_r.R +47 -0
- rasch_per-0.1.0/src/rasch_per/__init__.py +22 -0
- rasch_per-0.1.0/src/rasch_per/cli.py +142 -0
- rasch_per-0.1.0/src/rasch_per/ctt.py +218 -0
- rasch_per-0.1.0/src/rasch_per/data.py +230 -0
- rasch_per-0.1.0/src/rasch_per/dif.py +225 -0
- rasch_per-0.1.0/src/rasch_per/dimensionality.py +104 -0
- rasch_per-0.1.0/src/rasch_per/plots.py +185 -0
- rasch_per-0.1.0/src/rasch_per/py.typed +0 -0
- rasch_per-0.1.0/src/rasch_per/rasch/__init__.py +42 -0
- rasch_per-0.1.0/src/rasch_per/rasch/dimensionality.py +104 -0
- rasch_per-0.1.0/src/rasch_per/rasch/estimation.py +323 -0
- rasch_per-0.1.0/src/rasch_per/rasch/fit.py +241 -0
- rasch_per-0.1.0/src/rasch_per/rasch/info.py +153 -0
- rasch_per-0.1.0/src/rasch_per/rasch/model.py +199 -0
- rasch_per-0.1.0/src/rasch_per/reliability.py +184 -0
- rasch_per-0.1.0/src/rasch_per/report.py +186 -0
- rasch_per-0.1.0/src/rasch_per/simulate.py +119 -0
- rasch_per-0.1.0/templates/report_template.html.j2 +11 -0
- rasch_per-0.1.0/tests/conftest.py +10 -0
- rasch_per-0.1.0/tests/data/README.md +2 -0
- rasch_per-0.1.0/tests/data/reference_values.json +16 -0
- rasch_per-0.1.0/tests/data/synthetic_small.csv +6 -0
- rasch_per-0.1.0/tests/test_cli.py +81 -0
- rasch_per-0.1.0/tests/test_ctt.py +131 -0
- rasch_per-0.1.0/tests/test_data.py +115 -0
- rasch_per-0.1.0/tests/test_dif.py +112 -0
- rasch_per-0.1.0/tests/test_dimensionality.py +58 -0
- rasch_per-0.1.0/tests/test_fit.py +151 -0
- rasch_per-0.1.0/tests/test_info.py +72 -0
- rasch_per-0.1.0/tests/test_plots.py +101 -0
- rasch_per-0.1.0/tests/test_rasch_estimation.py +123 -0
- rasch_per-0.1.0/tests/test_rasch_model.py +94 -0
- rasch_per-0.1.0/tests/test_reliability.py +80 -0
- rasch_per-0.1.0/tests/test_report.py +55 -0
- rasch_per-0.1.0/tests/test_simulate.py +94 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
pip install -e ".[dev]"
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: |
|
|
26
|
+
ruff check .
|
|
27
|
+
ruff format --check .
|
|
28
|
+
- name: Type check
|
|
29
|
+
run: mypy
|
|
30
|
+
- name: Test with coverage gate
|
|
31
|
+
# Coverage gate (85%) is enforced now that the library modules are
|
|
32
|
+
# implemented (Phases 0-5).
|
|
33
|
+
run: pytest --cov=rasch_per --cov-fail-under=85 --cov-report=xml
|
|
34
|
+
- name: Upload coverage
|
|
35
|
+
if: matrix.python-version == '3.13'
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: coverage.xml
|
|
39
|
+
path: coverage.xml
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docsrc/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- ".readthedocs.yaml"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: pages
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Install docs extra
|
|
31
|
+
run: python -m pip install --upgrade pip && python -m pip install ".[docs]"
|
|
32
|
+
- name: Build site
|
|
33
|
+
run: mkdocs build --strict
|
|
34
|
+
- uses: actions/upload-pages-artifact@v3
|
|
35
|
+
with:
|
|
36
|
+
path: site
|
|
37
|
+
deploy:
|
|
38
|
+
needs: build
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
environment:
|
|
41
|
+
name: github-pages
|
|
42
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
43
|
+
steps:
|
|
44
|
+
- id: deployment
|
|
45
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*.*.*"]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.13"
|
|
16
|
+
- name: Build distributions
|
|
17
|
+
run: |
|
|
18
|
+
python -m pip install --upgrade build twine
|
|
19
|
+
python -m build
|
|
20
|
+
twine check dist/*
|
|
21
|
+
- uses: actions/upload-artifact@v4
|
|
22
|
+
with:
|
|
23
|
+
name: dist
|
|
24
|
+
path: dist/
|
|
25
|
+
|
|
26
|
+
publish-testpypi:
|
|
27
|
+
# Requires Trusted Publishing configured on testpypi.org for this repo
|
|
28
|
+
# (manual one-time setup, done in Phase 7). Until then, opt in by setting
|
|
29
|
+
# the repository variable ENABLE_TESTPYPI_PUBLISH=true; the job skips
|
|
30
|
+
# otherwise so pushes to main stay green.
|
|
31
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/main') && vars.ENABLE_TESTPYPI_PUBLISH == 'true'
|
|
32
|
+
needs: build
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment: testpypi
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/download-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
- name: Publish to TestPyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
repository-url: https://test.pypi.org/legacy/
|
|
46
|
+
|
|
47
|
+
publish-pypi:
|
|
48
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
49
|
+
needs: build
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
environment: pypi
|
|
52
|
+
permissions:
|
|
53
|
+
id-token: write
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/download-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist/
|
|
59
|
+
- name: Publish to PyPI (Trusted Publisher)
|
|
60
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# FirstHQ workflow files (never commit)
|
|
2
|
+
docs/
|
|
3
|
+
AGENT.md
|
|
4
|
+
CONTEXT.json
|
|
5
|
+
TODO.md
|
|
6
|
+
update.md
|
|
7
|
+
log.md
|
|
8
|
+
.opencode/
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*.egg-info/
|
|
14
|
+
dist/
|
|
15
|
+
build/
|
|
16
|
+
site/
|
|
17
|
+
.mkdocs_cache/
|
|
18
|
+
.venv/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
coverage.xml
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# Editors / OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
cd "$(dirname "$0")"
|
|
5
|
+
|
|
6
|
+
if [ ! -d ".venv" ]; then
|
|
7
|
+
echo "Creating virtualenv..."
|
|
8
|
+
uv venv --python 3.13 .venv
|
|
9
|
+
uv pip install -e ".[dev]" -p .venv
|
|
10
|
+
fi
|
|
11
|
+
PY=".venv/bin/python"
|
|
12
|
+
|
|
13
|
+
echo "=== Validation Loop ==="
|
|
14
|
+
|
|
15
|
+
echo "[1/4] Building..."
|
|
16
|
+
$PY -m compileall -q src/rasch_per
|
|
17
|
+
|
|
18
|
+
echo "[2/4] Type checking..."
|
|
19
|
+
$PY -m mypy src/
|
|
20
|
+
|
|
21
|
+
echo "[3/4] Linting..."
|
|
22
|
+
$PY -m ruff check .
|
|
23
|
+
$PY -m ruff format --check .
|
|
24
|
+
|
|
25
|
+
echo "[4/4] Testing..."
|
|
26
|
+
$PY -m pytest --cov=rasch_per -q
|
|
27
|
+
# Coverage gate (85%) is enforced in CI (.github/workflows/ci.yml), not locally.
|
|
28
|
+
|
|
29
|
+
echo "=== All checks passed ==="
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Project scaffolding: package skeleton, CLI entry point, CI and publish workflows.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: rasch-per
|
|
4
|
+
abstract: >-
|
|
5
|
+
Rasch model and Classical Test Theory (CTT) psychometric analysis for
|
|
6
|
+
education research, built for physics / STEM / discipline-based education
|
|
7
|
+
researchers (PER / DBER). Provides person abilities, item difficulties,
|
|
8
|
+
fit statistics, dimensionality checks, differential item functioning (DIF)
|
|
9
|
+
analysis, and a self-contained HTML validity report from dichotomous
|
|
10
|
+
item-response data.
|
|
11
|
+
authors:
|
|
12
|
+
- family-names: Anil
|
|
13
|
+
given-names: Aditya
|
|
14
|
+
email: aditya.anil.productions@gmail.com
|
|
15
|
+
repository-code: "https://github.com/aditya-an1l/rasch-per"
|
|
16
|
+
url: "https://github.com/aditya-an1l/rasch-per"
|
|
17
|
+
license: MIT
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
date-released: 2026-08-27
|
|
20
|
+
keywords:
|
|
21
|
+
- psychometrics
|
|
22
|
+
- rasch
|
|
23
|
+
- item response theory
|
|
24
|
+
- education research
|
|
25
|
+
- physics education research
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Contributing to rasch-per
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/aditya-an1l/rasch-per
|
|
9
|
+
cd rasch-per
|
|
10
|
+
uv venv --python 3.13 .venv
|
|
11
|
+
uv pip install -e ".[dev]" -p .venv
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or with plain pip:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python -m venv .venv && source .venv/bin/activate
|
|
18
|
+
pip install -e ".[dev]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Running checks
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
./.validation.sh # build + type check + lint + tests
|
|
25
|
+
python -m pytest # tests only (with coverage)
|
|
26
|
+
python -m ruff check . # lint only
|
|
27
|
+
python -m mypy # types only
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
CI runs the same checks on Python 3.10-3.13 and enforces a 85% coverage gate.
|
|
31
|
+
Both `ruff check` and `mypy` must pass before any PR is merged.
|
|
32
|
+
|
|
33
|
+
## Code style expectations
|
|
34
|
+
|
|
35
|
+
- Full NumPy-style docstrings (Parameters, Returns, worked mini-example) on
|
|
36
|
+
every public function/class.
|
|
37
|
+
- Type hints everywhere; no new hard dependencies without discussion first.
|
|
38
|
+
- Statistical formulas follow the conventions fixed in the project spec; do
|
|
39
|
+
not swap in alternative conventions from other sources without raising it
|
|
40
|
+
for discussion first.
|
|
41
|
+
- All example/test data comes from `rasch_per.simulate` - never vendor
|
|
42
|
+
third-party data or item text.
|
|
43
|
+
|
|
44
|
+
## Publishing (maintainers)
|
|
45
|
+
|
|
46
|
+
The GitHub Actions workflow `.github/workflows/publish.yml` builds and
|
|
47
|
+
publishes via PyPI Trusted Publishing on tagged releases (`v*.*.*`). TestPyPI
|
|
48
|
+
publishes on every push to `main`.
|
|
49
|
+
|
|
50
|
+
Manual publish (first-time alternative):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python -m pip install --upgrade build twine
|
|
54
|
+
python -m build
|
|
55
|
+
twine check dist/*
|
|
56
|
+
twine upload --repository testpypi dist/* # sanity check first
|
|
57
|
+
twine upload dist/* # real release
|
|
58
|
+
```
|
rasch_per-0.1.0/GRILL.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Grill Session — 2026-08-27
|
|
2
|
+
|
|
3
|
+
## Project: rasch-per
|
|
4
|
+
|
|
5
|
+
### Shortcomings
|
|
6
|
+
|
|
7
|
+
- AGENT.md references `docs/skills/SPEC.md` as the authoritative source for resolved conventions (Q3 adjustment, ETS delta sign, Ferguson delta), but that file does not exist in the repo. Phase 3 implemented `fit.py` (Yen's Q3) and `dimensionality.py` (PCAR) using the standard published formulas rather than a spec-pinned variant. Risk: if the spec specified a non-standard Q3 adjustment or PCAR centering, current output diverges from intended behavior.
|
|
8
|
+
|
|
9
|
+
### Unresolved Questions
|
|
10
|
+
|
|
11
|
+
- Where is the original build spec (the SPEC.md source)? It drove Phases 0-2 but is not on disk. Does the user have it, or was it only in the earlier chat session?
|
|
12
|
+
- Should the Q3 / PCAR implementation use the classic residual-correlation form (current) or a spec-specific adjustment once recovered?
|
|
13
|
+
|
|
14
|
+
### Next Steps
|
|
15
|
+
|
|
16
|
+
- Recover or re-supply SPEC.md (or the relevant convention snippets) and re-verify fit.py / dimensionality.py against it before Phase 5 (DIF, which also depends on the ETS delta sign convention).
|
|
17
|
+
|
|
18
|
+
## Resolved 2026-08-27
|
|
19
|
+
|
|
20
|
+
- The SPEC.md gap (shortcoming above) was closed by reconstructing `docs/skills/SPEC.md` from the implemented code. The original Phase 0-2 spec remains unavailable; the reconstructed conventions use the standard published forms (Yen 1984 Q3, ETS delta sign positive = easier for focal, Ferguson delta per Ferguson 1949). If the original spec is recovered, diff and correct before a public release.
|
rasch_per-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aditya Anil
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
rasch_per-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rasch-per
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Rasch model and CTT psychometric analysis for education research
|
|
5
|
+
Project-URL: Homepage, https://github.com/aditya-an1l/rasch-per
|
|
6
|
+
Project-URL: Repository, https://github.com/aditya-an1l/rasch-per
|
|
7
|
+
Project-URL: Issues, https://github.com/aditya-an1l/rasch-per/issues
|
|
8
|
+
Author-email: Aditya Anil <aditya.anil.productions@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: education research,item response theory,physics education research,psychometrics,rasch
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: jinja2>=3.1
|
|
24
|
+
Requires-Dist: matplotlib>=3.7
|
|
25
|
+
Requires-Dist: numpy>=1.24
|
|
26
|
+
Requires-Dist: pandas>=2.0
|
|
27
|
+
Requires-Dist: scipy>=1.10
|
|
28
|
+
Requires-Dist: typer>=0.9
|
|
29
|
+
Provides-Extra: cfa
|
|
30
|
+
Requires-Dist: semopy; extra == 'cfa'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
33
|
+
Requires-Dist: pandas-stubs; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
37
|
+
Requires-Dist: scipy-stubs; extra == 'dev'
|
|
38
|
+
Provides-Extra: docs
|
|
39
|
+
Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
|
|
40
|
+
Requires-Dist: mkdocs>=1.5; extra == 'docs'
|
|
41
|
+
Provides-Extra: pdf
|
|
42
|
+
Requires-Dist: weasyprint; extra == 'pdf'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# rasch-per
|
|
46
|
+
|
|
47
|
+
Rasch model and Classical Test Theory (CTT) psychometric analysis for
|
|
48
|
+
education research, built for physics / STEM / discipline-based education
|
|
49
|
+
researchers (PER / DBER).
|
|
50
|
+
|
|
51
|
+
Feed it a CSV of dichotomous (0/1) item responses and get person abilities,
|
|
52
|
+
item difficulties, fit statistics, dimensionality checks, DIF analysis, and a
|
|
53
|
+
full self-contained HTML validity report.
|
|
54
|
+
|
|
55
|
+
Status: implemented and validated (119 tests, ~97% coverage). The library is
|
|
56
|
+
Beta; the public API is stable for the analyses listed below.
|
|
57
|
+
|
|
58
|
+
## Quickstart (CLI)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install rasch-per
|
|
62
|
+
rasch-per simulate --output demo.csv
|
|
63
|
+
rasch-per analyze demo.csv --output report.html
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`simulate` writes a synthetic response CSV (with a `person_id` index). `analyze`
|
|
67
|
+
reads it back (the first column is the person index), runs the full pipeline,
|
|
68
|
+
and writes a self-contained HTML report.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# With differential item functioning (DIF) by a group column
|
|
72
|
+
rasch-per analyze demo.csv --groups groups.csv --dif-group gender \
|
|
73
|
+
--reference Man --focal Non-man --output report.html
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Python API
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import pandas as pd
|
|
80
|
+
from rasch_per import (
|
|
81
|
+
ResponseData,
|
|
82
|
+
CTTAnalysis,
|
|
83
|
+
RaschModel,
|
|
84
|
+
DIFAnalysis,
|
|
85
|
+
generate_report,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Load a response matrix (persons as rows, items as columns)
|
|
89
|
+
df = pd.read_csv("responses.csv", index_col=0)
|
|
90
|
+
data = ResponseData(df)
|
|
91
|
+
|
|
92
|
+
# Classical Test Theory
|
|
93
|
+
ctt = CTTAnalysis(data).run()
|
|
94
|
+
print(ctt.summary())
|
|
95
|
+
print(ctt.reliability.cronbach_alpha) # attribute, not a method
|
|
96
|
+
|
|
97
|
+
# Rasch (MML is the default estimator)
|
|
98
|
+
model = RaschModel().fit(data, estimator="MML")
|
|
99
|
+
print(model.item_difficulties) # pandas Series indexed by item name
|
|
100
|
+
print(model.fit_statistics()) # infit / outfit mean-squares
|
|
101
|
+
|
|
102
|
+
# Differential Item Functioning
|
|
103
|
+
groups = pd.read_csv("groups.csv", index_col=0)["gender"].reindex(data.person_ids)
|
|
104
|
+
dif = DIFAnalysis(model, groups=groups.to_numpy(), reference="Man", focal="Non-man").analyze()
|
|
105
|
+
print(dif.summary()) # ETS delta classification + BH flags
|
|
106
|
+
|
|
107
|
+
# Self-contained HTML validity report
|
|
108
|
+
generate_report(
|
|
109
|
+
df,
|
|
110
|
+
output="validity_report.html",
|
|
111
|
+
groups=groups.to_numpy(),
|
|
112
|
+
reference="Man",
|
|
113
|
+
focal="Non-man",
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Notes on the API:
|
|
118
|
+
|
|
119
|
+
- `generate_report` takes a `pandas.DataFrame`, not a `ResponseData`. When you
|
|
120
|
+
pass `groups`, they must be aligned to the DataFrame's row order (here, the
|
|
121
|
+
person index).
|
|
122
|
+
- `CTTResults.reliability` is an attribute (`cronbach_alpha`, `mcdonald_omega`,
|
|
123
|
+
`ferguson_delta`), not a callable.
|
|
124
|
+
- `DIFAnalysis` is run with `.analyze()` (it returns a `DIFResults`).
|
|
125
|
+
|
|
126
|
+
## What's inside
|
|
127
|
+
|
|
128
|
+
- **CTT**: item difficulty (p-values), discrimination (corrected item-total,
|
|
129
|
+
rest-score based) with bootstrap SEs; Cronbach's alpha, McDonald's omega,
|
|
130
|
+
Ferguson's delta.
|
|
131
|
+
- **Rasch**: JML and MML estimation (MML default), standard errors.
|
|
132
|
+
- **Fit**: infit/outfit mean-square with low-stakes/high-stakes presets;
|
|
133
|
+
Yen's Q3 local independence check.
|
|
134
|
+
- **Dimensionality**: PCAR first-contrast eigenvalue diagnostic.
|
|
135
|
+
- **DIF**: Lord's chi-square, mean/mean linking, ETS delta effect sizes,
|
|
136
|
+
Benjamini-Hochberg correction.
|
|
137
|
+
- **Report**: single-file HTML validity report with embedded plots.
|
|
138
|
+
|
|
139
|
+
## Methodology & References
|
|
140
|
+
|
|
141
|
+
This package implements standard, published psychometric methods used
|
|
142
|
+
throughout physics / discipline-based education research:
|
|
143
|
+
|
|
144
|
+
- Rasch model (Rasch, 1960; Wright & Stone, 1979)
|
|
145
|
+
- Joint and marginal maximum likelihood estimation (e.g., as in R packages
|
|
146
|
+
`TAM`, `eRm`)
|
|
147
|
+
- Infit/outfit mean-square fit statistics (Smith, 2000; Linacre, 2002)
|
|
148
|
+
- Yen's Q3 local independence statistic (Yen, 1984)
|
|
149
|
+
- Principal components analysis of residuals (Linacre, 1998)
|
|
150
|
+
- Lord's chi-square DIF test (Lord, 1980)
|
|
151
|
+
- ETS delta scale DIF classification (ETS categories A/B/C)
|
|
152
|
+
- Benjamini-Hochberg false discovery rate control (Benjamini & Hochberg, 1995)
|
|
153
|
+
- Cronbach's alpha (Cronbach, 1951), McDonald's omega (McDonald, 1999),
|
|
154
|
+
Ferguson's delta (Ferguson, 1949)
|
|
155
|
+
|
|
156
|
+
No third-party assessment content or data is included; all examples use
|
|
157
|
+
synthetic data from the package's own simulator.
|
|
158
|
+
|
|
159
|
+
## Documentation & Examples
|
|
160
|
+
|
|
161
|
+
- API and usage docs: `mkdocs serve` (source in `docsrc/`, requires the
|
|
162
|
+
`docs` extra: `pip install "rasch-per[docs]"`).
|
|
163
|
+
- Worked notebooks: `examples/notebooks/quickstart.ipynb` and
|
|
164
|
+
`examples/notebooks/report_walkthrough.ipynb`.
|
|
165
|
+
- Optional analyses (PDF export, CFA, Stocking-Lord linking, R cross-validation)
|
|
166
|
+
live in `scripts/` and use the `pdf` / `cfa` extras where needed.
|
|
167
|
+
|
|
168
|
+
## Screenshots
|
|
169
|
+
|
|
170
|
+
Key diagnostic plots produced by the package's plotting API on simulated data
|
|
171
|
+
(500 persons, 20 items, seed 42):
|
|
172
|
+
|
|
173
|
+
| Plot | Description |
|
|
174
|
+
|------|-------------|
|
|
175
|
+
|  | Person ability vs item difficulty (Wright map) |
|
|
176
|
+
|  | Test information and standard error of measurement across the ability scale |
|
|
177
|
+
|  | Item characteristic curve with empirical overlay (item 1) |
|
|
178
|
+
|  | CTT item difficulty with bootstrap standard-error bars |
|
|
179
|
+
|  | CTT item point-biserial discrimination |
|
|
180
|
+
|  | DIF contrasts with ETS A/B/C classification |
|
|
181
|
+
|
|
182
|
+
The figures are generated from `simulate_rasch_data` and the analysis pipeline
|
|
183
|
+
(CTT, Rasch MML, DIF).
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Validation loop: `./.validation.sh`.
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|