Metadata-Version: 2.4
Name: rcrpy
Version: 0.1.0
Summary: Robust Chauvenet Rejection (RCR), Python reimplementation
Project-URL: Homepage, https://github.com/nickk124/RCR
Project-URL: Repository, https://github.com/nickk124/RCR
Project-URL: Issues, https://github.com/nickk124/RCR/issues
Project-URL: Paper, https://arxiv.org/abs/1807.05276
Author-email: Haislip <haislip@physics.unc.edu>
Maintainer-email: UNC Physics RCR successors <haislip@physics.unc.edu>
License: COPYRIGHT AND PERMISSION NOTICE
        UNC Software "Robust Chauvenet Outlier Rejection" (Ref No. 17-0086)
        Copyright (C) 2020 The University of North Carolina at Chapel Hill
        All rights reserved.
        
        The University of North Carolina at Chapel Hill ("UNC") and the developers ("Developers") of Robust
        Chauvenet Outlier Rejection software ("Software") give recipient ("Recipient") and Recipient's Institution
        ("Institution") permission to use, copy and redistribute the software in source and binary forms, with or without
        modification for non-commercial purposes only provided that the following conditions are met:
        
        1) All copies and redistributions of Software in binary form and/or source code, related documentation
        and/or other materials provided with the Software must reproduce and retain the above copyright notice,
        this list of conditions and the following disclaimer.
        
        2) Recipient will provide the Developers with feedback on the use of the Software in their research. The
        Developers and UNC are permitted to use any information Recipient provides in making changes to the
        Software. All feedback, bug reports and technical questions shall be sent to: Prof. Daniel Reichart
        (reichart@email.unc.edu).
        
        3) Recipient acknowledges that the Developers, UNC and its licensees may develop modifications to
        Software that may be substantially similar to Recipient's modifications of Software, and that the
        Developers, UNC and its licensees shall not be constrained in any way by Recipient in UNC's or its
        licensees' use or management of such modifications. Recipient acknowledges the right of the
        Developers and UNC to prepare and publish modifications to Software that may be substantially similar
        or functionally equivalent to your modifications and improvements, and if Recipient or Institution
        obtains patent protection for any modification or improvement to Software, Recipient and Institution
        agree not to allege or enjoin infringement of their patent by the Developers, UNC or any of UNC's
        licensees obtaining modifications or improvements to Software from the UNC or the Developers.
        
        4) Recipient and Developer will acknowledge in their respective publications the contributions made to
        each other's research involving or based on the Software. The current citations for Software are:
        M. P. Maples, D. E. Reichart, N. C. Konz, T. A. Berger, A. S. Trotter, J. R. Martin, D. A. Dutton, M. L.
        Paggen, R. E. Joyner, C. P. Salemi, 2018 ApJS, 238.1, p. 2
        
        5) Any party desiring a license to use the Software for commercial purposes shall contact UNC's Office of
        Commercialization and Economic Development at 919-966-3929 or oced@unc.edu.
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS, AND THE
        UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL "AS IS" AND ANY EXPRESS OR IMPLIED
        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
        MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
        EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY OF NORTH
        CAROLINA AT CHAPEL HILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
        EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
        PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
        IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        POSSIBILITY OF SUCH DAMAGE.
License-File: LICENSE
Keywords: astronomy,chauvenet,outlier-rejection,robust-statistics,statistics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.11
Requires-Dist: numpy>=1.26
Requires-Dist: scipy>=1.11
Description-Content-Type: text/markdown

# rcrpy

Python reimplementation of Robust Chauvenet Rejection (RCR) — statistical
outlier rejection that stays accurate on samples that are >85% contaminated.

