Skip to main content

Statys

Latest release CI PyPI License

Statys provides descriptive measures, pairwise non-parametric tests, Friedman and Iman-Davenport statistics, Nemenyi critical differences, and comparison plots.

Installation

Statys requires Python 3.11 or newer. Add it to a project managed by uv with:

uv add statys

For a consumer installation in an existing Python environment, pip is also supported:

pip install statys

Repeated comparisons

import numpy as np

from statys import friedman, nemenyi, plot_critical_difference

scores = np.array(
    [
        [0.82, 0.79, 0.75],
        [0.80, 0.77, 0.78],
        [0.84, 0.81, 0.76],
        [0.79, 0.75, 0.74],
    ]
)

print(friedman(scores))
ranks, critical_difference = nemenyi(scores)
plot_critical_difference(
    ranks,
    critical_difference,
    labels=["Model A", "Model B", "Model C"],
    output="critical-difference.pdf",
)

Rows are experimental blocks and columns are the treatments being compared. Smaller values receive lower ranks, with average ranks for ties. For metrics where larger is better (such as accuracy), use nemenyi(-scores) to give better treatments lower ranks.

friedman returns ((chi_square, df), (F, (df1, df2))), with tie correction from SciPy and an Iman-Davenport F statistic. Perfect agreement between non-constant block rankings gives F = inf. NaN inputs or blocks that all tie every treatment give undefined (nan) statistics, not evidence for the null hypothesis.

Measures and pairwise tests

from statys import measures, pairwise, significance

control = [0.82, 0.80, 0.84, 0.79]
model_a = [0.79, 0.77, 0.81, 0.75]
model_b = [0.75, 0.78, 0.76, 0.74]

print(measures.mean(control, model_a, model_b))

results = pairwise.signed_rank(control, model_a, model_b)
significance.plot_p_value(
    results,
    labels=["Control", "Model A", "Model B"],
    output="p-values.pdf",
)

The measures module also provides kurtosis, max, median, min, rank, skewness, std, and var. The pairwise module provides u_test, signed_rank, and rank_sum.

Pairwise results map arg{i}-arg{j} (i < j) to (reject, p_value), in input order. reject is 1 when p_value < alpha and 0 otherwise; p-values are not adjusted for multiple comparisons. Additional keyword arguments are forwarded to SciPy. The signed-rank test requires aligned, paired observations; the other two tests compare independent samples. Use u_test rather than rank_sum when tie correction is needed.

A test producing a non-finite p-value raises ValueError identifying the affected pair rather than reporting a false no-rejection decision. Missing data handling can be selected explicitly, for example with nan_policy="omit".

Significance plots mirror each stored comparison into both matrix halves. For one-sided tests, that result retains the original input order; the mirrored cell is not a test in the opposite direction. Missing comparisons remain blank. P-value colors use 1 - p on a fixed zero-to-one scale, so colors have the same meaning across plots; annotations show the original p-values.

Development

uv sync
uv run pytest
uv run pre-commit run --all-files
uv run --extra docs sphinx-build -W --keep-going -b html docs docs/_build/html
uv build

pytest also executes the examples in public docstrings. CI runs the interpreter matrix, existing style hooks, and a warning-as-error documentation build before permitting a release.

Code style

