rulewise 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.
- rulewise-0.1.0/CHANGELOG.md +38 -0
- rulewise-0.1.0/LICENSE +21 -0
- rulewise-0.1.0/MANIFEST.in +4 -0
- rulewise-0.1.0/PKG-INFO +273 -0
- rulewise-0.1.0/README.md +229 -0
- rulewise-0.1.0/benchmarks/COMPARISON.md +115 -0
- rulewise-0.1.0/benchmarks/PRE_PUBLISH_REVIEW.md +228 -0
- rulewise-0.1.0/benchmarks/RESULTS.md +153 -0
- rulewise-0.1.0/benchmarks/classifier_eval.py +54 -0
- rulewise-0.1.0/benchmarks/data/README.md +14 -0
- rulewise-0.1.0/benchmarks/error_control_simulation.py +35 -0
- rulewise-0.1.0/benchmarks/get_data.py +40 -0
- rulewise-0.1.0/benchmarks/library_comparison.py +668 -0
- rulewise-0.1.0/benchmarks/power_study.py +45 -0
- rulewise-0.1.0/benchmarks/power_study_multiseed.py +24 -0
- rulewise-0.1.0/benchmarks/real_classification.py +63 -0
- rulewise-0.1.0/benchmarks/replication_groceries.py +85 -0
- rulewise-0.1.0/docs/api.md +402 -0
- rulewise-0.1.0/docs/comparison.md +160 -0
- rulewise-0.1.0/docs/img/groceries_effect.png +0 -0
- rulewise-0.1.0/docs/img/groceries_forest.png +0 -0
- rulewise-0.1.0/docs/img/groceries_network.png +0 -0
- rulewise-0.1.0/docs/img/groceries_validation.png +0 -0
- rulewise-0.1.0/docs/index.md +47 -0
- rulewise-0.1.0/docs/limitations.md +147 -0
- rulewise-0.1.0/docs/methodology.md +196 -0
- rulewise-0.1.0/docs/publishing.md +152 -0
- rulewise-0.1.0/docs/requirements.txt +2 -0
- rulewise-0.1.0/docs/tutorials/classification.md +95 -0
- rulewise-0.1.0/docs/tutorials/groceries.md +135 -0
- rulewise-0.1.0/mkdocs.yml +30 -0
- rulewise-0.1.0/pyproject.toml +43 -0
- rulewise-0.1.0/setup.cfg +4 -0
- rulewise-0.1.0/src/rulewise/__init__.py +9 -0
- rulewise-0.1.0/src/rulewise/_compat.py +36 -0
- rulewise-0.1.0/src/rulewise/classifier.py +257 -0
- rulewise-0.1.0/src/rulewise/classmining.py +76 -0
- rulewise-0.1.0/src/rulewise/io.py +98 -0
- rulewise-0.1.0/src/rulewise/miner.py +329 -0
- rulewise-0.1.0/src/rulewise/mining.py +109 -0
- rulewise-0.1.0/src/rulewise/plot.py +179 -0
- rulewise-0.1.0/src/rulewise/report.py +45 -0
- rulewise-0.1.0/src/rulewise/rules.py +145 -0
- rulewise-0.1.0/src/rulewise/rulesmixin.py +133 -0
- rulewise-0.1.0/src/rulewise/stats.py +135 -0
- rulewise-0.1.0/src/rulewise.egg-info/PKG-INFO +273 -0
- rulewise-0.1.0/src/rulewise.egg-info/SOURCES.txt +58 -0
- rulewise-0.1.0/src/rulewise.egg-info/dependency_links.txt +1 -0
- rulewise-0.1.0/src/rulewise.egg-info/requires.txt +21 -0
- rulewise-0.1.0/src/rulewise.egg-info/top_level.txt +1 -0
- rulewise-0.1.0/tests/__init__.py +0 -0
- rulewise-0.1.0/tests/test_backend_io.py +133 -0
- rulewise-0.1.0/tests/test_ci.py +49 -0
- rulewise-0.1.0/tests/test_class_conditional.py +65 -0
- rulewise-0.1.0/tests/test_classifier.py +156 -0
- rulewise-0.1.0/tests/test_m1.py +70 -0
- rulewise-0.1.0/tests/test_m4.py +138 -0
- rulewise-0.1.0/tests/test_miner.py +79 -0
- rulewise-0.1.0/tests/test_mining.py +22 -0
- rulewise-0.1.0/tests/test_stats.py +59 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-11-12)
|
|
4
|
+
|
|
5
|
+
First public version.
|
|
6
|
+
|
|
7
|
+
* `RuleMiner`: mining (bitset Eclat; optional PyFIM backend), exact one-sided Fisher tests,
|
|
8
|
+
Webb productivity test, Holm / Bonferroni / Benjamini-Hochberg / Benjamini-Yekutieli
|
|
9
|
+
correction, holdout mode and direct mode (layered critical values), Wilson and bootstrap
|
|
10
|
+
intervals, multi-item consequents, stability and null-calibration validation.
|
|
11
|
+
* `RuleClassifier`: class-association rules, class-conditional mining, optional negated items,
|
|
12
|
+
probabilities from the best matching rule, `explain`, permuted-label and cross-validated
|
|
13
|
+
validation.
|
|
14
|
+
* scikit-learn compatibility; input from lists, numpy, scipy sparse, pandas and polars.
|
|
15
|
+
* `query`, `export` (csv/json/parquet), `to_polars`, `summary`, plots.
|
|
16
|
+
* Documentation site under `docs/`, with an API reference, methodology, limitations,
|
|
17
|
+
comparison, two tutorials and a release guide. Build with `python -m mkdocs build --strict`.
|
|
18
|
+
* Known limits are documented in `docs/limitations.md` and `benchmarks/RESULTS.md`.
|
|
19
|
+
Head-to-head evidence against mlxtend is in `benchmarks/COMPARISON.md`, with a
|
|
20
|
+
pre-publication review in `benchmarks/PRE_PUBLISH_REVIEW.md`.
|
|
21
|
+
|
|
22
|
+
### Packaging
|
|
23
|
+
|
|
24
|
+
* Removed the `[fast]` extra and `pyfim` from `[dev]`: PyFIM has no wheel for modern Python
|
|
25
|
+
and failed to build on Python 3.12/Windows. The `backend="pyfim"` option remains for users
|
|
26
|
+
who install it themselves; the pure-Python backend is the default.
|
|
27
|
+
* Benchmark data is local and reproducible via `benchmarks/get_data.py`.
|
|
28
|
+
|
|
29
|
+
### Fixes
|
|
30
|
+
|
|
31
|
+
* `RuleMiner.transform` no longer crashes when a fit returns zero rules; it returns an
|
|
32
|
+
`(n, 0)` matrix and the empty `rules_` table keeps the standard columns.
|
|
33
|
+
* `predict`/`transform` before `fit` raise scikit-learn's `NotFittedError`.
|
|
34
|
+
* Matrix input sets `n_features_in_` and checks the column count after fit; complex data,
|
|
35
|
+
NaN/inf and zero-width matrices are rejected with clear errors.
|
|
36
|
+
* Optional-dependency tests now skip individually, so the pure-Python backend and the input
|
|
37
|
+
adapters are tested even when PyFIM, scikit-learn, matplotlib, mlxtend or statsmodels are
|
|
38
|
+
absent.
|
rulewise-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adnan Mohamud
|
|
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.
|
rulewise-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rulewise
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Association rule mining with statistically sound validation
|
|
5
|
+
Author: Adnan Mohamud
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ShadowGodd1/Rulewise
|
|
8
|
+
Project-URL: Repository, https://github.com/ShadowGodd1/Rulewise
|
|
9
|
+
Project-URL: Issues, https://github.com/ShadowGodd1/Rulewise/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/ShadowGodd1/Rulewise/tree/main/docs
|
|
11
|
+
Project-URL: Changelog, https://github.com/ShadowGodd1/Rulewise/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: association rules,pattern mining,multiple testing,frequent itemsets
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy>=1.23
|
|
27
|
+
Requires-Dist: scipy>=1.9
|
|
28
|
+
Requires-Dist: pandas>=1.5
|
|
29
|
+
Provides-Extra: plot
|
|
30
|
+
Requires-Dist: matplotlib; extra == "plot"
|
|
31
|
+
Provides-Extra: sklearn
|
|
32
|
+
Requires-Dist: scikit-learn; extra == "sklearn"
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
35
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
38
|
+
Requires-Dist: statsmodels; extra == "dev"
|
|
39
|
+
Requires-Dist: mlxtend; extra == "dev"
|
|
40
|
+
Requires-Dist: polars; extra == "dev"
|
|
41
|
+
Requires-Dist: scikit-learn; extra == "dev"
|
|
42
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# rulewise
|
|
46
|
+
|
|
47
|
+
Repository: https://github.com/ShadowGodd1/Rulewise
|
|
48
|
+
Documentation: https://shadowgodd1.github.io/Rulewise/
|
|
49
|
+
|
|
50
|
+
Association rule mining you can trust: every rule that comes back has survived exact statistical
|
|
51
|
+
testing, correction for the number of rules tried, and a test that it still beats simpler versions
|
|
52
|
+
of itself. The library also reports how far to trust the result.
|
|
53
|
+
|
|
54
|
+
Classical rule mining returns thousands of rules ranked by support and confidence. Many are
|
|
55
|
+
redundant, explained by a simpler rule; on pure noise, all of them are chance. rulewise mines the
|
|
56
|
+
candidates, tests each one with an exact test, corrects for the number of rules tried, drops rules
|
|
57
|
+
whose extra antecedent items add nothing over a simpler version of the same rule, and reports
|
|
58
|
+
validation metrics. On data with no real structure it almost never returns a rule: across 1,800
|
|
59
|
+
pure-noise datasets it reported a spurious rule in at most 1.7% of them (nominal level 5%).
|
|
60
|
+
|
|
61
|
+
The guarantee is conservative. The price of almost never reporting a spurious rule is that weak
|
|
62
|
+
associations need more data to be detected, and recall is lower than a classical list. See
|
|
63
|
+
"Limitations" below and `docs/limitations.md`.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install rulewise # core, pure-Python Eclat
|
|
69
|
+
pip install "rulewise[sklearn]" # plus scikit-learn integration
|
|
70
|
+
pip install "rulewise[plot]" # plus matplotlib plots
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
PyFIM is an optional legacy C backend (`backend="pyfim"`). It has no wheel for modern Python, so
|
|
74
|
+
it is not installed by default. Install it manually if your platform can build it; the pure-Python
|
|
75
|
+
backend is the default.
|
|
76
|
+
|
|
77
|
+
## Find rules
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from rulewise import RuleMiner
|
|
81
|
+
|
|
82
|
+
baskets = [["milk", "bread", "butter"], ["beer", "chips"], ...] # or a 0/1 matrix,
|
|
83
|
+
# scipy sparse, pandas, polars
|
|
84
|
+
miner = RuleMiner(min_support=0.01).fit(baskets)
|
|
85
|
+
|
|
86
|
+
print(miner.summary())
|
|
87
|
+
miner.rules_ # DataFrame of significant rules and their measures
|
|
88
|
+
miner.query(min_lift=2, contains="milk", sort_by="lift", top=10)
|
|
89
|
+
miner.recommend(["milk"], k=5, explain=True)
|
|
90
|
+
miner.export("rules.csv") # .csv, .json or .parquet
|
|
91
|
+
miner.to_polars() # requires polars
|
|
92
|
+
miner.plot("effect") # also "forest" and "network"; requires matplotlib
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`RuleMiner` splits the data by default: it finds candidates on one part and tests and measures
|
|
96
|
+
them on the other (`mode="holdout"`). That makes the reported confidence, lift and intervals
|
|
97
|
+
honest. Set `mode="direct"` to use all data and Webb's layered critical values; direct mode is
|
|
98
|
+
usually more powerful on small data, but its effect sizes are measured on the same data that
|
|
99
|
+
selected the rules and are therefore optimistic. See "Modes" below.
|
|
100
|
+
|
|
101
|
+
## What comes back
|
|
102
|
+
|
|
103
|
+
`miner.rules_` contains one row per significant rule:
|
|
104
|
+
|
|
105
|
+
| column | meaning |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `antecedent`, `consequent` | tuples of item names |
|
|
108
|
+
| `support` | share of transactions containing antecedent and consequent |
|
|
109
|
+
| `confidence` | P(consequent present given antecedent present) |
|
|
110
|
+
| `lift` | confidence divided by the consequent's base rate |
|
|
111
|
+
| `leverage` | support minus the support expected under independence |
|
|
112
|
+
| `conviction` | how much more confident the rule is than a rule with no antecedent |
|
|
113
|
+
| `count`, `count_antecedent`, `count_consequent` | raw counts |
|
|
114
|
+
| `p_value` | exact productivity p-value |
|
|
115
|
+
| `p_adjusted` | p-value after the chosen correction |
|
|
116
|
+
| `confidence_low`, `confidence_high` | Wilson interval on confidence |
|
|
117
|
+
| `lift_low`, `lift_high` | bootstrap interval on lift (when `n_bootstrap > 0`) |
|
|
118
|
+
| `leverage_low`, `leverage_high` | bootstrap interval on leverage (when `n_bootstrap > 0`) |
|
|
119
|
+
|
|
120
|
+
In holdout mode all measures are computed on the holdout part. In direct mode they are computed
|
|
121
|
+
on the full data and are in-sample estimates.
|
|
122
|
+
|
|
123
|
+
## Validate the pipeline
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
report = miner.validate(baskets, n_null=50, n_stability=20)
|
|
127
|
+
print(report)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`validate` refits the pipeline on shuffled data with the same item frequencies. A sound pipeline
|
|
131
|
+
returns almost nothing there; the share of null datasets with at least one rule estimates the
|
|
132
|
+
empirical family-wise error rate. It also reports holdout replication and, if `n_stability > 0`,
|
|
133
|
+
how stable the rule set is across random subsamples. The result is a `ValidationReport`, a dict
|
|
134
|
+
with a readable `summary()`.
|
|
135
|
+
|
|
136
|
+
## Classify with rules
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from rulewise import RuleClassifier
|
|
140
|
+
|
|
141
|
+
clf = RuleClassifier(min_support=0.01).fit(X, y) # X: transactions, 0/1 matrix or sparse
|
|
142
|
+
clf.predict(X_new)
|
|
143
|
+
clf.predict_proba(X_new)
|
|
144
|
+
clf.score(X_test, y_test)
|
|
145
|
+
clf.explain(X_new[:1]) # the rule behind each prediction
|
|
146
|
+
clf.validate(X, y, cv=5) # permuted-label null plus cross-validation
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`RuleClassifier` mines rules of the form `antecedent -> class`, keeps only significant ones, and
|
|
150
|
+
predicts with the best matching rule ordered by the Wilson lower bound on confidence. When no
|
|
151
|
+
rule matches, it returns the class priors. It works in scikit-learn pipelines and
|
|
152
|
+
cross-validation.
|
|
153
|
+
|
|
154
|
+
Numeric features must be binned first, for example with
|
|
155
|
+
`KBinsDiscretizer(encode="onehot")`. `add_negations=True` adds a `("not", item)` item for every
|
|
156
|
+
feature a row lacks, so rules can express "feature absent".
|
|
157
|
+
|
|
158
|
+
## How it works
|
|
159
|
+
|
|
160
|
+
1. **Mine** frequent itemsets with a bitset Eclat implementation, or PyFIM when installed.
|
|
161
|
+
2. **Generate candidates** `X -> Y` with confidence above `min_confidence` and lift above 1.
|
|
162
|
+
3. **Test productivity.** A rule must beat every simpler version of itself: for every proper
|
|
163
|
+
subset `Z` of `X`, adding `X - Z` must raise the rate of `Y`. Each comparison is a one-sided
|
|
164
|
+
Fisher exact test; the rule's p-value is the maximum over comparisons, so a rule is
|
|
165
|
+
significant only if all comparisons are. Rules with multi-item consequents must also show
|
|
166
|
+
positive dependence across every bipartition of the itemset.
|
|
167
|
+
4. **Correct for multiple testing.** Holm or Bonferroni control the family-wise error rate;
|
|
168
|
+
Benjamini-Hochberg or Benjamini-Yekutieli control the false discovery rate.
|
|
169
|
+
5. **Estimate honestly.** Holdout mode finds rules on one part and tests and measures them on the
|
|
170
|
+
other. Direct mode uses all data and Webb's layered critical values.
|
|
171
|
+
|
|
172
|
+
## Modes
|
|
173
|
+
|
|
174
|
+
| mode | where candidates are found | where they are tested and measured | use when |
|
|
175
|
+
|---|---|---|---|
|
|
176
|
+
| `holdout` (default for `RuleMiner`) | exploration part | independent holdout part | you need honest effect sizes and intervals |
|
|
177
|
+
| `direct` (default for `RuleClassifier`) | all data | all data, with layered critical values | data is small, or you need maximum power, and you accept optimistic effect sizes |
|
|
178
|
+
|
|
179
|
+
In direct mode, `alpha` is split equally across rule sizes and Bonferroni-corrected within each
|
|
180
|
+
size over the full search space. The `correction` parameter is ignored. In holdout mode,
|
|
181
|
+
`holdout_fraction=0` disables the split; p-values are then not valid and a warning is raised.
|
|
182
|
+
|
|
183
|
+
## Statistical guarantees and assumptions
|
|
184
|
+
|
|
185
|
+
The guarantee is family-wise or false-discovery control over the rules actually tested, under
|
|
186
|
+
these assumptions:
|
|
187
|
+
|
|
188
|
+
* transactions are independent and identically distributed;
|
|
189
|
+
* the null model is independence between the items in a rule;
|
|
190
|
+
* in holdout mode, the holdout part is independent of the part used to select candidates;
|
|
191
|
+
* in direct mode, the correction covers the full search space, not only the candidates that
|
|
192
|
+
happened to pass the other filters.
|
|
193
|
+
|
|
194
|
+
The testing is conservative, which is why the null-data error rate is well below 5%. It does not
|
|
195
|
+
model dependence caused by basket size or by other variables. Items can be associated only
|
|
196
|
+
because some baskets are larger, or because a third variable drives both, and such rules can be
|
|
197
|
+
reported. It also does not establish causation. See `docs/methodology.md` and
|
|
198
|
+
`docs/limitations.md`.
|
|
199
|
+
|
|
200
|
+
## Input formats
|
|
201
|
+
|
|
202
|
+
`RuleMiner` and `RuleClassifier` accept:
|
|
203
|
+
|
|
204
|
+
* an iterable of item collections, including a generator;
|
|
205
|
+
* a 2D numpy array or a 0/1 matrix (any numeric dtype, nonzero means present);
|
|
206
|
+
* a scipy sparse matrix;
|
|
207
|
+
* a pandas DataFrame (dense or sparse one-hot) or a Series of lists;
|
|
208
|
+
* a polars DataFrame (one-hot or a single list column) or a Series of lists.
|
|
209
|
+
|
|
210
|
+
Use `item_names` to label matrix columns with strings. Strings as transactions are rejected
|
|
211
|
+
because a single string is ambiguous; split or encode them first. Matrix input rejects NaN,
|
|
212
|
+
infinity, complex numbers and zero-width data.
|
|
213
|
+
|
|
214
|
+
## When to use it, and when not to
|
|
215
|
+
|
|
216
|
+
Good for:
|
|
217
|
+
|
|
218
|
+
* sparse transaction-style or categorical data where a few rules carry the structure;
|
|
219
|
+
* explainable models and auditable discovery;
|
|
220
|
+
* separating real associations from noise in small or exploratory samples;
|
|
221
|
+
* reporting an interval and a p-value with every rule.
|
|
222
|
+
|
|
223
|
+
Weak for:
|
|
224
|
+
|
|
225
|
+
* signal spread thinly over many dense numeric features; bin first, and expect low accuracy
|
|
226
|
+
compared with regularized linear models or trees;
|
|
227
|
+
* maximum recall: a classical list contains more true rules, mixed with more redundancy;
|
|
228
|
+
* recommendation hit rate: a popularity baseline can beat rule-based recommendations;
|
|
229
|
+
* negative associations, closed or maximal itemsets, constrained mining, and the full metric
|
|
230
|
+
catalogue that mlxtend provides.
|
|
231
|
+
|
|
232
|
+
Measured results and the head-to-head against mlxtend are in `benchmarks/COMPARISON.md`. The
|
|
233
|
+
methodology and limits are in `docs/methodology.md` and `docs/limitations.md`.
|
|
234
|
+
|
|
235
|
+
## Documentation
|
|
236
|
+
|
|
237
|
+
* `docs/index.md`: documentation home.
|
|
238
|
+
* `docs/api.md`: parameters, attributes, methods and output columns.
|
|
239
|
+
* `docs/methodology.md`: the statistical method and its assumptions.
|
|
240
|
+
* `docs/limitations.md`: known failure modes and power.
|
|
241
|
+
* `docs/comparison.md`: rulewise compared with existing Python libraries.
|
|
242
|
+
* `docs/tutorials/groceries.md`: a real supermarket basket walkthrough.
|
|
243
|
+
* `docs/tutorials/classification.md`: rules as a classifier on Mushrooms and Titanic.
|
|
244
|
+
* `docs/publishing.md`: release steps.
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pip install -e ".[dev]"
|
|
250
|
+
python benchmarks/get_data.py
|
|
251
|
+
python benchmarks/library_comparison.py --section all --runs 500 --seeds 5
|
|
252
|
+
python -m pytest
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The test suite checks p-values and corrections against scipy and statsmodels, itemsets against
|
|
256
|
+
mlxtend and across backends, error control by simulation on null data, and recovery of planted
|
|
257
|
+
rules. Optional-dependency tests skip when the dependency is absent, so the core install is
|
|
258
|
+
tested without PyFIM, scikit-learn, matplotlib, mlxtend, statsmodels or polars.
|
|
259
|
+
|
|
260
|
+
## References
|
|
261
|
+
|
|
262
|
+
* Webb, G.I. (2007). Discovering significant patterns. Machine Learning 68(1).
|
|
263
|
+
* Webb, G.I. (2008). Layered critical values: a powerful direct-adjustment approach to
|
|
264
|
+
discovering significant patterns. Machine Learning 71.
|
|
265
|
+
* Hamalainen, W. and Webb, G.I. (2019). A tutorial on statistically sound pattern discovery.
|
|
266
|
+
Data Mining and Knowledge Discovery 33.
|
|
267
|
+
* Webb, G.I. (2010). Self-sufficient itemsets. ACM TKDD 4(1).
|
|
268
|
+
* Holm (1979); Benjamini and Hochberg (1995); Benjamini and Yekutieli (2001).
|
|
269
|
+
* Borgelt, C. PyFIM and Eclat (MIT licence).
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT. See `LICENSE`.
|
rulewise-0.1.0/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# rulewise
|
|
2
|
+
|
|
3
|
+
Repository: https://github.com/ShadowGodd1/Rulewise
|
|
4
|
+
Documentation: https://shadowgodd1.github.io/Rulewise/
|
|
5
|
+
|
|
6
|
+
Association rule mining you can trust: every rule that comes back has survived exact statistical
|
|
7
|
+
testing, correction for the number of rules tried, and a test that it still beats simpler versions
|
|
8
|
+
of itself. The library also reports how far to trust the result.
|
|
9
|
+
|
|
10
|
+
Classical rule mining returns thousands of rules ranked by support and confidence. Many are
|
|
11
|
+
redundant, explained by a simpler rule; on pure noise, all of them are chance. rulewise mines the
|
|
12
|
+
candidates, tests each one with an exact test, corrects for the number of rules tried, drops rules
|
|
13
|
+
whose extra antecedent items add nothing over a simpler version of the same rule, and reports
|
|
14
|
+
validation metrics. On data with no real structure it almost never returns a rule: across 1,800
|
|
15
|
+
pure-noise datasets it reported a spurious rule in at most 1.7% of them (nominal level 5%).
|
|
16
|
+
|
|
17
|
+
The guarantee is conservative. The price of almost never reporting a spurious rule is that weak
|
|
18
|
+
associations need more data to be detected, and recall is lower than a classical list. See
|
|
19
|
+
"Limitations" below and `docs/limitations.md`.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install rulewise # core, pure-Python Eclat
|
|
25
|
+
pip install "rulewise[sklearn]" # plus scikit-learn integration
|
|
26
|
+
pip install "rulewise[plot]" # plus matplotlib plots
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
PyFIM is an optional legacy C backend (`backend="pyfim"`). It has no wheel for modern Python, so
|
|
30
|
+
it is not installed by default. Install it manually if your platform can build it; the pure-Python
|
|
31
|
+
backend is the default.
|
|
32
|
+
|
|
33
|
+
## Find rules
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from rulewise import RuleMiner
|
|
37
|
+
|
|
38
|
+
baskets = [["milk", "bread", "butter"], ["beer", "chips"], ...] # or a 0/1 matrix,
|
|
39
|
+
# scipy sparse, pandas, polars
|
|
40
|
+
miner = RuleMiner(min_support=0.01).fit(baskets)
|
|
41
|
+
|
|
42
|
+
print(miner.summary())
|
|
43
|
+
miner.rules_ # DataFrame of significant rules and their measures
|
|
44
|
+
miner.query(min_lift=2, contains="milk", sort_by="lift", top=10)
|
|
45
|
+
miner.recommend(["milk"], k=5, explain=True)
|
|
46
|
+
miner.export("rules.csv") # .csv, .json or .parquet
|
|
47
|
+
miner.to_polars() # requires polars
|
|
48
|
+
miner.plot("effect") # also "forest" and "network"; requires matplotlib
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`RuleMiner` splits the data by default: it finds candidates on one part and tests and measures
|
|
52
|
+
them on the other (`mode="holdout"`). That makes the reported confidence, lift and intervals
|
|
53
|
+
honest. Set `mode="direct"` to use all data and Webb's layered critical values; direct mode is
|
|
54
|
+
usually more powerful on small data, but its effect sizes are measured on the same data that
|
|
55
|
+
selected the rules and are therefore optimistic. See "Modes" below.
|
|
56
|
+
|
|
57
|
+
## What comes back
|
|
58
|
+
|
|
59
|
+
`miner.rules_` contains one row per significant rule:
|
|
60
|
+
|
|
61
|
+
| column | meaning |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `antecedent`, `consequent` | tuples of item names |
|
|
64
|
+
| `support` | share of transactions containing antecedent and consequent |
|
|
65
|
+
| `confidence` | P(consequent present given antecedent present) |
|
|
66
|
+
| `lift` | confidence divided by the consequent's base rate |
|
|
67
|
+
| `leverage` | support minus the support expected under independence |
|
|
68
|
+
| `conviction` | how much more confident the rule is than a rule with no antecedent |
|
|
69
|
+
| `count`, `count_antecedent`, `count_consequent` | raw counts |
|
|
70
|
+
| `p_value` | exact productivity p-value |
|
|
71
|
+
| `p_adjusted` | p-value after the chosen correction |
|
|
72
|
+
| `confidence_low`, `confidence_high` | Wilson interval on confidence |
|
|
73
|
+
| `lift_low`, `lift_high` | bootstrap interval on lift (when `n_bootstrap > 0`) |
|
|
74
|
+
| `leverage_low`, `leverage_high` | bootstrap interval on leverage (when `n_bootstrap > 0`) |
|
|
75
|
+
|
|
76
|
+
In holdout mode all measures are computed on the holdout part. In direct mode they are computed
|
|
77
|
+
on the full data and are in-sample estimates.
|
|
78
|
+
|
|
79
|
+
## Validate the pipeline
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
report = miner.validate(baskets, n_null=50, n_stability=20)
|
|
83
|
+
print(report)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`validate` refits the pipeline on shuffled data with the same item frequencies. A sound pipeline
|
|
87
|
+
returns almost nothing there; the share of null datasets with at least one rule estimates the
|
|
88
|
+
empirical family-wise error rate. It also reports holdout replication and, if `n_stability > 0`,
|
|
89
|
+
how stable the rule set is across random subsamples. The result is a `ValidationReport`, a dict
|
|
90
|
+
with a readable `summary()`.
|
|
91
|
+
|
|
92
|
+
## Classify with rules
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from rulewise import RuleClassifier
|
|
96
|
+
|
|
97
|
+
clf = RuleClassifier(min_support=0.01).fit(X, y) # X: transactions, 0/1 matrix or sparse
|
|
98
|
+
clf.predict(X_new)
|
|
99
|
+
clf.predict_proba(X_new)
|
|
100
|
+
clf.score(X_test, y_test)
|
|
101
|
+
clf.explain(X_new[:1]) # the rule behind each prediction
|
|
102
|
+
clf.validate(X, y, cv=5) # permuted-label null plus cross-validation
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`RuleClassifier` mines rules of the form `antecedent -> class`, keeps only significant ones, and
|
|
106
|
+
predicts with the best matching rule ordered by the Wilson lower bound on confidence. When no
|
|
107
|
+
rule matches, it returns the class priors. It works in scikit-learn pipelines and
|
|
108
|
+
cross-validation.
|
|
109
|
+
|
|
110
|
+
Numeric features must be binned first, for example with
|
|
111
|
+
`KBinsDiscretizer(encode="onehot")`. `add_negations=True` adds a `("not", item)` item for every
|
|
112
|
+
feature a row lacks, so rules can express "feature absent".
|
|
113
|
+
|
|
114
|
+
## How it works
|
|
115
|
+
|
|
116
|
+
1. **Mine** frequent itemsets with a bitset Eclat implementation, or PyFIM when installed.
|
|
117
|
+
2. **Generate candidates** `X -> Y` with confidence above `min_confidence` and lift above 1.
|
|
118
|
+
3. **Test productivity.** A rule must beat every simpler version of itself: for every proper
|
|
119
|
+
subset `Z` of `X`, adding `X - Z` must raise the rate of `Y`. Each comparison is a one-sided
|
|
120
|
+
Fisher exact test; the rule's p-value is the maximum over comparisons, so a rule is
|
|
121
|
+
significant only if all comparisons are. Rules with multi-item consequents must also show
|
|
122
|
+
positive dependence across every bipartition of the itemset.
|
|
123
|
+
4. **Correct for multiple testing.** Holm or Bonferroni control the family-wise error rate;
|
|
124
|
+
Benjamini-Hochberg or Benjamini-Yekutieli control the false discovery rate.
|
|
125
|
+
5. **Estimate honestly.** Holdout mode finds rules on one part and tests and measures them on the
|
|
126
|
+
other. Direct mode uses all data and Webb's layered critical values.
|
|
127
|
+
|
|
128
|
+
## Modes
|
|
129
|
+
|
|
130
|
+
| mode | where candidates are found | where they are tested and measured | use when |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `holdout` (default for `RuleMiner`) | exploration part | independent holdout part | you need honest effect sizes and intervals |
|
|
133
|
+
| `direct` (default for `RuleClassifier`) | all data | all data, with layered critical values | data is small, or you need maximum power, and you accept optimistic effect sizes |
|
|
134
|
+
|
|
135
|
+
In direct mode, `alpha` is split equally across rule sizes and Bonferroni-corrected within each
|
|
136
|
+
size over the full search space. The `correction` parameter is ignored. In holdout mode,
|
|
137
|
+
`holdout_fraction=0` disables the split; p-values are then not valid and a warning is raised.
|
|
138
|
+
|
|
139
|
+
## Statistical guarantees and assumptions
|
|
140
|
+
|
|
141
|
+
The guarantee is family-wise or false-discovery control over the rules actually tested, under
|
|
142
|
+
these assumptions:
|
|
143
|
+
|
|
144
|
+
* transactions are independent and identically distributed;
|
|
145
|
+
* the null model is independence between the items in a rule;
|
|
146
|
+
* in holdout mode, the holdout part is independent of the part used to select candidates;
|
|
147
|
+
* in direct mode, the correction covers the full search space, not only the candidates that
|
|
148
|
+
happened to pass the other filters.
|
|
149
|
+
|
|
150
|
+
The testing is conservative, which is why the null-data error rate is well below 5%. It does not
|
|
151
|
+
model dependence caused by basket size or by other variables. Items can be associated only
|
|
152
|
+
because some baskets are larger, or because a third variable drives both, and such rules can be
|
|
153
|
+
reported. It also does not establish causation. See `docs/methodology.md` and
|
|
154
|
+
`docs/limitations.md`.
|
|
155
|
+
|
|
156
|
+
## Input formats
|
|
157
|
+
|
|
158
|
+
`RuleMiner` and `RuleClassifier` accept:
|
|
159
|
+
|
|
160
|
+
* an iterable of item collections, including a generator;
|
|
161
|
+
* a 2D numpy array or a 0/1 matrix (any numeric dtype, nonzero means present);
|
|
162
|
+
* a scipy sparse matrix;
|
|
163
|
+
* a pandas DataFrame (dense or sparse one-hot) or a Series of lists;
|
|
164
|
+
* a polars DataFrame (one-hot or a single list column) or a Series of lists.
|
|
165
|
+
|
|
166
|
+
Use `item_names` to label matrix columns with strings. Strings as transactions are rejected
|
|
167
|
+
because a single string is ambiguous; split or encode them first. Matrix input rejects NaN,
|
|
168
|
+
infinity, complex numbers and zero-width data.
|
|
169
|
+
|
|
170
|
+
## When to use it, and when not to
|
|
171
|
+
|
|
172
|
+
Good for:
|
|
173
|
+
|
|
174
|
+
* sparse transaction-style or categorical data where a few rules carry the structure;
|
|
175
|
+
* explainable models and auditable discovery;
|
|
176
|
+
* separating real associations from noise in small or exploratory samples;
|
|
177
|
+
* reporting an interval and a p-value with every rule.
|
|
178
|
+
|
|
179
|
+
Weak for:
|
|
180
|
+
|
|
181
|
+
* signal spread thinly over many dense numeric features; bin first, and expect low accuracy
|
|
182
|
+
compared with regularized linear models or trees;
|
|
183
|
+
* maximum recall: a classical list contains more true rules, mixed with more redundancy;
|
|
184
|
+
* recommendation hit rate: a popularity baseline can beat rule-based recommendations;
|
|
185
|
+
* negative associations, closed or maximal itemsets, constrained mining, and the full metric
|
|
186
|
+
catalogue that mlxtend provides.
|
|
187
|
+
|
|
188
|
+
Measured results and the head-to-head against mlxtend are in `benchmarks/COMPARISON.md`. The
|
|
189
|
+
methodology and limits are in `docs/methodology.md` and `docs/limitations.md`.
|
|
190
|
+
|
|
191
|
+
## Documentation
|
|
192
|
+
|
|
193
|
+
* `docs/index.md`: documentation home.
|
|
194
|
+
* `docs/api.md`: parameters, attributes, methods and output columns.
|
|
195
|
+
* `docs/methodology.md`: the statistical method and its assumptions.
|
|
196
|
+
* `docs/limitations.md`: known failure modes and power.
|
|
197
|
+
* `docs/comparison.md`: rulewise compared with existing Python libraries.
|
|
198
|
+
* `docs/tutorials/groceries.md`: a real supermarket basket walkthrough.
|
|
199
|
+
* `docs/tutorials/classification.md`: rules as a classifier on Mushrooms and Titanic.
|
|
200
|
+
* `docs/publishing.md`: release steps.
|
|
201
|
+
|
|
202
|
+
## Development
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pip install -e ".[dev]"
|
|
206
|
+
python benchmarks/get_data.py
|
|
207
|
+
python benchmarks/library_comparison.py --section all --runs 500 --seeds 5
|
|
208
|
+
python -m pytest
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The test suite checks p-values and corrections against scipy and statsmodels, itemsets against
|
|
212
|
+
mlxtend and across backends, error control by simulation on null data, and recovery of planted
|
|
213
|
+
rules. Optional-dependency tests skip when the dependency is absent, so the core install is
|
|
214
|
+
tested without PyFIM, scikit-learn, matplotlib, mlxtend, statsmodels or polars.
|
|
215
|
+
|
|
216
|
+
## References
|
|
217
|
+
|
|
218
|
+
* Webb, G.I. (2007). Discovering significant patterns. Machine Learning 68(1).
|
|
219
|
+
* Webb, G.I. (2008). Layered critical values: a powerful direct-adjustment approach to
|
|
220
|
+
discovering significant patterns. Machine Learning 71.
|
|
221
|
+
* Hamalainen, W. and Webb, G.I. (2019). A tutorial on statistically sound pattern discovery.
|
|
222
|
+
Data Mining and Knowledge Discovery 33.
|
|
223
|
+
* Webb, G.I. (2010). Self-sufficient itemsets. ACM TKDD 4(1).
|
|
224
|
+
* Holm (1979); Benjamini and Hochberg (1995); Benjamini and Yekutieli (2001).
|
|
225
|
+
* Borgelt, C. PyFIM and Eclat (MIT licence).
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT. See `LICENSE`.
|