rcrpy 0.1.0__py3-none-any.whl
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/__init__.py +24 -0
- rcrpy/api.py +169 -0
- rcrpy/functional.py +1078 -0
- rcrpy/nonparametric.py +46 -0
- rcrpy/rejection.py +1714 -0
- rcrpy/stats.py +650 -0
- rcrpy/tables.py +382 -0
- rcrpy-0.1.0.dist-info/METADATA +247 -0
- rcrpy-0.1.0.dist-info/RECORD +11 -0
- rcrpy-0.1.0.dist-info/WHEEL +4 -0
- rcrpy-0.1.0.dist-info/licenses/LICENSE +48 -0
rcrpy/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""rcrpy: Robust Chauvenet Rejection, Python reimplementation."""
|
|
2
|
+
|
|
3
|
+
from rcrpy.api import (
|
|
4
|
+
RCR,
|
|
5
|
+
RCRResults,
|
|
6
|
+
RejectionTech,
|
|
7
|
+
MuType,
|
|
8
|
+
)
|
|
9
|
+
from rcrpy.nonparametric import NonParametric
|
|
10
|
+
from rcrpy.functional import FunctionalForm, FunctionalFormResults, Priors, PriorType
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RCR",
|
|
14
|
+
"RCRResults",
|
|
15
|
+
"RejectionTech",
|
|
16
|
+
"MuType",
|
|
17
|
+
"NonParametric",
|
|
18
|
+
"FunctionalForm",
|
|
19
|
+
"FunctionalFormResults",
|
|
20
|
+
"Priors",
|
|
21
|
+
"PriorType",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
rcrpy/api.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Public API for rcrpy.
|
|
2
|
+
|
|
3
|
+
Mirrors the surface defined in cpp/src/RCR.h:
|
|
4
|
+
enum RejectionTechs { SS_MEDIAN_DL, LS_MODE_68, LS_MODE_DL, ES_MODE_DL }
|
|
5
|
+
struct RCRResults { mu, sigma, stDev, ..., flags, indices, ... }
|
|
6
|
+
class RCR { performRejection, performBulkRejection, ... }
|
|
7
|
+
|
|
8
|
+
Single-value (non-parametric, non-functional) rejection only in Phase 1.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Sequence
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RejectionTech(Enum):
|
|
20
|
+
SS_MEDIAN_DL = "SS_MEDIAN_DL"
|
|
21
|
+
LS_MODE_68 = "LS_MODE_68"
|
|
22
|
+
LS_MODE_DL = "LS_MODE_DL"
|
|
23
|
+
ES_MODE_DL = "ES_MODE_DL"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MuType(Enum):
|
|
27
|
+
"""How `mu` is computed each iteration. VALUE is the default (use the
|
|
28
|
+
handled muTech directly on `y`); PARAMETRIC and NONPARAMETRIC delegate
|
|
29
|
+
to a model object that the user attaches via setter methods on RCR.
|
|
30
|
+
Ports of cpp/src/RCR.h::MuTypes."""
|
|
31
|
+
VALUE = "VALUE"
|
|
32
|
+
PARAMETRIC = "PARAMETRIC" # FunctionalForm — not yet wired in rcrpy
|
|
33
|
+
NONPARAMETRIC = "NONPARAMETRIC"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class RCRResults:
|
|
38
|
+
mu: float = float("nan")
|
|
39
|
+
sigma: float = float("nan")
|
|
40
|
+
sigma_below: float = float("nan")
|
|
41
|
+
sigma_above: float = float("nan")
|
|
42
|
+
st_dev: float = float("nan")
|
|
43
|
+
st_dev_below: float = float("nan")
|
|
44
|
+
st_dev_above: float = float("nan")
|
|
45
|
+
st_dev_total: float = float("nan")
|
|
46
|
+
flags: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=bool))
|
|
47
|
+
indices: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.int64))
|
|
48
|
+
clean_y: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
49
|
+
rejected_y: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
50
|
+
clean_w: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
51
|
+
rejected_w: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
52
|
+
original_y: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
53
|
+
original_w: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class RCR:
|
|
57
|
+
"""Single-value Robust Chauvenet Rejection.
|
|
58
|
+
|
|
59
|
+
Phase 1: API skeleton. The real iterative + bulk loops are ported in
|
|
60
|
+
rcrpy.rejection and dispatched here.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, rejection_tech: RejectionTech = RejectionTech.SS_MEDIAN_DL):
|
|
64
|
+
self.rejection_tech = rejection_tech
|
|
65
|
+
self.result = RCRResults()
|
|
66
|
+
self.mu_type: MuType = MuType.VALUE
|
|
67
|
+
self.non_parametric_model = None # set via set_non_parametric_model
|
|
68
|
+
self.parametric_model = None # set via set_parametric_model
|
|
69
|
+
|
|
70
|
+
def set_rejection_tech(self, tech: RejectionTech) -> None:
|
|
71
|
+
self.rejection_tech = tech
|
|
72
|
+
|
|
73
|
+
def set_mu_type(self, mu_type: MuType) -> None:
|
|
74
|
+
self.mu_type = mu_type
|
|
75
|
+
|
|
76
|
+
def set_non_parametric_model(self, model) -> None:
|
|
77
|
+
"""Attach a `rcrpy.NonParametric` subclass instance. The user must
|
|
78
|
+
also call `set_mu_type(MuType.NONPARAMETRIC)`. Port of
|
|
79
|
+
cpp/src/RCR.h::setNonParametricModel."""
|
|
80
|
+
self.non_parametric_model = model
|
|
81
|
+
self.mu_type = MuType.NONPARAMETRIC
|
|
82
|
+
|
|
83
|
+
def set_parametric_model(self, model) -> None:
|
|
84
|
+
"""Attach a `rcrpy.FunctionalForm` instance for model-fitting RCR.
|
|
85
|
+
Sets mu_type to PARAMETRIC automatically. Port of
|
|
86
|
+
cpp/src/RCR.h::setParametricModel."""
|
|
87
|
+
self.parametric_model = model
|
|
88
|
+
self.mu_type = MuType.PARAMETRIC
|
|
89
|
+
|
|
90
|
+
def perform_rejection(
|
|
91
|
+
self,
|
|
92
|
+
y: Sequence[float],
|
|
93
|
+
w: Sequence[float] | None = None,
|
|
94
|
+
) -> None:
|
|
95
|
+
from rcrpy import rejection
|
|
96
|
+
y_arr = np.asarray(y, dtype=np.float64)
|
|
97
|
+
w_arr = None if w is None else np.asarray(w, dtype=np.float64)
|
|
98
|
+
out = rejection.performRejection_LS(
|
|
99
|
+
y_arr, self.rejection_tech.value, w=w_arr,
|
|
100
|
+
non_parametric_model=(self.non_parametric_model
|
|
101
|
+
if self.mu_type is MuType.NONPARAMETRIC else None),
|
|
102
|
+
parametric_model=(self.parametric_model
|
|
103
|
+
if self.mu_type is MuType.PARAMETRIC else None),
|
|
104
|
+
)
|
|
105
|
+
r = self.result
|
|
106
|
+
r.mu = out["mu"]
|
|
107
|
+
r.flags = out["flags"]
|
|
108
|
+
r.indices = out["indices"]
|
|
109
|
+
r.clean_y = out["clean_y"]
|
|
110
|
+
# Each-sigma populates sigma_below/above; the other techs populate
|
|
111
|
+
# sigma/st_dev. setFinalVectors-only fields (rejectedY, originalY,
|
|
112
|
+
# clean_w/rejected_w/original_w) remain at default-empty per
|
|
113
|
+
# performRejection's contract.
|
|
114
|
+
if "sigma" in out:
|
|
115
|
+
r.sigma = out["sigma"]
|
|
116
|
+
r.st_dev = out["st_dev"]
|
|
117
|
+
if "sigma_below" in out:
|
|
118
|
+
r.sigma_below = out["sigma_below"]
|
|
119
|
+
r.sigma_above = out["sigma_above"]
|
|
120
|
+
if "st_dev_above" in out:
|
|
121
|
+
r.st_dev_above = out["st_dev_above"]
|
|
122
|
+
if "st_dev_below" in out:
|
|
123
|
+
r.st_dev_below = out["st_dev_below"]
|
|
124
|
+
if "clean_w" in out:
|
|
125
|
+
r.clean_w = out["clean_w"]
|
|
126
|
+
# NOTE: rejected_y / original_y / clean_w / rejected_w / original_w are
|
|
127
|
+
# NOT set here — the C++ performRejection (non-bulk) leaves them at
|
|
128
|
+
# their default-empty state; only performBulkRejection populates them
|
|
129
|
+
# via setFinalVectors. Match that contract for parity.
|
|
130
|
+
|
|
131
|
+
def perform_bulk_rejection(
|
|
132
|
+
self,
|
|
133
|
+
y: Sequence[float],
|
|
134
|
+
w: Sequence[float] | None = None,
|
|
135
|
+
) -> None:
|
|
136
|
+
from rcrpy import rejection
|
|
137
|
+
y_arr = np.asarray(y, dtype=np.float64)
|
|
138
|
+
w_arr = None if w is None else np.asarray(w, dtype=np.float64)
|
|
139
|
+
out = rejection.performBulkRejection_LS(
|
|
140
|
+
y_arr, self.rejection_tech.value, w=w_arr,
|
|
141
|
+
non_parametric_model=(self.non_parametric_model
|
|
142
|
+
if self.mu_type is MuType.NONPARAMETRIC else None),
|
|
143
|
+
parametric_model=(self.parametric_model
|
|
144
|
+
if self.mu_type is MuType.PARAMETRIC else None),
|
|
145
|
+
)
|
|
146
|
+
r = self.result
|
|
147
|
+
r.mu = out["mu"]
|
|
148
|
+
r.flags = out["flags"]
|
|
149
|
+
r.indices = out["indices"]
|
|
150
|
+
r.clean_y = out["clean_y"]
|
|
151
|
+
r.rejected_y = out["rejected_y"]
|
|
152
|
+
r.original_y = out["original_y"]
|
|
153
|
+
r.st_dev_total = out["st_dev_total"]
|
|
154
|
+
r.st_dev_above = out["st_dev_above"]
|
|
155
|
+
r.st_dev_below = out["st_dev_below"]
|
|
156
|
+
# Single/Lower set sigma + st_dev; Each sets sigma_below/sigma_above.
|
|
157
|
+
if "sigma" in out:
|
|
158
|
+
r.sigma = out["sigma"]
|
|
159
|
+
if "st_dev" in out:
|
|
160
|
+
r.st_dev = out["st_dev"]
|
|
161
|
+
if "sigma_below" in out:
|
|
162
|
+
r.sigma_below = out["sigma_below"]
|
|
163
|
+
r.sigma_above = out["sigma_above"]
|
|
164
|
+
if "clean_w" in out:
|
|
165
|
+
r.clean_w = out["clean_w"]
|
|
166
|
+
if "rejected_w" in out:
|
|
167
|
+
r.rejected_w = out["rejected_w"]
|
|
168
|
+
if "original_w" in out:
|
|
169
|
+
r.original_w = out["original_w"]
|