Adapted from cpmux's conventions, using the corresponding phitrain rule IDs:

  • Use modern unions (X | None) and builtin generics (dict[str, Any], list[str]). Import ABCs such as Callable and Iterable from collections.abc, not typing. Keep syntax compatible with Statys's declared Python 3.11+ support. (R2)
  • Keep imports top-level and absolute (from statys.x import y), grouped as stdlib, third-party, then local imports, with blank lines between groups.
  • Public APIs use Google-style docstrings with a single-sentence summary and one-line Args:, Returns:, and Raises: entries. Do not put semicolons or defaults to <X> tails in entries. Keep detailed contracts and examples in Notes: and Examples: rather than discarding them. (R3, R13)
  • Keep one blank line before a docstring's closing """ and one blank line after it before the first statement or field. Private helpers and framework-dispatched overrides have no docstrings. Test functions stay plain, without docstrings or type hints.
  • A regular class has a single-sentence class summary and documents constructor arguments on __init__. Data classes without an explicit constructor document every field in Attributes:, one name: what it holds. entry per line.
  • Raised messages name the backticked argument or offender and end with a period: "`<name>` <verb-phrase>[, but got <value>].". Use is None/is True prose. (R1)
  • Runtime validation uses if/raise with a specific exception, not assert. Never use a bare except: or translate dependency failures into successful-looking defaults.
  • Comments explain why, not what. Prefer no comment or one line, with a three-line maximum, no banner separators, and no trailing period. Copyright/license notices retain their prescribed punctuation. Preserve attribution for adapted code. (R8)
  • Separate logical phases with a single blank line in function bodies of at least 12 lines. Do not insert a blank line after every statement. (R11)
  • Inline first. Extract helpers, constants, or parameters when a second call site establishes reuse, rather than adding speculative abstractions. Preserve supported public APIs. (R16)
  • Use double-quoted string literals. Code and readable prose stay within 120 columns, using the existing Black, isort, and Flake8 tools without broad suppressions. (R9)
  • Never use print() in library code. If logging is introduced, follow the shared get_logger(__name__) pattern. Warning/error diagnostics use a backticked offender and a trailing period ("`name=value` <verb-phrase>."), while info/debug stay plain. (R14)

Every tracked Python file starts with this two-line header:

# Copyright (c) 2020-2026 Gustavo de Rosa.
# Licensed under the Apache License, Version 2.0.

Statys has no logging or application-framework layer to adapt, so these conventions do not introduce cpmux, Rich, Typer, Textual, or Pydantic dependencies. Example scripts are consumers rather than library code and may print their results. The Apache-2.0 license and supported package-level exports remain unchanged.

The library ships inline type information. Array inputs use numpy.typing.ArrayLike; rank arrays and fixed result tuples have concrete annotations. Measure return values and forwarded keyword arguments remain dynamic because NumPy/SciPy determine their type from the input dtype and options. Do not narrow these contracts by coercing or copying inputs merely to satisfy a type annotation.

Keep stateless operations as functions and share actual responsibilities such as the pairwise comparison loop. Use NumPy/SciPy for statistical primitives and explicit Matplotlib figures for plotting, without new factories or global plotting state.

Documentation is available at statys.readthedocs.io.

Releasing

Use uv version --bump patch (or the appropriate version increment), update statys.__version__ to match, and open a pull request for review.

After the pull request is merged into main and the full CI matrix succeeds, the release job publishes the untagged version to PyPI and creates a GitHub release and tag at that commit. Already-tagged versions are not republished. Publication uses the repository's PYPI_API_TOKEN secret and does not depend on a local CLI session.

Publishing a GitHub release manually remains supported; its tag must match the package version, prefixed with v. First attempts fail on duplicate PyPI files. An explicit rerun of the same workflow can resume a partial upload, skipping existing files rather than replacing them.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

statys-2.0.3.tar.gz (25.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

statys-2.0.3-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file statys-2.0.3.tar.gz.

File metadata

  • Download URL: statys-2.0.3.tar.gz
  • Upload date:
  • Size: 25.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for statys-2.0.3.tar.gz
Algorithm Hash digest
SHA256 5f0cf09831d1144f22a4936ee68c2271b25dc3a7b072035143ee7038b853672d
MD5 c63a2c2cc5aad85432fb8a62fcdd8b71
BLAKE2b-256 aa7667e637aecb2d4c02be69d3b251f52891d7a9b2f7f9b4b25cb665f3c8d9a4

See more details on using hashes here.

File details

Details for the file statys-2.0.3-py3-none-any.whl.

File metadata

  • Download URL: statys-2.0.3-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for statys-2.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a84369295507649df6c2cf1abe810bf1bb7514142194ef769699148ed097367f
MD5 c4d4b5b3418deb8c03a42015f87382ca
BLAKE2b-256 15bd89ed084d01ce1be92cde9fc03579480d093b7dcaaf6c7315e3ab91f8174a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.0.3 This release

2 files

2.0.2

2 files

2.0.1

2 files

2.0.0

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page