This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
CompStats implements a bootstrap-based evaluation methodology for statistically comparing the
performance of multiple algorithms/systems in a competition-style setting (e.g., comparing several
classifiers' predictions against the same held-out gold labels). It follows the sklearn.metrics
API convention: score functions take (y_true, y_pred) but return a rich object (Perf) instead of
a bare float, giving access to bootstrap standard error, confidence intervals, and pairwise
significance testing between systems.
All project artifacts must be written in English: function/variable names, comments, parameters,
docstrings, and any other generated text. This is a research project, so code clarity matters —
follow the existing docstring/comment style shown throughout CompStats/ (see metrics.py's
@metrics_docs pattern, and the :param:/:type:/doctest-style docstrings in interface.py,
bootstrap.py, measurements.py) rather than introducing a different documentation style.
Instructions from the user, in this conversation, may be given in either English or Spanish — that does not change the above: respond and implement with English identifiers/comments/docs regardless of the language the request was made in.
- Work is driven by GitHub issues: an issue is created describing the implementation to make, and Claude is asked to read that issue and implement it.
- Never implement directly on the main branch. If not already on a branch other than main, create a new branch first.
- The user typically works on a branch called
develop. If already ondevelop(or another non-main branch), it's fine to implement there directly — no need to create a new branch just for that.
Install the package and its dependencies (editable install, used by the devcontainer via
.devcontainer/python.sh):
pip install -e .
pip install -r requirements.txtRun the full test suite (this is what VS Code's test explorer and .vscode/settings.json are
configured to use — despite CI still invoking nosetests, day-to-day development here uses pytest):
pytest CompStatsRun a single test file or test:
pytest CompStats/tests/test_interface.py
pytest CompStats/tests/test_interface.py::test_Perf_nameRun with coverage (mirrors what CI collects, excluding the tests package per .coveragerc):
coverage run -m pytest CompStats
coverage reportBuild the Sphinx docs:
cd docs && make htmlNote: .github/workflows/test.yaml (CI) builds the environment with conda and runs nosetests,
not pytest — this is legacy and inconsistent with local/devcontainer tooling. Don't be surprised if
CI config and local dev commands diverge; prefer pytest locally.
The package is small and organized around one core data flow: raw predictions → bootstrap resampled statistic → derived comparisons/plots.
-
bootstrap.py—StatisticSamples: the foundational primitive. Given astatisticcallable (e.g.accuracy_score), it drawsnum_samplesbootstrap resamples (with replacement) of the population and evaluates the statistic on each resample, optionally in parallel (joblib). Results for a named system are cached inself.calls[name](a dict of name → ndarray of bootstrap samples). Bootstrap sample indices are cached per population size inself._samples, so multiple algorithms evaluated against the samey_truereuse the same resampling (this is what makes pairwise comparisons valid/paired). Supports__sklearn_clone__sosklearn.base.cloneproduces a fresh instance carrying over params (used heavily to createDifferenceobjects from aPerfwithout recomputing bootstrap samples). -
interface.py—PerfandDifference: the main user-facing entry point (re-exported at package root).Perf(y_true, *y_pred, name=..., func=..., BiB=True, **kwargs)wraps one or more systems' predictions against shared ground truth.funcis a single callable or a list of callables (a multi-measurePerf); direction (bigger-is-better vs smaller-is-better) comes from each callable's own.BiBattribute when it has one (e.g. set by ametrics.pywrapper's.measurefactory), falling back to the constructor'sBiBdefault otherwise —BiBis the single source of truth for direction, there is no separatescore_func/error_funcsplit. Internally holds aStatisticSampleskeyed by system name; new predictions can be added later viaperf(y_pred, name=...)(__call__).Perf.difference(wrt=...)produces aDifferenceinstance (comparing every system against the best, or an explicit reference) whosep_value()is computed directly from the bootstrap distribution of paired differences — no parametric test assumptions.Perf.plot()/Difference.plot()render via seaborncatplot, with confidence intervals computed bymeasurements.CIpassed as theerrorbarcallback. -
metrics.py: thin wrappers aroundsklearn.metricsfunctions (accuracy_score,balanced_accuracy_score,top_k_accuracy_score,f1_score, etc.). Each wrapper closes over the sklearn metric (plus its metric-specific kwargs likeaverage,normalize) and constructs aPerfwith that asfunc, tagging the inner closure's.BiB(Truefor a score,Falsefor an error) so direction travels with the callable. The@metrics_docsdecorator (fromutils.py) injects the sharedPerf-style docstring (params likenum_samples,n_jobs,use_tqdm) into each wrapper automatically — when adding a new metric wrapper, follow this same@metrics_docs(hy_name=..., bib=...)+ inner-function-closure pattern rather than duplicating docstrings. -
measurements.py: stateless helpers —CI(percentile bootstrap confidence interval),SE(bootstrap standard error),difference_p_value. Each accepts either a raw ndarray of bootstrap samples or aStatisticSamplesinstance (in which case it maps itself over.calls). -
performance.py: an alternative, more functional (non-Perf) API operating directly on apandas.DataFrame(one gold column + one column per system) —performance(),difference()/all_differences(), and theplot_performance*/plot_difference*family, plus*_multiplevariants for comparing several metrics at once (used for multi-metric competition reports: coefficient of variation, PPI, distance-to-best per metric). This module is older/more ad-hoc thaninterface.py'sPerf; new comparison features generally belong onPerf/Differenceunless they specifically need the DataFrame-of-multiple-metrics shape. -
utils.py:progress_bar(tqdm wrapper, no-op if tqdm isn't installed oruse_tqdm=False),metrics_docs(docstring-injecting decorator described above), anddataframe()(melts aPerf's orDifference's bootstrap samples into a long-format DataFrame for seaborn plotting).
- Bootstrap resampling must stay paired across systems being compared —
StatisticSamples.samplescaches resample indices by population sizeNprecisely so every system's bootstrap replicateiuses the same resampled indices. Don't introduce per-system independent resampling. BiB(Bigger is Better) must be threaded consistently: a callable's own.BiBattribute wins when present, otherwisePerf'sBiBconstructor argument is the default. Sorting,best, and p-value sign logic throughoutinterface.py/performance.pyread this flag (self._bib/statistic_samples.BiB) rather than re-deriving it from which argument a function was passed as.sklearn.base.clone/__sklearn_clone__is used to duplicatePerf/StatisticSamplesinstances while reusing already-computed bootstrap samples (e.g.Perf.difference(),performance.difference). Don't replace these with plain re-instantiation, as that silently redraws new bootstrap samples and breaks paired comparisons.