Based on the algorithm in
[Maples et al. 2018, ApJS 238.1](https://arxiv.org/abs/1807.05276).
This package is a numpy / scipy port of the original C++/pybind11
implementation at [`../cpp/`](../cpp/), preserving algorithmic parity
while running pure Python — no compiler toolchain required.

## Install

```bash
pip install rcrpy
```

Runtime deps: `numpy>=1.26`, `scipy>=1.11`. Python ≥3.11.

## Quick start

### Single-value RCR

Reject outliers from a 1-D dataset and recover the underlying central
value and width.

```python
import rcrpy
import numpy as np

# Heavily contaminated data: mu=0, sigma=1, plus one-sided outliers
rng = np.random.default_rng(42)
y = np.concatenate([
    rng.normal(0, 1, size=150),                # clean
    np.abs(rng.normal(0, 10, size=850)),       # 85% contamination
])

r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
r.perform_rejection(y.tolist())

print(f"Recovered mu     = {r.result.mu:.3f}    (truth: 0)")
print(f"Recovered sigma  = {r.result.sigma:.3f} (truth: 1)")
print(f"Points kept      = {r.result.flags.sum()} / {len(y)}")
```

### Functional-form RCR (linear model fit with outlier rejection)

Fit a parametric model to (x, y) data while rejecting outliers.

```python
import rcrpy
import numpy as np

# Linear data with outliers
rng = np.random.default_rng(0)
x = np.linspace(-5, 5, 100)
y = 2.0 + 1.5 * x + rng.normal(0, 0.3, size=x.size)
# Inject 20 outliers
out = rng.choice(x.size, size=20, replace=False)
y[out] += rng.normal(15, 5, size=20)

def linear(x, params):
    return params[0] + params[1] * x

def d_linear_b(x, params):
    return 1.0

def d_linear_m(x, params):
    return x

model = rcrpy.FunctionalForm(
    linear, x, y, [d_linear_b, d_linear_m], guess=[0.0, 0.0],
)
r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
r.set_parametric_model(model)
r.perform_rejection(y.tolist())

b, m = model.result.parameters
print(f"Recovered b = {b:.3f} (truth: 2.0)")
print(f"Recovered m = {m:.3f} (truth: 1.5)")
print(f"Kept {int(r.result.flags.sum())} / {len(y)} points")
```

## Rejection techniques

| `RejectionTech.*` | Use case |
|---|---|
| `LS_MODE_68` | Symmetric uncontaminated distribution, one-sided contaminants |
| `LS_MODE_DL` | Mixed one-sided + two-sided contaminants |
| `SS_MEDIAN_DL` | Symmetric uncontaminated distribution, two-sided contaminants |
| `ES_MODE_DL` | Mildly asymmetric uncontaminated distribution and/or very small N |

> ⚠️ **Known algorithm bug**: `ES_MODE_DL` combined with `FunctionalForm`
> (parametric models) exhibits a runaway-rejection cascade that drops
> ~75% of inliers even on perfectly clean data. **This is an algorithm bug
> inherited from the C++ source** — the C++ oracle exhibits the same
> behavior; it is not a port deficiency. Empirical characterization is in
> [`benchmarks/es_mode_dl_truth_test.py`](benchmarks/es_mode_dl_truth_test.py).
> All other combinations of rejection technique × model type work
> correctly. **For functional-form fits, use one of `LS_MODE_68`,
> `LS_MODE_DL`, or `SS_MEDIAN_DL`.** `ES_MODE_DL` remains correct for
> single-value (non-parametric) rejection.

See [agents/python_vs_rust_plan.md](../agents/python_vs_rust_plan.md) and
[Maples et al. 2018](https://arxiv.org/abs/1807.05276) for the science.

## More invocations

| Want… | Call |
|---|---|
| Bulk rejection (faster on large N) | `r.perform_bulk_rejection(y)` |
| Weighted RCR | `r.perform_rejection(y, w=weights)` |
| User-defined "candidate" mu | subclass `rcrpy.NonParametric`, `r.set_non_parametric_model(model)` |
| Add Gaussian / bounded priors | `rcrpy.Priors(prior_type=..., gaussian_params=...)` → pass via `priors=` to `FunctionalForm` |
| Pivot search for power-law / exponential fits | pass `pivot_function=...` and `pivot_guess=...` to `FunctionalForm` |
| ND independent variable | pass `xdata` as `(N, D)` array; user functions take vector `x` |

## Status

| Slice | Status |
|---|---|
| Phase 1 single-value RCR (4 techs × iter/bulk × weighted/unweighted) | ✅ 100%; bit-identical to C++ oracle at rtol=1e-12 (worst case 1.7e-15 = 8 ULPs) |
| Phase 2 functional form (1D + ND, priors, pivot search, paramuncertainty) | ✅ 99%; parity with C++ at rtol=5% on 6 sweep scenarios (RNG-limited) |
| Packaging / CI / docs | 🚧 0.1.0 release; collecting real-user feedback before 1.0 |

85 tests passing (+ 1 documented xfail). Reproduce parity with
[`benchmarks/reflect_parity.py`](benchmarks/reflect_parity.py); benchmark
with [`benchmarks/diagnostics.py`](benchmarks/diagnostics.py) and
[`benchmarks/diagnostics_functional.py`](benchmarks/diagnostics_functional.py).

## Layout

```
python/
├── pyproject.toml
├── README.md             (you are here)
├── LICENSE               (mirrors ../cpp/LICENSE — academic / non-commercial)
├── src/rcrpy/
│   ├── __init__.py       public API re-exports
│   ├── api.py            RCR class, RCRResults, RejectionTech, MuType
│   ├── stats.py          median, halfSampleMode, robust-sigma helpers
│   ├── rejection.py      iterative + bulk loops (lower/single/each sigma)
│   ├── tables.py         unity tables ported verbatim from cpp/src/RCR.cpp
│   ├── nonparametric.py  NonParametric base class
│   └── functional.py     FunctionalForm + Priors for parametric fits
├── tests/                pytest suite (85 + 1 xfailed)
├── docs/                 markdown tutorials + PUBLISHING checklist
├── benchmarks/           parity + perf scripts (not part of the wheel)
└── scripts/              one-off codegen + post-install smoke check
```

## Citing

If you use `rcrpy` in academic work, please cite the original paper:

```bibtex
@article{maples2018robust,
    title  = {Robust Chauvenet Outlier Rejection},
    author = {Maples, M.P. and Reichart, D.E. and Konz, N.C. and Berger, T.A.
              and Trotter, A.S. and Martin, J.R. and Dutton, D.A.
              and Paggen, M.L. and Joyner, R.E. and Salemi, C.P.},
    journal = {The Astrophysical Journal Supplement Series},
    volume  = {238}, number = {1}, pages = {2}, year = {2018},
}
```

## License

See [LICENSE](LICENSE) — mirrors the upstream C++ license. Free for
academic and non-commercial use; contact UNC's Office of
Commercialization and Economic Development for commercial licensing.
