fastsrs 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.
- fastsrs-0.1.0/LICENSE +21 -0
- fastsrs-0.1.0/PKG-INFO +363 -0
- fastsrs-0.1.0/README.md +332 -0
- fastsrs-0.1.0/pyproject.toml +51 -0
- fastsrs-0.1.0/setup.cfg +4 -0
- fastsrs-0.1.0/src/fastsrs/__init__.py +80 -0
- fastsrs-0.1.0/src/fastsrs/data.py +43 -0
- fastsrs-0.1.0/src/fastsrs/nobounds.py +1264 -0
- fastsrs-0.1.0/src/fastsrs/optimal.py +897 -0
- fastsrs-0.1.0/src/fastsrs/proprules.py +284 -0
- fastsrs-0.1.0/src/fastsrs/rashomon_epsilon.py +875 -0
- fastsrs-0.1.0/src/fastsrs/rashomon_nsize.py +875 -0
- fastsrs-0.1.0/src/fastsrs/two_step.py +1338 -0
- fastsrs-0.1.0/src/fastsrs/two_step_rashomon_epsilon.py +1354 -0
- fastsrs-0.1.0/src/fastsrs/util.py +452 -0
- fastsrs-0.1.0/src/fastsrs.egg-info/PKG-INFO +363 -0
- fastsrs-0.1.0/src/fastsrs.egg-info/SOURCES.txt +19 -0
- fastsrs-0.1.0/src/fastsrs.egg-info/dependency_links.txt +1 -0
- fastsrs-0.1.0/src/fastsrs.egg-info/requires.txt +11 -0
- fastsrs-0.1.0/src/fastsrs.egg-info/top_level.txt +1 -0
- fastsrs-0.1.0/tests/test_smoke.py +106 -0
fastsrs-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cristina Molero-Río, Boxuan Li, Tong Wang, and Cynthia Rudin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fastsrs-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastsrs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast Rashomon Sets of Sparse Rule Sets: learn short, accurate rule sets for binary classification with simulated annealing
|
|
5
|
+
Author: Tong Wang, Cynthia Rudin
|
|
6
|
+
Author-email: Cristina Molero-Río <mmolero@us.es>, Boxuan Li <bl3011@columbia.edu>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/mmolerous/FastSRS
|
|
9
|
+
Project-URL: Repository, https://github.com/mmolerous/FastSRS
|
|
10
|
+
Project-URL: Issues, https://github.com/mmolerous/FastSRS/issues
|
|
11
|
+
Keywords: rule sets,interpretable machine learning,Rashomon set,sparse models,simulated annealing
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy>=1.21
|
|
21
|
+
Requires-Dist: pandas>=1.3
|
|
22
|
+
Requires-Dist: scipy>=1.7
|
|
23
|
+
Requires-Dist: scikit-learn>=1.0
|
|
24
|
+
Requires-Dist: joblib>=1.0
|
|
25
|
+
Requires-Dist: pyfim>=6.28
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest; extra == "dev"
|
|
28
|
+
Requires-Dist: build; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# FastSRS
|
|
33
|
+
|
|
34
|
+
Source code for the paper *"Fast Rashomon Sets of Sparse Rule Sets"* by Cristina Molero-Río, Boxuan Li, Tong Wang, and Cynthia Rudin.
|
|
35
|
+
|
|
36
|
+
FastSRS learns short, accurate rule sets for binary classification using simulated annealing on a regularized objective. Two flavors are provided:
|
|
37
|
+
|
|
38
|
+
- **Optimal rule set** (`fastsrs.optimal.ORS`, also exported as `fastsrs.ORS`) — finds a single sparse rule set that minimizes `(1-acc) + c1·#conditions + c2·#rules + c3·#values`.
|
|
39
|
+
- **ε-Rashomon set** (`fastsrs.rashomon_epsilon.ORS`) — additionally collects every rule set visited by the SA loop so you can extract all rule sets within `(1+ε)` of the optimal objective.
|
|
40
|
+
|
|
41
|
+
A few research variants are also included; see [Package layout](#5-package-layout) and [Reproducibility](#6-reproducibility).
|
|
42
|
+
|
|
43
|
+
## 1. Installation
|
|
44
|
+
|
|
45
|
+
FastSRS is a regular Python package (`fastsrs`) and requires Python ≥ 3.9.
|
|
46
|
+
|
|
47
|
+
### From PyPI
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install fastsrs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From GitHub (latest development version)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install "git+https://github.com/mmolerous/FastSRS.git"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### From a local clone (for development)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/mmolerous/FastSRS.git
|
|
63
|
+
cd FastSRS
|
|
64
|
+
pip install -e ".[dev]" # editable install + pytest/build/twine
|
|
65
|
+
pytest # quick smoke tests on datasets/heart.csv
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Dependencies
|
|
69
|
+
|
|
70
|
+
`numpy`, `pandas`, `scipy`, `scikit-learn`, `joblib` and [`pyfim`](https://borgelt.net/pyfim.html) (the `fim` frequent-itemset miner) are installed automatically. `pyfim` ships as a C source distribution, so pip needs a C compiler to build it: `gcc`/`clang` on Linux and macOS, or the *Microsoft C++ Build Tools* on Windows. If you would rather not compile, install a prebuilt binary first and then install FastSRS on top:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
conda install -c conda-forge pyfim
|
|
74
|
+
pip install fastsrs
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The exact environment used for the paper's experiments is recorded in [`environment.yml`](environment.yml) / [`requirements.txt`](requirements.txt) (`conda env create -f environment.yml`); it is not needed to use the package.
|
|
78
|
+
|
|
79
|
+
## 2. Data format
|
|
80
|
+
|
|
81
|
+
FastSRS expects an all-binary CSV with a final integer `Class` column (0/1). Each non-target column is a 0/1 indicator. The included [`datasets/`](datasets/) directory has seven prepared datasets — `adult`, `compas`, `diabetes`, `fico`, `heart`, `invehicle`, `recidivism` — plus one simulated dataset.
|
|
82
|
+
|
|
83
|
+
The included CSVs follow these conventions for the indicator-column names:
|
|
84
|
+
|
|
85
|
+
- **Binary attribute** with values `{a, b}` ⇒ one column `attr_b` (the larger / last-sorted value) and its negation `attr_notb`.
|
|
86
|
+
- **Categorical attribute** with values `{a, b, c, …}` ⇒ a column `attr_v` and its negation `attr_notv` for *every* value `v`.
|
|
87
|
+
- **Numerical attribute** ⇒ for each of `Nlevel-1` quantile thresholds `t` (default 9 thresholds): `attr_<=t` and `attr_>t`.
|
|
88
|
+
|
|
89
|
+
## 3. Preparing your own data
|
|
90
|
+
|
|
91
|
+
`util.preprocess_data` converts a raw `pandas.DataFrame` (or a path to a CSV) into the binary format above. Columns are auto-detected by dtype unless you override them: 2-unique-value → binary, `object`/`category` → categorical, otherwise → numerical.
|
|
92
|
+
|
|
93
|
+
### Minimal example (clean numeric data)
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import pandas as pd
|
|
97
|
+
from fastsrs import preprocess_data
|
|
98
|
+
|
|
99
|
+
raw = pd.DataFrame({
|
|
100
|
+
'sex': [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], # binary
|
|
101
|
+
'cp': ['a','b','c','a','b','c','a','b','c','a'], # categorical (object dtype)
|
|
102
|
+
'age': [25, 30, 45, 50, 33, 60, 28, 41, 55, 38], # numerical
|
|
103
|
+
'Class':[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
df = preprocess_data(raw, target_col='Class', Nlevel=4)
|
|
107
|
+
# Optionally save: preprocess_data(raw, target_col='Class', output_path='datasets/mydata.csv')
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Resulting columns:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
sex_1, sex_not1,
|
|
114
|
+
cp_a, cp_nota, cp_b, cp_notb, cp_c, cp_notc,
|
|
115
|
+
age_<=30.75, age_>30.75, age_<=39.5, age_>39.5, age_<=48.75, age_>48.75,
|
|
116
|
+
Class
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Overriding auto-detection
|
|
120
|
+
|
|
121
|
+
Auto-detection treats numeric columns with > 2 unique values as numerical (quantile-binarized). If a numeric integer column is actually categorical (e.g. `cp` with codes 1/2/3/4 in the heart data), pass it explicitly:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
preprocess_data(
|
|
125
|
+
raw_df,
|
|
126
|
+
target_col='num',
|
|
127
|
+
binary=['sex', 'fbs', 'exang'],
|
|
128
|
+
categorical=['cp', 'restecg', 'slope', 'ca', 'thal'],
|
|
129
|
+
numerical=['age', 'trestbps', 'chol', 'thalach', 'oldpeak'],
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Non-integer class labels
|
|
134
|
+
|
|
135
|
+
Use `class_map` to map raw labels to 0/1:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
preprocess_data(raw, target_col='income',
|
|
139
|
+
class_map={' >50K': 1, ' <=50K': 0})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`class_map` accepts a dict or any callable (e.g. `lambda v: 0 if v == 0 else 1` to binarize a multi-class target).
|
|
143
|
+
|
|
144
|
+
### Other knobs
|
|
145
|
+
|
|
146
|
+
| parameter | default | purpose |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `Nlevel` | 10 | number of quantiles for numerical binarization (produces `Nlevel-1` thresholds) |
|
|
149
|
+
| `include_negations` | `True` | emit both `_v` and `_notv` (and both `_<=t` and `_>t`) |
|
|
150
|
+
| `dropna` | `True` | drop rows with NaN in any feature column |
|
|
151
|
+
| `missing_value`, `missing_label` | `None` | rename a category in the resulting column names after encoding (e.g. `' ?' → ' int'` for adult.csv) |
|
|
152
|
+
| `output_path` | `None` | if given, also write the result to this CSV path |
|
|
153
|
+
|
|
154
|
+
The `preprocess_data` function reproduces the existing `datasets/heart.csv` and `datasets/adult.csv` exactly (same shape, same cell values) when invoked with the matching options.
|
|
155
|
+
|
|
156
|
+
## 4. Usage
|
|
157
|
+
|
|
158
|
+
All examples below assume you have a binary CSV such as [`datasets/heart.csv`](datasets/heart.csv). `fastsrs.load_binary_csv` reads it into the `(X, y, col_ls)` triple that `ORS` expects and drops constant columns; the standalone scripts [`test_FastSRS.py`](test_FastSRS.py) and [`test_FastSRS_Rashomon_epsilon.py`](test_FastSRS_Rashomon_epsilon.py) show the equivalent manual pandas code and can be run directly from the repository root.
|
|
159
|
+
|
|
160
|
+
### 4.1 Optimal sparse rule set — [`test_FastSRS.py`](test_FastSRS.py)
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import numpy as np
|
|
164
|
+
import random
|
|
165
|
+
from fastsrs import * # ORS, load_binary_csv and the util helpers
|
|
166
|
+
|
|
167
|
+
# Read dataset
|
|
168
|
+
X, y, col_ls = load_binary_csv('datasets/heart.csv')
|
|
169
|
+
|
|
170
|
+
# Set parameters
|
|
171
|
+
supp, maxlen, Nrules = 5, 2, 2000
|
|
172
|
+
method = 'fpgrowth'
|
|
173
|
+
Niteration, q = 500, 0.25
|
|
174
|
+
c2 = 0.001; c1 = c2 / 4; c3 = c1 / 3
|
|
175
|
+
|
|
176
|
+
# Run FastSRS
|
|
177
|
+
random.seed(1); np.random.seed(1)
|
|
178
|
+
model = ORS(X, y, col_ls, method)
|
|
179
|
+
model.set_parameters(c1=c1, c2=c2, c3=c3)
|
|
180
|
+
model.set_fixed_bounds()
|
|
181
|
+
model.generate_rules(supp, maxlen, Nrules, method=method, criteria='precision')
|
|
182
|
+
grs, maps = model.train(Niteration, q, False)
|
|
183
|
+
merge_interval(model, grs)
|
|
184
|
+
merge_logical(model, grs)
|
|
185
|
+
|
|
186
|
+
# Print rules
|
|
187
|
+
print("\n===== RULE SET =====")
|
|
188
|
+
model.printMRS(grs)
|
|
189
|
+
|
|
190
|
+
# Compute metrics
|
|
191
|
+
Yhat = predict_MRS(grs, X)
|
|
192
|
+
TP, FP, TN, FN = getConfusion(Yhat, y)
|
|
193
|
+
acc = float(TP + TN) / (TP + TN + FP + FN)
|
|
194
|
+
nrules = calculate_rules(grs)
|
|
195
|
+
nconditions = calculate_conditions(grs)
|
|
196
|
+
nvalues = calculate_values(model, grs)
|
|
197
|
+
objvalue = (1 - acc) + model.c2 * nrules + model.c1 * nconditions + model.c3 * nvalues
|
|
198
|
+
|
|
199
|
+
print("\n===== RESULTS =====")
|
|
200
|
+
print('acc', acc)
|
|
201
|
+
print('nrules', nrules)
|
|
202
|
+
print('nconditions', nconditions)
|
|
203
|
+
print('nvalues', nvalues)
|
|
204
|
+
print('objvalue', objvalue)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Typical output on `heart` with the parameters above (≈7–10 rules, ≈86–88% training accuracy, objective ≈0.13–0.15):
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
===== RULE SET =====
|
|
211
|
+
rule 0:(ca:2.0),(thalach:<=170.0),
|
|
212
|
+
rule 1:(cp:4.0),(ca:not0.0),
|
|
213
|
+
rule 2:(thal:7.0),(oldpeak:>1.9),
|
|
214
|
+
...
|
|
215
|
+
|
|
216
|
+
===== RESULTS =====
|
|
217
|
+
acc 0.8619528619528619
|
|
218
|
+
nrules 7
|
|
219
|
+
nconditions 18
|
|
220
|
+
nvalues 18
|
|
221
|
+
objvalue 0.1510471380471381
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
> **Note on reproducibility.** `seed(1)` and `np.random.seed(1)` are set at the start of `train()` and `generate_rules()`, but rule screening uses `joblib.Parallel(n_jobs=-1)` and worker results are collected in completion order. The order of `self.rules` therefore varies slightly between runs, which can change which rule set the SA loop ends up with. Both runs give a valid optimal-or-near-optimal sparse rule set; exact numbers will differ from the snippet above.
|
|
225
|
+
|
|
226
|
+
### 4.2 ε-Rashomon set of sparse rule sets — [`test_FastSRS_Rashomon_epsilon.py`](test_FastSRS_Rashomon_epsilon.py)
|
|
227
|
+
|
|
228
|
+
`fastsrs.rashomon_epsilon.ORS.train()` returns one extra value, `Rset` — the list `[MRS, objective, accuracy]` for every iteration of the SA loop. `fastsrs.get_epsilon_rashomon(Rset, eps)` returns the unique rule sets whose objective is within `(1+eps)·best`.
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
import numpy as np
|
|
232
|
+
import random
|
|
233
|
+
from fastsrs import load_binary_csv, merge_interval, merge_logical, predict_MRS, getConfusion, \
|
|
234
|
+
calculate_rules, calculate_conditions, calculate_values, get_epsilon_rashomon, \
|
|
235
|
+
prediction_diversity, structural_diversity
|
|
236
|
+
from fastsrs.rashomon_epsilon import ORS # Rashomon-collecting variant of the learner
|
|
237
|
+
|
|
238
|
+
# Read dataset (same as above)
|
|
239
|
+
X, y, col_ls = load_binary_csv('datasets/heart.csv')
|
|
240
|
+
|
|
241
|
+
supp, maxlen, Nrules = 5, 2, 2000
|
|
242
|
+
method = 'fpgrowth'
|
|
243
|
+
Niteration, q = 500, 0.25
|
|
244
|
+
c2 = 0.001; c1 = c2 / 4; c3 = c1 / 3
|
|
245
|
+
|
|
246
|
+
random.seed(1); np.random.seed(1)
|
|
247
|
+
model = ORS(X, y, col_ls, method)
|
|
248
|
+
model.set_parameters(c1=c1, c2=c2, c3=c3)
|
|
249
|
+
model.set_fixed_bounds()
|
|
250
|
+
model.generate_rules(supp, maxlen, Nrules, method=method, criteria='precision')
|
|
251
|
+
grs, Rset, maps = model.train(Niteration, q, False) # NOTE: 3-tuple return
|
|
252
|
+
merge_interval(model, grs); merge_logical(model, grs)
|
|
253
|
+
|
|
254
|
+
# Optimal model
|
|
255
|
+
print("===== Optimal RULE SET =====")
|
|
256
|
+
model.printMRS(grs)
|
|
257
|
+
Yhat = predict_MRS(grs, X)
|
|
258
|
+
TP, FP, TN, FN = getConfusion(Yhat, y)
|
|
259
|
+
acc = float(TP + TN) / (TP + TN + FP + FN)
|
|
260
|
+
nrules = calculate_rules(grs)
|
|
261
|
+
nconditions = calculate_conditions(grs)
|
|
262
|
+
nvalues = calculate_values(model, grs)
|
|
263
|
+
objvalue = (1 - acc) + model.c2 * nrules + model.c1 * nconditions + model.c3 * nvalues
|
|
264
|
+
print("\n===== Metrics for the optimal RULE SET =====")
|
|
265
|
+
print('acc', acc); print('nrules', nrules)
|
|
266
|
+
print('nconditions', nconditions); print('nvalues', nvalues)
|
|
267
|
+
print('objvalue', objvalue)
|
|
268
|
+
|
|
269
|
+
# ε-Rashomon set
|
|
270
|
+
print("\n===== RASHOMON SET for a given ε =====")
|
|
271
|
+
eps = 0.05
|
|
272
|
+
print("ε:", eps)
|
|
273
|
+
Rset_eps = get_epsilon_rashomon(Rset, eps)
|
|
274
|
+
print("Size of the ε-Rashomon set:", len(Rset_eps))
|
|
275
|
+
|
|
276
|
+
# Compare two random members of the ε-Rashomon set
|
|
277
|
+
print("\n===== Metrics for two random models R1 and R2 from the ε-RASHOMON SET =====")
|
|
278
|
+
k1, k2 = random.sample(range(len(Rset_eps)), 2)
|
|
279
|
+
R1 = Rset_eps[k1][0] # each Rset entry is (rules, objective, 1-Error)
|
|
280
|
+
R2 = Rset_eps[k2][0]
|
|
281
|
+
merge_interval(model, R1); merge_logical(model, R1)
|
|
282
|
+
merge_interval(model, R2); merge_logical(model, R2)
|
|
283
|
+
|
|
284
|
+
print("===== R1 ====="); model.printMRS(R1)
|
|
285
|
+
print("===== R2 ====="); model.printMRS(R2)
|
|
286
|
+
print('Prediction diversity R1-R2:', prediction_diversity(R1, R2, X))
|
|
287
|
+
print('Structural diversity R1-R2:', structural_diversity(R1, R2))
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Typical output on `heart` (sizes vary slightly run-to-run, see note in §4.1):
|
|
291
|
+
|
|
292
|
+
```text
|
|
293
|
+
===== Metrics for the optimal RULE SET =====
|
|
294
|
+
acc ≈ 0.86
|
|
295
|
+
nrules ≈ 7
|
|
296
|
+
nconditions ≈ 18
|
|
297
|
+
nvalues ≈ 18
|
|
298
|
+
objvalue ≈ 0.15
|
|
299
|
+
|
|
300
|
+
===== RASHOMON SET for a given ε =====
|
|
301
|
+
ε: 0.05
|
|
302
|
+
Size of the ε-Rashomon set: ~15-20
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 4.3 Behavior under extreme regularization
|
|
306
|
+
|
|
307
|
+
If `c1+c2+c3` is so large that no rule satisfies the Theorem-1 minimum-negative-support bound, the rule miner produces nothing and `train()` short-circuits to an **empty rule set** (which predicts the majority/default class) with a warning. The Rashomon variants additionally return an empty Rashomon set. No exception is raised, and downstream helpers (`predict_MRS`, `calculate_*`, `get_epsilon_rashomon`) all handle the empty case cleanly.
|
|
308
|
+
|
|
309
|
+
## 5. Package layout
|
|
310
|
+
|
|
311
|
+
The learner lives in `src/fastsrs/`. Each research variant is a submodule that defines its own `ORS` class with the same interface; import the one you need.
|
|
312
|
+
|
|
313
|
+
| import | purpose |
|
|
314
|
+
|---|---|
|
|
315
|
+
| `from fastsrs import ORS` (= `fastsrs.optimal`) | optimal sparse rule set (main method) |
|
|
316
|
+
| `fastsrs.rashomon_epsilon` | ε-Rashomon set: `train()` also returns `Rset` |
|
|
317
|
+
| `fastsrs.rashomon_nsize` | size-bounded Rashomon set |
|
|
318
|
+
| `fastsrs.two_step` | two-step training (subsample warm-up + full data) |
|
|
319
|
+
| `fastsrs.two_step_rashomon_epsilon` | two-step trainer + ε-Rashomon-set collection, every iteration evaluated on the full data |
|
|
320
|
+
| `fastsrs.nobounds` | ablation: no Theorem-1/2 bounds during screening |
|
|
321
|
+
| `fastsrs.proprules` | rule-count statistics through the screening pipeline |
|
|
322
|
+
| `fastsrs.util` | `preprocess_data`, `predict_MRS`, `merge_interval`, `merge_logical`, `calculate_*`, diversity measures, `get_epsilon_rashomon` (all re-exported from `fastsrs`) |
|
|
323
|
+
| `fastsrs.data` | `load_binary_csv` |
|
|
324
|
+
|
|
325
|
+
`pip install -e .` from a clone installs the package in editable mode, so edits under `src/fastsrs/` are picked up without reinstalling. `pytest` runs the smoke tests in [`tests/`](tests/); `python -m build` produces the sdist and wheel in `dist/`.
|
|
326
|
+
|
|
327
|
+
## 6. Reproducibility
|
|
328
|
+
|
|
329
|
+
To replicate the results from the main paper, install the package (see [Installation](#1-installation)) and run the following scripts from a clone of the repository. Update the `pathcode` variable at the top of each file to your local path, and the `dataname` variable to the dataset you want to run.
|
|
330
|
+
|
|
331
|
+
| script | purpose |
|
|
332
|
+
|---|---|
|
|
333
|
+
| `run_FastSRS.py` | optimal sparse rule set (main results) |
|
|
334
|
+
| `run_FastSRS_two_step.py` | optimal model with two-step training (subsample warm-up + full data) |
|
|
335
|
+
| `run_FastSRS_nobounds.py` | ablation: no Theorem-1/2 bounds during screening |
|
|
336
|
+
| `run_FastSRS_proportionrules.py` | rule-count statistics through the screening pipeline |
|
|
337
|
+
| `run_FastSRS_robustness_study_features.py` | robustness study, perturbing features |
|
|
338
|
+
| `run_FastSRS_robustness_study_class.py` | robustness study, perturbing labels |
|
|
339
|
+
| `run_FastSRS_Rashomon_epsilon.py` | ε-Rashomon set of sparse rule sets |
|
|
340
|
+
| `run_FastSRS_Rashomon_nsize.py` | size-bounded Rashomon set |
|
|
341
|
+
|
|
342
|
+
The variant module `fastsrs.two_step_rashomon_epsilon` combines the two-step trainer with ε-Rashomon-set collection, evaluating every iteration on the full data so phase-1 (subsample) and phase-2 (full) entries are directly comparable.
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
This project is released under the [MIT License](LICENSE).
|
|
347
|
+
|
|
348
|
+
## Contact
|
|
349
|
+
|
|
350
|
+
- Cristina Molero-Río (mmolero@us.es)
|
|
351
|
+
- Boxuan Li (bl3011@columbia.edu)
|
|
352
|
+
|
|
353
|
+
## Citing this work
|
|
354
|
+
|
|
355
|
+
If you find this work useful, please consider citing it.
|
|
356
|
+
|
|
357
|
+
<!--
|
|
358
|
+
```bibtex
|
|
359
|
+
@inproceedings{example,
|
|
360
|
+
title={Example},
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
-->
|