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.
Files changed (60) hide show
  1. rulewise-0.1.0/CHANGELOG.md +38 -0
  2. rulewise-0.1.0/LICENSE +21 -0
  3. rulewise-0.1.0/MANIFEST.in +4 -0
  4. rulewise-0.1.0/PKG-INFO +273 -0
  5. rulewise-0.1.0/README.md +229 -0
  6. rulewise-0.1.0/benchmarks/COMPARISON.md +115 -0
  7. rulewise-0.1.0/benchmarks/PRE_PUBLISH_REVIEW.md +228 -0
  8. rulewise-0.1.0/benchmarks/RESULTS.md +153 -0
  9. rulewise-0.1.0/benchmarks/classifier_eval.py +54 -0
  10. rulewise-0.1.0/benchmarks/data/README.md +14 -0
  11. rulewise-0.1.0/benchmarks/error_control_simulation.py +35 -0
  12. rulewise-0.1.0/benchmarks/get_data.py +40 -0
  13. rulewise-0.1.0/benchmarks/library_comparison.py +668 -0
  14. rulewise-0.1.0/benchmarks/power_study.py +45 -0
  15. rulewise-0.1.0/benchmarks/power_study_multiseed.py +24 -0
  16. rulewise-0.1.0/benchmarks/real_classification.py +63 -0
  17. rulewise-0.1.0/benchmarks/replication_groceries.py +85 -0
  18. rulewise-0.1.0/docs/api.md +402 -0
  19. rulewise-0.1.0/docs/comparison.md +160 -0
  20. rulewise-0.1.0/docs/img/groceries_effect.png +0 -0
  21. rulewise-0.1.0/docs/img/groceries_forest.png +0 -0
  22. rulewise-0.1.0/docs/img/groceries_network.png +0 -0
  23. rulewise-0.1.0/docs/img/groceries_validation.png +0 -0
  24. rulewise-0.1.0/docs/index.md +47 -0
  25. rulewise-0.1.0/docs/limitations.md +147 -0
  26. rulewise-0.1.0/docs/methodology.md +196 -0
  27. rulewise-0.1.0/docs/publishing.md +152 -0
  28. rulewise-0.1.0/docs/requirements.txt +2 -0
  29. rulewise-0.1.0/docs/tutorials/classification.md +95 -0
  30. rulewise-0.1.0/docs/tutorials/groceries.md +135 -0
  31. rulewise-0.1.0/mkdocs.yml +30 -0
  32. rulewise-0.1.0/pyproject.toml +43 -0
  33. rulewise-0.1.0/setup.cfg +4 -0
  34. rulewise-0.1.0/src/rulewise/__init__.py +9 -0
  35. rulewise-0.1.0/src/rulewise/_compat.py +36 -0
  36. rulewise-0.1.0/src/rulewise/classifier.py +257 -0
  37. rulewise-0.1.0/src/rulewise/classmining.py +76 -0
  38. rulewise-0.1.0/src/rulewise/io.py +98 -0
  39. rulewise-0.1.0/src/rulewise/miner.py +329 -0
  40. rulewise-0.1.0/src/rulewise/mining.py +109 -0
  41. rulewise-0.1.0/src/rulewise/plot.py +179 -0
  42. rulewise-0.1.0/src/rulewise/report.py +45 -0
  43. rulewise-0.1.0/src/rulewise/rules.py +145 -0
  44. rulewise-0.1.0/src/rulewise/rulesmixin.py +133 -0
  45. rulewise-0.1.0/src/rulewise/stats.py +135 -0
  46. rulewise-0.1.0/src/rulewise.egg-info/PKG-INFO +273 -0
  47. rulewise-0.1.0/src/rulewise.egg-info/SOURCES.txt +58 -0
  48. rulewise-0.1.0/src/rulewise.egg-info/dependency_links.txt +1 -0
  49. rulewise-0.1.0/src/rulewise.egg-info/requires.txt +21 -0
  50. rulewise-0.1.0/src/rulewise.egg-info/top_level.txt +1 -0
  51. rulewise-0.1.0/tests/__init__.py +0 -0
  52. rulewise-0.1.0/tests/test_backend_io.py +133 -0
  53. rulewise-0.1.0/tests/test_ci.py +49 -0
  54. rulewise-0.1.0/tests/test_class_conditional.py +65 -0
  55. rulewise-0.1.0/tests/test_classifier.py +156 -0
  56. rulewise-0.1.0/tests/test_m1.py +70 -0
  57. rulewise-0.1.0/tests/test_m4.py +138 -0
  58. rulewise-0.1.0/tests/test_miner.py +79 -0
  59. rulewise-0.1.0/tests/test_mining.py +22 -0
  60. 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.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md CHANGELOG.md mkdocs.yml
2
+ recursive-include docs *.md *.png *.txt
3
+ recursive-include benchmarks *.py *.md
4
+ recursive-include tests *.py
@@ -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`.
@@ -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`.