rcrpy 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.
- rcrpy-0.1.0/.gitignore +162 -0
- rcrpy-0.1.0/LICENSE +48 -0
- rcrpy-0.1.0/PKG-INFO +247 -0
- rcrpy-0.1.0/README.md +171 -0
- rcrpy-0.1.0/benchmarks/README.md +26 -0
- rcrpy-0.1.0/benchmarks/bench_port_vs_oracle.py +46 -0
- rcrpy-0.1.0/benchmarks/compare_full_rcr.py +385 -0
- rcrpy-0.1.0/benchmarks/diagnostics.py +181 -0
- rcrpy-0.1.0/benchmarks/diagnostics_functional.py +171 -0
- rcrpy-0.1.0/benchmarks/es_mode_dl_truth_test.py +165 -0
- rcrpy-0.1.0/benchmarks/extreme_contamination_test.py +196 -0
- rcrpy-0.1.0/benchmarks/optimizer_quality_test.py +172 -0
- rcrpy-0.1.0/benchmarks/port_coverage_test.py +363 -0
- rcrpy-0.1.0/benchmarks/reflect_parity.py +170 -0
- rcrpy-0.1.0/benchmarks/sampling_regime_test.py +102 -0
- rcrpy-0.1.0/docs/PUBLISHING.md +109 -0
- rcrpy-0.1.0/docs/README.md +33 -0
- rcrpy-0.1.0/docs/advanced_functional.md +160 -0
- rcrpy-0.1.0/docs/full_rcr_handoff_explainer.txt +281 -0
- rcrpy-0.1.0/docs/functional_form.md +138 -0
- rcrpy-0.1.0/docs/migration_from_legacy_rcr.md +135 -0
- rcrpy-0.1.0/docs/single_value.md +91 -0
- rcrpy-0.1.0/full_rcr.py +4009 -0
- rcrpy-0.1.0/pyproject.toml +53 -0
- rcrpy-0.1.0/scripts/build_single_file.py +192 -0
- rcrpy-0.1.0/scripts/extract_unity_tables.py +163 -0
- rcrpy-0.1.0/scripts/post_install_check.py +53 -0
- rcrpy-0.1.0/scripts/smoke_test_full_rcr.py +80 -0
- rcrpy-0.1.0/src/rcrpy/__init__.py +24 -0
- rcrpy-0.1.0/src/rcrpy/api.py +169 -0
- rcrpy-0.1.0/src/rcrpy/functional.py +1078 -0
- rcrpy-0.1.0/src/rcrpy/nonparametric.py +46 -0
- rcrpy-0.1.0/src/rcrpy/rejection.py +1714 -0
- rcrpy-0.1.0/src/rcrpy/stats.py +650 -0
- rcrpy-0.1.0/src/rcrpy/tables.py +382 -0
- rcrpy-0.1.0/tests/__init__.py +0 -0
- rcrpy-0.1.0/tests/conftest.py +52 -0
- rcrpy-0.1.0/tests/test_functional.py +345 -0
- rcrpy-0.1.0/tests/test_functional_parity_extended.py +315 -0
- rcrpy-0.1.0/tests/test_functional_parity_sweep.py +149 -0
- rcrpy-0.1.0/tests/test_functional_rigorous.py +231 -0
- rcrpy-0.1.0/tests/test_nonparametric.py +86 -0
- rcrpy-0.1.0/tests/test_parity_bulk_all_techs.py +83 -0
- rcrpy-0.1.0/tests/test_parity_bulk_lsmode68.py +72 -0
- rcrpy-0.1.0/tests/test_parity_bulk_weighted.py +49 -0
- rcrpy-0.1.0/tests/test_parity_es_mode_dl.py +94 -0
- rcrpy-0.1.0/tests/test_parity_lsmode68.py +83 -0
- rcrpy-0.1.0/tests/test_parity_lsmode68_weighted.py +58 -0
- rcrpy-0.1.0/tests/test_parity_lsmode_dl.py +98 -0
- rcrpy-0.1.0/tests/test_parity_ss_median_dl.py +93 -0
- rcrpy-0.1.0/tests/test_smoke.py +44 -0
- rcrpy-0.1.0/tests/test_tables.py +77 -0
- rcrpy-0.1.0/uv.lock +247 -0
rcrpy-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Python
|
|
3
|
+
# ============================================================================
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.pyo
|
|
8
|
+
*.pyd
|
|
9
|
+
|
|
10
|
+
# Distribution / packaging
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
wheels/
|
|
14
|
+
*.egg
|
|
15
|
+
*.egg-info/
|
|
16
|
+
*.whl
|
|
17
|
+
MANIFEST
|
|
18
|
+
|
|
19
|
+
# Installer logs
|
|
20
|
+
pip-log.txt
|
|
21
|
+
pip-delete-this-directory.txt
|
|
22
|
+
|
|
23
|
+
# Unit test / coverage
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.tox/
|
|
26
|
+
.nox/
|
|
27
|
+
.coverage
|
|
28
|
+
.coverage.*
|
|
29
|
+
htmlcov/
|
|
30
|
+
.cache
|
|
31
|
+
nosetests.xml
|
|
32
|
+
coverage.xml
|
|
33
|
+
*.cover
|
|
34
|
+
.hypothesis/
|
|
35
|
+
|
|
36
|
+
# Type checkers / linters
|
|
37
|
+
.mypy_cache/
|
|
38
|
+
.dmypy.json
|
|
39
|
+
dmypy.json
|
|
40
|
+
.pyre/
|
|
41
|
+
.ruff_cache/
|
|
42
|
+
.pytype/
|
|
43
|
+
|
|
44
|
+
# Jupyter
|
|
45
|
+
.ipynb_checkpoints/
|
|
46
|
+
*.ipynb_checkpoints
|
|
47
|
+
|
|
48
|
+
# pyenv
|
|
49
|
+
.python-version
|
|
50
|
+
|
|
51
|
+
# Sphinx documentation
|
|
52
|
+
docs/_build/
|
|
53
|
+
|
|
54
|
+
# ============================================================================
|
|
55
|
+
# Virtual environments (project-specific names + the standard ones)
|
|
56
|
+
# ============================================================================
|
|
57
|
+
.venv/
|
|
58
|
+
.venv-*/
|
|
59
|
+
venv/
|
|
60
|
+
env/
|
|
61
|
+
ENV/
|
|
62
|
+
env.bak/
|
|
63
|
+
venv.bak/
|
|
64
|
+
|
|
65
|
+
# ============================================================================
|
|
66
|
+
# C++ build artifacts
|
|
67
|
+
# ============================================================================
|
|
68
|
+
*.o
|
|
69
|
+
*.obj
|
|
70
|
+
*.a
|
|
71
|
+
*.lib
|
|
72
|
+
*.so
|
|
73
|
+
*.so.*
|
|
74
|
+
*.dylib
|
|
75
|
+
*.dll
|
|
76
|
+
*.exe
|
|
77
|
+
*.out
|
|
78
|
+
*.app
|
|
79
|
+
|
|
80
|
+
# CMake
|
|
81
|
+
CMakeCache.txt
|
|
82
|
+
CMakeFiles/
|
|
83
|
+
CMakeScripts/
|
|
84
|
+
cmake_install.cmake
|
|
85
|
+
install_manifest.txt
|
|
86
|
+
CTestTestfile.cmake
|
|
87
|
+
_deps/
|
|
88
|
+
|
|
89
|
+
# Build directories
|
|
90
|
+
build/
|
|
91
|
+
build-*/
|
|
92
|
+
cmake-build-*/
|
|
93
|
+
|
|
94
|
+
# ============================================================================
|
|
95
|
+
# IDE / editor state
|
|
96
|
+
# ============================================================================
|
|
97
|
+
.vscode/
|
|
98
|
+
.idea/
|
|
99
|
+
*.swp
|
|
100
|
+
*.swo
|
|
101
|
+
*~
|
|
102
|
+
.project
|
|
103
|
+
.settings/
|
|
104
|
+
.classpath
|
|
105
|
+
|
|
106
|
+
# JetBrains
|
|
107
|
+
*.iml
|
|
108
|
+
.idea_modules/
|
|
109
|
+
|
|
110
|
+
# ============================================================================
|
|
111
|
+
# OS junk
|
|
112
|
+
# ============================================================================
|
|
113
|
+
.DS_Store
|
|
114
|
+
.DS_Store?
|
|
115
|
+
._*
|
|
116
|
+
.Spotlight-V100
|
|
117
|
+
.Trashes
|
|
118
|
+
ehthumbs.db
|
|
119
|
+
Thumbs.db
|
|
120
|
+
desktop.ini
|
|
121
|
+
$RECYCLE.BIN/
|
|
122
|
+
|
|
123
|
+
# ============================================================================
|
|
124
|
+
# Claude / agent local state (conversation transcripts live here on the user's
|
|
125
|
+
# machine; do not commit even by accident if a .claude/ ends up in the repo).
|
|
126
|
+
# ============================================================================
|
|
127
|
+
.claude/
|
|
128
|
+
.aider*
|
|
129
|
+
CLAUDE.md.local
|
|
130
|
+
|
|
131
|
+
# ============================================================================
|
|
132
|
+
# Logs and temporary files
|
|
133
|
+
# ============================================================================
|
|
134
|
+
*.log
|
|
135
|
+
*.tmp
|
|
136
|
+
*.bak
|
|
137
|
+
*.orig
|
|
138
|
+
*.rej
|
|
139
|
+
|
|
140
|
+
# ============================================================================
|
|
141
|
+
# Local secrets / credentials (defense in depth — nothing should ever be here)
|
|
142
|
+
# ============================================================================
|
|
143
|
+
.env
|
|
144
|
+
.env.local
|
|
145
|
+
.env.*.local
|
|
146
|
+
*.pem
|
|
147
|
+
*.key
|
|
148
|
+
secrets/
|
|
149
|
+
|
|
150
|
+
# ============================================================================
|
|
151
|
+
# Project-specific
|
|
152
|
+
# ============================================================================
|
|
153
|
+
# Compiled rcr extension lands here after pip install -e cpp/
|
|
154
|
+
cpp/rcr.*.pyd
|
|
155
|
+
cpp/rcr.*.so
|
|
156
|
+
cpp/build/
|
|
157
|
+
|
|
158
|
+
# Background-task transcripts (Claude Code writes these locally during sessions)
|
|
159
|
+
*.output
|
|
160
|
+
|
|
161
|
+
# Editor swap files in nested dirs
|
|
162
|
+
**/*~
|
rcrpy-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
COPYRIGHT AND PERMISSION NOTICE
|
|
2
|
+
UNC Software "Robust Chauvenet Outlier Rejection" (Ref No. 17-0086)
|
|
3
|
+
Copyright (C) 2020 The University of North Carolina at Chapel Hill
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
The University of North Carolina at Chapel Hill ("UNC") and the developers ("Developers") of Robust
|
|
7
|
+
Chauvenet Outlier Rejection software ("Software") give recipient ("Recipient") and Recipient's Institution
|
|
8
|
+
("Institution") permission to use, copy and redistribute the software in source and binary forms, with or without
|
|
9
|
+
modification for non-commercial purposes only provided that the following conditions are met:
|
|
10
|
+
|
|
11
|
+
1) All copies and redistributions of Software in binary form and/or source code, related documentation
|
|
12
|
+
and/or other materials provided with the Software must reproduce and retain the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer.
|
|
14
|
+
|
|
15
|
+
2) Recipient will provide the Developers with feedback on the use of the Software in their research. The
|
|
16
|
+
Developers and UNC are permitted to use any information Recipient provides in making changes to the
|
|
17
|
+
Software. All feedback, bug reports and technical questions shall be sent to: Prof. Daniel Reichart
|
|
18
|
+
(reichart@email.unc.edu).
|
|
19
|
+
|
|
20
|
+
3) Recipient acknowledges that the Developers, UNC and its licensees may develop modifications to
|
|
21
|
+
Software that may be substantially similar to Recipient's modifications of Software, and that the
|
|
22
|
+
Developers, UNC and its licensees shall not be constrained in any way by Recipient in UNC's or its
|
|
23
|
+
licensees' use or management of such modifications. Recipient acknowledges the right of the
|
|
24
|
+
Developers and UNC to prepare and publish modifications to Software that may be substantially similar
|
|
25
|
+
or functionally equivalent to your modifications and improvements, and if Recipient or Institution
|
|
26
|
+
obtains patent protection for any modification or improvement to Software, Recipient and Institution
|
|
27
|
+
agree not to allege or enjoin infringement of their patent by the Developers, UNC or any of UNC's
|
|
28
|
+
licensees obtaining modifications or improvements to Software from the UNC or the Developers.
|
|
29
|
+
|
|
30
|
+
4) Recipient and Developer will acknowledge in their respective publications the contributions made to
|
|
31
|
+
each other's research involving or based on the Software. The current citations for Software are:
|
|
32
|
+
M. P. Maples, D. E. Reichart, N. C. Konz, T. A. Berger, A. S. Trotter, J. R. Martin, D. A. Dutton, M. L.
|
|
33
|
+
Paggen, R. E. Joyner, C. P. Salemi, 2018 ApJS, 238.1, p. 2
|
|
34
|
+
|
|
35
|
+
5) Any party desiring a license to use the Software for commercial purposes shall contact UNC's Office of
|
|
36
|
+
Commercialization and Economic Development at 919-966-3929 or oced@unc.edu.
|
|
37
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS, AND THE
|
|
38
|
+
UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
39
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
40
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
41
|
+
EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY OF NORTH
|
|
42
|
+
CAROLINA AT CHAPEL HILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
43
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
44
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
45
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
46
|
+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
47
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
48
|
+
POSSIBILITY OF SUCH DAMAGE.
|
rcrpy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rcrpy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Robust Chauvenet Rejection (RCR), Python reimplementation
|
|
5
|
+
Project-URL: Homepage, https://github.com/nickk124/RCR
|
|
6
|
+
Project-URL: Repository, https://github.com/nickk124/RCR
|
|
7
|
+
Project-URL: Issues, https://github.com/nickk124/RCR/issues
|
|
8
|
+
Project-URL: Paper, https://arxiv.org/abs/1807.05276
|
|
9
|
+
Author-email: Haislip <haislip@physics.unc.edu>
|
|
10
|
+
Maintainer-email: UNC Physics RCR successors <haislip@physics.unc.edu>
|
|
11
|
+
License: COPYRIGHT AND PERMISSION NOTICE
|
|
12
|
+
UNC Software "Robust Chauvenet Outlier Rejection" (Ref No. 17-0086)
|
|
13
|
+
Copyright (C) 2020 The University of North Carolina at Chapel Hill
|
|
14
|
+
All rights reserved.
|
|
15
|
+
|
|
16
|
+
The University of North Carolina at Chapel Hill ("UNC") and the developers ("Developers") of Robust
|
|
17
|
+
Chauvenet Outlier Rejection software ("Software") give recipient ("Recipient") and Recipient's Institution
|
|
18
|
+
("Institution") permission to use, copy and redistribute the software in source and binary forms, with or without
|
|
19
|
+
modification for non-commercial purposes only provided that the following conditions are met:
|
|
20
|
+
|
|
21
|
+
1) All copies and redistributions of Software in binary form and/or source code, related documentation
|
|
22
|
+
and/or other materials provided with the Software must reproduce and retain the above copyright notice,
|
|
23
|
+
this list of conditions and the following disclaimer.
|
|
24
|
+
|
|
25
|
+
2) Recipient will provide the Developers with feedback on the use of the Software in their research. The
|
|
26
|
+
Developers and UNC are permitted to use any information Recipient provides in making changes to the
|
|
27
|
+
Software. All feedback, bug reports and technical questions shall be sent to: Prof. Daniel Reichart
|
|
28
|
+
(reichart@email.unc.edu).
|
|
29
|
+
|
|
30
|
+
3) Recipient acknowledges that the Developers, UNC and its licensees may develop modifications to
|
|
31
|
+
Software that may be substantially similar to Recipient's modifications of Software, and that the
|
|
32
|
+
Developers, UNC and its licensees shall not be constrained in any way by Recipient in UNC's or its
|
|
33
|
+
licensees' use or management of such modifications. Recipient acknowledges the right of the
|
|
34
|
+
Developers and UNC to prepare and publish modifications to Software that may be substantially similar
|
|
35
|
+
or functionally equivalent to your modifications and improvements, and if Recipient or Institution
|
|
36
|
+
obtains patent protection for any modification or improvement to Software, Recipient and Institution
|
|
37
|
+
agree not to allege or enjoin infringement of their patent by the Developers, UNC or any of UNC's
|
|
38
|
+
licensees obtaining modifications or improvements to Software from the UNC or the Developers.
|
|
39
|
+
|
|
40
|
+
4) Recipient and Developer will acknowledge in their respective publications the contributions made to
|
|
41
|
+
each other's research involving or based on the Software. The current citations for Software are:
|
|
42
|
+
M. P. Maples, D. E. Reichart, N. C. Konz, T. A. Berger, A. S. Trotter, J. R. Martin, D. A. Dutton, M. L.
|
|
43
|
+
Paggen, R. E. Joyner, C. P. Salemi, 2018 ApJS, 238.1, p. 2
|
|
44
|
+
|
|
45
|
+
5) Any party desiring a license to use the Software for commercial purposes shall contact UNC's Office of
|
|
46
|
+
Commercialization and Economic Development at 919-966-3929 or oced@unc.edu.
|
|
47
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS, AND THE
|
|
48
|
+
UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
49
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
50
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
51
|
+
EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY OF NORTH
|
|
52
|
+
CAROLINA AT CHAPEL HILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
53
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
54
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
55
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
56
|
+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
57
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
58
|
+
POSSIBILITY OF SUCH DAMAGE.
|
|
59
|
+
License-File: LICENSE
|
|
60
|
+
Keywords: astronomy,chauvenet,outlier-rejection,robust-statistics,statistics
|
|
61
|
+
Classifier: Development Status :: 4 - Beta
|
|
62
|
+
Classifier: Intended Audience :: Science/Research
|
|
63
|
+
Classifier: License :: Other/Proprietary License
|
|
64
|
+
Classifier: Operating System :: OS Independent
|
|
65
|
+
Classifier: Programming Language :: Python :: 3
|
|
66
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
67
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
68
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
69
|
+
Classifier: Topic :: Scientific/Engineering
|
|
70
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
71
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
72
|
+
Requires-Python: >=3.11
|
|
73
|
+
Requires-Dist: numpy>=1.26
|
|
74
|
+
Requires-Dist: scipy>=1.11
|
|
75
|
+
Description-Content-Type: text/markdown
|
|
76
|
+
|
|
77
|
+
# rcrpy
|
|
78
|
+
|
|
79
|
+
Python reimplementation of Robust Chauvenet Rejection (RCR) — statistical
|
|
80
|
+
outlier rejection that stays accurate on samples that are >85% contaminated.
|
|
81
|
+
|
|
82
|
+
Based on the algorithm in
|
|
83
|
+
[Maples et al. 2018, ApJS 238.1](https://arxiv.org/abs/1807.05276).
|
|
84
|
+
This package is a numpy / scipy port of the original C++/pybind11
|
|
85
|
+
implementation at [`../cpp/`](../cpp/), preserving algorithmic parity
|
|
86
|
+
while running pure Python — no compiler toolchain required.
|
|
87
|
+
|
|
88
|
+
## Install
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install rcrpy
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Runtime deps: `numpy>=1.26`, `scipy>=1.11`. Python ≥3.11.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
### Single-value RCR
|
|
99
|
+
|
|
100
|
+
Reject outliers from a 1-D dataset and recover the underlying central
|
|
101
|
+
value and width.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import rcrpy
|
|
105
|
+
import numpy as np
|
|
106
|
+
|
|
107
|
+
# Heavily contaminated data: mu=0, sigma=1, plus one-sided outliers
|
|
108
|
+
rng = np.random.default_rng(42)
|
|
109
|
+
y = np.concatenate([
|
|
110
|
+
rng.normal(0, 1, size=150), # clean
|
|
111
|
+
np.abs(rng.normal(0, 10, size=850)), # 85% contamination
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
|
|
115
|
+
r.perform_rejection(y.tolist())
|
|
116
|
+
|
|
117
|
+
print(f"Recovered mu = {r.result.mu:.3f} (truth: 0)")
|
|
118
|
+
print(f"Recovered sigma = {r.result.sigma:.3f} (truth: 1)")
|
|
119
|
+
print(f"Points kept = {r.result.flags.sum()} / {len(y)}")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Functional-form RCR (linear model fit with outlier rejection)
|
|
123
|
+
|
|
124
|
+
Fit a parametric model to (x, y) data while rejecting outliers.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import rcrpy
|
|
128
|
+
import numpy as np
|
|
129
|
+
|
|
130
|
+
# Linear data with outliers
|
|
131
|
+
rng = np.random.default_rng(0)
|
|
132
|
+
x = np.linspace(-5, 5, 100)
|
|
133
|
+
y = 2.0 + 1.5 * x + rng.normal(0, 0.3, size=x.size)
|
|
134
|
+
# Inject 20 outliers
|
|
135
|
+
out = rng.choice(x.size, size=20, replace=False)
|
|
136
|
+
y[out] += rng.normal(15, 5, size=20)
|
|
137
|
+
|
|
138
|
+
def linear(x, params):
|
|
139
|
+
return params[0] + params[1] * x
|
|
140
|
+
|
|
141
|
+
def d_linear_b(x, params):
|
|
142
|
+
return 1.0
|
|
143
|
+
|
|
144
|
+
def d_linear_m(x, params):
|
|
145
|
+
return x
|
|
146
|
+
|
|
147
|
+
model = rcrpy.FunctionalForm(
|
|
148
|
+
linear, x, y, [d_linear_b, d_linear_m], guess=[0.0, 0.0],
|
|
149
|
+
)
|
|
150
|
+
r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
|
|
151
|
+
r.set_parametric_model(model)
|
|
152
|
+
r.perform_rejection(y.tolist())
|
|
153
|
+
|
|
154
|
+
b, m = model.result.parameters
|
|
155
|
+
print(f"Recovered b = {b:.3f} (truth: 2.0)")
|
|
156
|
+
print(f"Recovered m = {m:.3f} (truth: 1.5)")
|
|
157
|
+
print(f"Kept {int(r.result.flags.sum())} / {len(y)} points")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Rejection techniques
|
|
161
|
+
|
|
162
|
+
| `RejectionTech.*` | Use case |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `LS_MODE_68` | Symmetric uncontaminated distribution, one-sided contaminants |
|
|
165
|
+
| `LS_MODE_DL` | Mixed one-sided + two-sided contaminants |
|
|
166
|
+
| `SS_MEDIAN_DL` | Symmetric uncontaminated distribution, two-sided contaminants |
|
|
167
|
+
| `ES_MODE_DL` | Mildly asymmetric uncontaminated distribution and/or very small N |
|
|
168
|
+
|
|
169
|
+
> ⚠️ **Known algorithm bug**: `ES_MODE_DL` combined with `FunctionalForm`
|
|
170
|
+
> (parametric models) exhibits a runaway-rejection cascade that drops
|
|
171
|
+
> ~75% of inliers even on perfectly clean data. **This is an algorithm bug
|
|
172
|
+
> inherited from the C++ source** — the C++ oracle exhibits the same
|
|
173
|
+
> behavior; it is not a port deficiency. Empirical characterization is in
|
|
174
|
+
> [`benchmarks/es_mode_dl_truth_test.py`](benchmarks/es_mode_dl_truth_test.py).
|
|
175
|
+
> All other combinations of rejection technique × model type work
|
|
176
|
+
> correctly. **For functional-form fits, use one of `LS_MODE_68`,
|
|
177
|
+
> `LS_MODE_DL`, or `SS_MEDIAN_DL`.** `ES_MODE_DL` remains correct for
|
|
178
|
+
> single-value (non-parametric) rejection.
|
|
179
|
+
|
|
180
|
+
See [agents/python_vs_rust_plan.md](../agents/python_vs_rust_plan.md) and
|
|
181
|
+
[Maples et al. 2018](https://arxiv.org/abs/1807.05276) for the science.
|
|
182
|
+
|
|
183
|
+
## More invocations
|
|
184
|
+
|
|
185
|
+
| Want… | Call |
|
|
186
|
+
|---|---|
|
|
187
|
+
| Bulk rejection (faster on large N) | `r.perform_bulk_rejection(y)` |
|
|
188
|
+
| Weighted RCR | `r.perform_rejection(y, w=weights)` |
|
|
189
|
+
| User-defined "candidate" mu | subclass `rcrpy.NonParametric`, `r.set_non_parametric_model(model)` |
|
|
190
|
+
| Add Gaussian / bounded priors | `rcrpy.Priors(prior_type=..., gaussian_params=...)` → pass via `priors=` to `FunctionalForm` |
|
|
191
|
+
| Pivot search for power-law / exponential fits | pass `pivot_function=...` and `pivot_guess=...` to `FunctionalForm` |
|
|
192
|
+
| ND independent variable | pass `xdata` as `(N, D)` array; user functions take vector `x` |
|
|
193
|
+
|
|
194
|
+
## Status
|
|
195
|
+
|
|
196
|
+
| Slice | Status |
|
|
197
|
+
|---|---|
|
|
198
|
+
| 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) |
|
|
199
|
+
| Phase 2 functional form (1D + ND, priors, pivot search, paramuncertainty) | ✅ 99%; parity with C++ at rtol=5% on 6 sweep scenarios (RNG-limited) |
|
|
200
|
+
| Packaging / CI / docs | 🚧 0.1.0 release; collecting real-user feedback before 1.0 |
|
|
201
|
+
|
|
202
|
+
85 tests passing (+ 1 documented xfail). Reproduce parity with
|
|
203
|
+
[`benchmarks/reflect_parity.py`](benchmarks/reflect_parity.py); benchmark
|
|
204
|
+
with [`benchmarks/diagnostics.py`](benchmarks/diagnostics.py) and
|
|
205
|
+
[`benchmarks/diagnostics_functional.py`](benchmarks/diagnostics_functional.py).
|
|
206
|
+
|
|
207
|
+
## Layout
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
python/
|
|
211
|
+
├── pyproject.toml
|
|
212
|
+
├── README.md (you are here)
|
|
213
|
+
├── LICENSE (mirrors ../cpp/LICENSE — academic / non-commercial)
|
|
214
|
+
├── src/rcrpy/
|
|
215
|
+
│ ├── __init__.py public API re-exports
|
|
216
|
+
│ ├── api.py RCR class, RCRResults, RejectionTech, MuType
|
|
217
|
+
│ ├── stats.py median, halfSampleMode, robust-sigma helpers
|
|
218
|
+
│ ├── rejection.py iterative + bulk loops (lower/single/each sigma)
|
|
219
|
+
│ ├── tables.py unity tables ported verbatim from cpp/src/RCR.cpp
|
|
220
|
+
│ ├── nonparametric.py NonParametric base class
|
|
221
|
+
│ └── functional.py FunctionalForm + Priors for parametric fits
|
|
222
|
+
├── tests/ pytest suite (85 + 1 xfailed)
|
|
223
|
+
├── docs/ markdown tutorials + PUBLISHING checklist
|
|
224
|
+
├── benchmarks/ parity + perf scripts (not part of the wheel)
|
|
225
|
+
└── scripts/ one-off codegen + post-install smoke check
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Citing
|
|
229
|
+
|
|
230
|
+
If you use `rcrpy` in academic work, please cite the original paper:
|
|
231
|
+
|
|
232
|
+
```bibtex
|
|
233
|
+
@article{maples2018robust,
|
|
234
|
+
title = {Robust Chauvenet Outlier Rejection},
|
|
235
|
+
author = {Maples, M.P. and Reichart, D.E. and Konz, N.C. and Berger, T.A.
|
|
236
|
+
and Trotter, A.S. and Martin, J.R. and Dutton, D.A.
|
|
237
|
+
and Paggen, M.L. and Joyner, R.E. and Salemi, C.P.},
|
|
238
|
+
journal = {The Astrophysical Journal Supplement Series},
|
|
239
|
+
volume = {238}, number = {1}, pages = {2}, year = {2018},
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
See [LICENSE](LICENSE) — mirrors the upstream C++ license. Free for
|
|
246
|
+
academic and non-commercial use; contact UNC's Office of
|
|
247
|
+
Commercialization and Economic Development for commercial licensing.
|
rcrpy-0.1.0/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# rcrpy
|
|
2
|
+
|
|
3
|
+
Python reimplementation of Robust Chauvenet Rejection (RCR) — statistical
|
|
4
|
+
outlier rejection that stays accurate on samples that are >85% contaminated.
|
|
5
|
+
|
|
6
|
+
Based on the algorithm in
|
|
7
|
+
[Maples et al. 2018, ApJS 238.1](https://arxiv.org/abs/1807.05276).
|
|
8
|
+
This package is a numpy / scipy port of the original C++/pybind11
|
|
9
|
+
implementation at [`../cpp/`](../cpp/), preserving algorithmic parity
|
|
10
|
+
while running pure Python — no compiler toolchain required.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install rcrpy
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Runtime deps: `numpy>=1.26`, `scipy>=1.11`. Python ≥3.11.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
### Single-value RCR
|
|
23
|
+
|
|
24
|
+
Reject outliers from a 1-D dataset and recover the underlying central
|
|
25
|
+
value and width.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import rcrpy
|
|
29
|
+
import numpy as np
|
|
30
|
+
|
|
31
|
+
# Heavily contaminated data: mu=0, sigma=1, plus one-sided outliers
|
|
32
|
+
rng = np.random.default_rng(42)
|
|
33
|
+
y = np.concatenate([
|
|
34
|
+
rng.normal(0, 1, size=150), # clean
|
|
35
|
+
np.abs(rng.normal(0, 10, size=850)), # 85% contamination
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
|
|
39
|
+
r.perform_rejection(y.tolist())
|
|
40
|
+
|
|
41
|
+
print(f"Recovered mu = {r.result.mu:.3f} (truth: 0)")
|
|
42
|
+
print(f"Recovered sigma = {r.result.sigma:.3f} (truth: 1)")
|
|
43
|
+
print(f"Points kept = {r.result.flags.sum()} / {len(y)}")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Functional-form RCR (linear model fit with outlier rejection)
|
|
47
|
+
|
|
48
|
+
Fit a parametric model to (x, y) data while rejecting outliers.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import rcrpy
|
|
52
|
+
import numpy as np
|
|
53
|
+
|
|
54
|
+
# Linear data with outliers
|
|
55
|
+
rng = np.random.default_rng(0)
|
|
56
|
+
x = np.linspace(-5, 5, 100)
|
|
57
|
+
y = 2.0 + 1.5 * x + rng.normal(0, 0.3, size=x.size)
|
|
58
|
+
# Inject 20 outliers
|
|
59
|
+
out = rng.choice(x.size, size=20, replace=False)
|
|
60
|
+
y[out] += rng.normal(15, 5, size=20)
|
|
61
|
+
|
|
62
|
+
def linear(x, params):
|
|
63
|
+
return params[0] + params[1] * x
|
|
64
|
+
|
|
65
|
+
def d_linear_b(x, params):
|
|
66
|
+
return 1.0
|
|
67
|
+
|
|
68
|
+
def d_linear_m(x, params):
|
|
69
|
+
return x
|
|
70
|
+
|
|
71
|
+
model = rcrpy.FunctionalForm(
|
|
72
|
+
linear, x, y, [d_linear_b, d_linear_m], guess=[0.0, 0.0],
|
|
73
|
+
)
|
|
74
|
+
r = rcrpy.RCR(rcrpy.RejectionTech.LS_MODE_68)
|
|
75
|
+
r.set_parametric_model(model)
|
|
76
|
+
r.perform_rejection(y.tolist())
|
|
77
|
+
|
|
78
|
+
b, m = model.result.parameters
|
|
79
|
+
print(f"Recovered b = {b:.3f} (truth: 2.0)")
|
|
80
|
+
print(f"Recovered m = {m:.3f} (truth: 1.5)")
|
|
81
|
+
print(f"Kept {int(r.result.flags.sum())} / {len(y)} points")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Rejection techniques
|
|
85
|
+
|
|
86
|
+
| `RejectionTech.*` | Use case |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `LS_MODE_68` | Symmetric uncontaminated distribution, one-sided contaminants |
|
|
89
|
+
| `LS_MODE_DL` | Mixed one-sided + two-sided contaminants |
|
|
90
|
+
| `SS_MEDIAN_DL` | Symmetric uncontaminated distribution, two-sided contaminants |
|
|
91
|
+
| `ES_MODE_DL` | Mildly asymmetric uncontaminated distribution and/or very small N |
|
|
92
|
+
|
|
93
|
+
> ⚠️ **Known algorithm bug**: `ES_MODE_DL` combined with `FunctionalForm`
|
|
94
|
+
> (parametric models) exhibits a runaway-rejection cascade that drops
|
|
95
|
+
> ~75% of inliers even on perfectly clean data. **This is an algorithm bug
|
|
96
|
+
> inherited from the C++ source** — the C++ oracle exhibits the same
|
|
97
|
+
> behavior; it is not a port deficiency. Empirical characterization is in
|
|
98
|
+
> [`benchmarks/es_mode_dl_truth_test.py`](benchmarks/es_mode_dl_truth_test.py).
|
|
99
|
+
> All other combinations of rejection technique × model type work
|
|
100
|
+
> correctly. **For functional-form fits, use one of `LS_MODE_68`,
|
|
101
|
+
> `LS_MODE_DL`, or `SS_MEDIAN_DL`.** `ES_MODE_DL` remains correct for
|
|
102
|
+
> single-value (non-parametric) rejection.
|
|
103
|
+
|
|
104
|
+
See [agents/python_vs_rust_plan.md](../agents/python_vs_rust_plan.md) and
|
|
105
|
+
[Maples et al. 2018](https://arxiv.org/abs/1807.05276) for the science.
|
|
106
|
+
|
|
107
|
+
## More invocations
|
|
108
|
+
|
|
109
|
+
| Want… | Call |
|
|
110
|
+
|---|---|
|
|
111
|
+
| Bulk rejection (faster on large N) | `r.perform_bulk_rejection(y)` |
|
|
112
|
+
| Weighted RCR | `r.perform_rejection(y, w=weights)` |
|
|
113
|
+
| User-defined "candidate" mu | subclass `rcrpy.NonParametric`, `r.set_non_parametric_model(model)` |
|
|
114
|
+
| Add Gaussian / bounded priors | `rcrpy.Priors(prior_type=..., gaussian_params=...)` → pass via `priors=` to `FunctionalForm` |
|
|
115
|
+
| Pivot search for power-law / exponential fits | pass `pivot_function=...` and `pivot_guess=...` to `FunctionalForm` |
|
|
116
|
+
| ND independent variable | pass `xdata` as `(N, D)` array; user functions take vector `x` |
|
|
117
|
+
|
|
118
|
+
## Status
|
|
119
|
+
|
|
120
|
+
| Slice | Status |
|
|
121
|
+
|---|---|
|
|
122
|
+
| 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) |
|
|
123
|
+
| Phase 2 functional form (1D + ND, priors, pivot search, paramuncertainty) | ✅ 99%; parity with C++ at rtol=5% on 6 sweep scenarios (RNG-limited) |
|
|
124
|
+
| Packaging / CI / docs | 🚧 0.1.0 release; collecting real-user feedback before 1.0 |
|
|
125
|
+
|
|
126
|
+
85 tests passing (+ 1 documented xfail). Reproduce parity with
|
|
127
|
+
[`benchmarks/reflect_parity.py`](benchmarks/reflect_parity.py); benchmark
|
|
128
|
+
with [`benchmarks/diagnostics.py`](benchmarks/diagnostics.py) and
|
|
129
|
+
[`benchmarks/diagnostics_functional.py`](benchmarks/diagnostics_functional.py).
|
|
130
|
+
|
|
131
|
+
## Layout
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
python/
|
|
135
|
+
├── pyproject.toml
|
|
136
|
+
├── README.md (you are here)
|
|
137
|
+
├── LICENSE (mirrors ../cpp/LICENSE — academic / non-commercial)
|
|
138
|
+
├── src/rcrpy/
|
|
139
|
+
│ ├── __init__.py public API re-exports
|
|
140
|
+
│ ├── api.py RCR class, RCRResults, RejectionTech, MuType
|
|
141
|
+
│ ├── stats.py median, halfSampleMode, robust-sigma helpers
|
|
142
|
+
│ ├── rejection.py iterative + bulk loops (lower/single/each sigma)
|
|
143
|
+
│ ├── tables.py unity tables ported verbatim from cpp/src/RCR.cpp
|
|
144
|
+
│ ├── nonparametric.py NonParametric base class
|
|
145
|
+
│ └── functional.py FunctionalForm + Priors for parametric fits
|
|
146
|
+
├── tests/ pytest suite (85 + 1 xfailed)
|
|
147
|
+
├── docs/ markdown tutorials + PUBLISHING checklist
|
|
148
|
+
├── benchmarks/ parity + perf scripts (not part of the wheel)
|
|
149
|
+
└── scripts/ one-off codegen + post-install smoke check
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Citing
|
|
153
|
+
|
|
154
|
+
If you use `rcrpy` in academic work, please cite the original paper:
|
|
155
|
+
|
|
156
|
+
```bibtex
|
|
157
|
+
@article{maples2018robust,
|
|
158
|
+
title = {Robust Chauvenet Outlier Rejection},
|
|
159
|
+
author = {Maples, M.P. and Reichart, D.E. and Konz, N.C. and Berger, T.A.
|
|
160
|
+
and Trotter, A.S. and Martin, J.R. and Dutton, D.A.
|
|
161
|
+
and Paggen, M.L. and Joyner, R.E. and Salemi, C.P.},
|
|
162
|
+
journal = {The Astrophysical Journal Supplement Series},
|
|
163
|
+
volume = {238}, number = {1}, pages = {2}, year = {2018},
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
See [LICENSE](LICENSE) — mirrors the upstream C++ license. Free for
|
|
170
|
+
academic and non-commercial use; contact UNC's Office of
|
|
171
|
+
Commercialization and Economic Development for commercial licensing.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# `rcrpy` benchmarks
|
|
2
|
+
|
|
3
|
+
Standalone scripts for measuring `rcrpy` correctness and performance.
|
|
4
|
+
None of these are part of the installed package — they're maintainer
|
|
5
|
+
tools that live alongside the source.
|
|
6
|
+
|
|
7
|
+
| Script | What it does |
|
|
8
|
+
|---|---|
|
|
9
|
+
| [`reflect_parity.py`](reflect_parity.py) | Sweep all 20 Phase 1 parity scenarios (single-value RCR × every tech × iterative+bulk × weighted+unweighted). Reports max relative scalar disagreement vs the C++ oracle. Worst case: 1.7×10⁻¹⁵. |
|
|
10
|
+
| [`diagnostics.py`](diagnostics.py) | Per-scenario wall-clock + precision sweep for single-value RCR (port vs. C++ oracle). |
|
|
11
|
+
| [`diagnostics_functional.py`](diagnostics_functional.py) | Same for functional-form RCR. Demonstrates the port is FASTER than the oracle on this workload (80×–6000× depending on N). |
|
|
12
|
+
| [`bench_port_vs_oracle.py`](bench_port_vs_oracle.py) | One-shot bench on `data_singlevalue.csv` for quick perf checks. |
|
|
13
|
+
|
|
14
|
+
## Running
|
|
15
|
+
|
|
16
|
+
Each script is self-contained. From the repo root:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python rcrpy/benchmarks/reflect_parity.py
|
|
20
|
+
python rcrpy/benchmarks/diagnostics.py
|
|
21
|
+
python rcrpy/benchmarks/diagnostics_functional.py
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
All four require the legacy `rcr` C++ module (the oracle) plus `rcrpy`
|
|
25
|
+
itself installed in the active environment — i.e., `pip install -e
|
|
26
|
+
./python[dev]` covers everything.
|