mclovin 1.0.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 (44) hide show
  1. mclovin-1.0.0/LICENSE +21 -0
  2. mclovin-1.0.0/PKG-INFO +319 -0
  3. mclovin-1.0.0/README.md +278 -0
  4. mclovin-1.0.0/pyproject.toml +49 -0
  5. mclovin-1.0.0/setup.cfg +4 -0
  6. mclovin-1.0.0/src/mclovin/__init__.py +118 -0
  7. mclovin-1.0.0/src/mclovin/basketball/__init__.py +65 -0
  8. mclovin-1.0.0/src/mclovin/basketball/box_metrics.py +916 -0
  9. mclovin-1.0.0/src/mclovin/basketball/coefficients.py +30 -0
  10. mclovin-1.0.0/src/mclovin/basketball/draft.py +954 -0
  11. mclovin-1.0.0/src/mclovin/basketball/four_factors.py +527 -0
  12. mclovin-1.0.0/src/mclovin/basketball/identities.py +55 -0
  13. mclovin-1.0.0/src/mclovin/basketball/possessions.py +247 -0
  14. mclovin-1.0.0/src/mclovin/basketball/reconstruct.py +201 -0
  15. mclovin-1.0.0/src/mclovin/core.py +177 -0
  16. mclovin-1.0.0/src/mclovin/evaluation.py +125 -0
  17. mclovin-1.0.0/src/mclovin/panel/__init__.py +29 -0
  18. mclovin-1.0.0/src/mclovin/panel/features.py +204 -0
  19. mclovin-1.0.0/src/mclovin/panel/splits.py +93 -0
  20. mclovin-1.0.0/src/mclovin/py.typed +0 -0
  21. mclovin-1.0.0/src/mclovin/quality.py +309 -0
  22. mclovin-1.0.0/src/mclovin/schema.py +167 -0
  23. mclovin-1.0.0/src/mclovin/shrinkage.py +615 -0
  24. mclovin-1.0.0/src/mclovin/similarity.py +625 -0
  25. mclovin-1.0.0/src/mclovin/survival.py +221 -0
  26. mclovin-1.0.0/src/mclovin/units.py +253 -0
  27. mclovin-1.0.0/src/mclovin/vocab/__init__.py +128 -0
  28. mclovin-1.0.0/src/mclovin/vocab/basketball.py +175 -0
  29. mclovin-1.0.0/src/mclovin.egg-info/PKG-INFO +319 -0
  30. mclovin-1.0.0/src/mclovin.egg-info/SOURCES.txt +42 -0
  31. mclovin-1.0.0/src/mclovin.egg-info/dependency_links.txt +1 -0
  32. mclovin-1.0.0/src/mclovin.egg-info/requires.txt +28 -0
  33. mclovin-1.0.0/src/mclovin.egg-info/top_level.txt +1 -0
  34. mclovin-1.0.0/tests/test_core_and_possessions.py +164 -0
  35. mclovin-1.0.0/tests/test_evaluation.py +76 -0
  36. mclovin-1.0.0/tests/test_metrics.py +629 -0
  37. mclovin-1.0.0/tests/test_panel.py +196 -0
  38. mclovin-1.0.0/tests/test_quality_defaults.py +58 -0
  39. mclovin-1.0.0/tests/test_reconstruct.py +108 -0
  40. mclovin-1.0.0/tests/test_research_modules.py +986 -0
  41. mclovin-1.0.0/tests/test_schema.py +162 -0
  42. mclovin-1.0.0/tests/test_similarity.py +341 -0
  43. mclovin-1.0.0/tests/test_units.py +274 -0
  44. mclovin-1.0.0/tests/test_vocab.py +75 -0
mclovin-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SidharthJoly
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.
mclovin-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,319 @@
1
+ Metadata-Version: 2.4
2
+ Name: mclovin
3
+ Version: 1.0.0
4
+ Summary: Trustworthy features from messy tabular data: schema resolution, provenance tiers, small-sample correction and leakage-safe panel features.
5
+ Author: SidharthJoly
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/SidharthJoly/36120-26SP-group10-25664929-package
8
+ Project-URL: Documentation, https://mclovin.readthedocs.io/
9
+ Keywords: feature-engineering,data-quality,panel-data,time-series,empirical-bayes,shrinkage,provenance,schema-matching,sports-analytics
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pandas>=1.5
19
+ Requires-Dist: numpy>=1.23
20
+ Requires-Dist: scipy>=1.9
21
+ Provides-Extra: survival
22
+ Requires-Dist: lifelines>=0.27; extra == "survival"
23
+ Provides-Extra: models
24
+ Requires-Dist: scikit-learn>=1.0; extra == "models"
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: twine; extra == "dev"
30
+ Requires-Dist: mypy>=1.0; extra == "dev"
31
+ Requires-Dist: pandas-stubs; extra == "dev"
32
+ Requires-Dist: scipy-stubs; python_version >= "3.10" and extra == "dev"
33
+ Requires-Dist: lifelines>=0.27; extra == "dev"
34
+ Requires-Dist: scikit-learn>=1.0; extra == "dev"
35
+ Provides-Extra: docs
36
+ Requires-Dist: sphinx>=7.0; extra == "docs"
37
+ Requires-Dist: furo; extra == "docs"
38
+ Requires-Dist: myst-parser>=2.0; extra == "docs"
39
+ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
40
+ Dynamic: license-file
41
+
42
+ # mclovin
43
+
44
+ [![tests](https://github.com/SidharthJoly/36120-26SP-group10-25664929-package/actions/workflows/tests.yml/badge.svg)](https://github.com/SidharthJoly/36120-26SP-group10-25664929-package/actions/workflows/tests.yml)
45
+ [![codecov](https://codecov.io/gh/SidharthJoly/36120-26SP-group10-25664929-package/branch/main/graph/badge.svg?token=MPNVUWTTKY)](https://codecov.io/gh/SidharthJoly/36120-26SP-group10-25664929-package)
46
+ [![PyPI](https://img.shields.io/pypi/v/mclovin.svg?cacheSeconds=3600)](https://pypi.org/project/mclovin/)
47
+ [![TestPyPI](https://img.shields.io/badge/TestPyPI-mclovin-orange.svg)](https://test.pypi.org/project/mclovin/)
48
+ [![Downloads](https://img.shields.io/pypi/dm/mclovin.svg)](https://pypi.org/project/mclovin/)
49
+ [![Python versions](https://img.shields.io/pypi/pyversions/mclovin.svg?cacheSeconds=3600)](https://pypi.org/project/mclovin/)
50
+ [![License](https://img.shields.io/pypi/l/mclovin.svg)](LICENSE)
51
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
52
+
53
+ Trustworthy features from messy tabular data: schema resolution, provenance
54
+ tiers, small-sample correction and leakage-safe panel features.
55
+
56
+ It began as basketball code. What survived the move to a general-purpose
57
+ library is the part that was never really about basketball. The basketball
58
+ metrics are still here, in `mclovin.basketball`, as one domain pack among
59
+ others -- and the previous release is still on PyPI as
60
+ [moneyBBall 0.6.2](https://pypi.org/project/moneyBBall/).
61
+
62
+ ---
63
+
64
+ ## Design principles
65
+
66
+ **1. Schema-flexible.** Functions resolve canonical names (`PTS`, `STL_PCT`,
67
+ `TEMP_MAX`, …) against whatever your DataFrame actually calls them, using a
68
+ `Vocabulary` you point the resolver at. The resolver itself holds no domain
69
+ knowledge, which is what lets the same machinery work on a box score and a
70
+ weather archive without modification.
71
+
72
+ **2. Honest about provenance.** Many derived quantities are *defined* in terms
73
+ of context a given dataset may not carry -- team totals, station baselines,
74
+ cohort aggregates. Rather than quietly approximating, every metric declares
75
+ its inputs and reports a reliability tier
76
+ (`EXACT`, `APPROXIMATED`, `PASSTHROUGH`, `DERIVED`, `UNAVAILABLE`), so you
77
+ know which numbers can bear weight.
78
+
79
+ **3. Leakage-resistant by default.** Rolling windows require a full window,
80
+ horizon joins match on the key rather than on position, and splits are
81
+ chronological. The defaults are the careful choice, not the convenient one.
82
+
83
+ **4. Research-cited.** Domain formulas carry their sources: Oliver's
84
+ *Basketball on Paper*, Kubatko et al. (2007) *JQAS*, Myers' BPM 2.0,
85
+ Hollinger's PER, Sill's RAPM regularisation, Pelton's age adjustment, Vashro
86
+ on steal rate, and Cheng on free-throw percentage as a shooting-touch proxy.
87
+
88
+ ---
89
+
90
+ ## Modules
91
+
92
+ | Module | Purpose |
93
+ |---|---|
94
+ | `schema` | Canonical-name resolution, coverage auditing, context detection |
95
+ | `vocab` | Domain vocabularies: aliases, context markers, rate ceilings |
96
+ | `core` | Provenance system: `MetricSpec`, `ComputationReport`, `Reliability` |
97
+ | `panel` | Lags, deltas, rolling windows, horizon joins, temporal splits |
98
+ | `units` | Detect and harmonise mixed per-period / cumulative columns |
99
+ | `shrinkage` | Empirical-Bayes (flat and hierarchical) and James-Stein small-sample correction |
100
+ | `quality` | Rate validation, artifact detection, robust standardisation |
101
+ | `similarity` | PCA decomposition, GMM soft archetypes, Mahalanobis-distance neighbours |
102
+ | `survival` | Kaplan-Meier, Cox, censoring-aware label construction |
103
+ | `evaluation` | Shared regression and classification scoring helpers |
104
+ | `basketball` | The domain pack: Four Factors, possessions, BPM/PER, draft features, count reconstruction |
105
+
106
+ ---
107
+
108
+ ## Validation
109
+
110
+ BPM reconstruction, unit harmonisation, and rate-artifact detection have
111
+ been checked against a real NCAA box-score dataset and an independent
112
+ reference. See [VALIDATION.md](VALIDATION.md) for methodology, results,
113
+ and the data-quality issues these modules handle.
114
+
115
+ ---
116
+
117
+ ## Installation
118
+
119
+ Published to [PyPI](https://pypi.org/project/mclovin/) and
120
+ [TestPyPI](https://test.pypi.org/project/mclovin/).
121
+
122
+ ```bash
123
+ # From PyPI (recommended)
124
+ pip install mclovin
125
+
126
+ # From TestPyPI (dependencies still resolve from PyPI)
127
+ pip install --index-url https://test.pypi.org/simple/ \
128
+ --extra-index-url https://pypi.org/simple/ mclovin
129
+
130
+ # Local development
131
+ pip install -e ".[dev]"
132
+
133
+ # With survival analysis (Cox models)
134
+ pip install -e ".[survival]"
135
+
136
+ # With the scoring helpers (scikit-learn)
137
+ pip install -e ".[models]"
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Usage
143
+
144
+ ### Quickstart
145
+
146
+ Resolve your schema once, then call any `add_*` function. Each one returns
147
+ your DataFrame with new columns appended.
148
+
149
+ ```python
150
+ import pandas as pd
151
+ import mclovin.basketball as bb
152
+
153
+ df = pd.read_csv("players.csv")
154
+ resolver = bb.resolver(df.columns) # basketball vocabulary supplied
155
+
156
+ df = bb.draft.add_prospect_features(df, resolver=resolver)
157
+ ```
158
+
159
+ `add_prospect_features` skips any feature whose inputs it can't resolve
160
+ rather than raising, so it's safe to run on a dataset you haven't audited
161
+ yet. For real-world data, though, run it after the fuller pipeline below,
162
+ since features computed on mixed units or unreconstructed counts will be
163
+ wrong.
164
+
165
+ ### Recommended pipeline order
166
+
167
+ Run these steps in order, since later ones depend on earlier ones: units
168
+ first, then reconstruction, then everything else.
169
+
170
+ ```python
171
+ import pandas as pd
172
+ from mclovin import ComputationReport, units, quality
173
+ import mclovin.basketball as bb
174
+ from mclovin.basketball import box_metrics, draft, reconstruct
175
+
176
+ df = pd.read_csv("players.csv")
177
+ df = df.drop_duplicates(subset=["pid", "year"])
178
+
179
+ r = bb.resolver(df.columns)
180
+ report = ComputationReport()
181
+
182
+ # 1. Audit what this dataset supports
183
+ print(r.coverage().query("available"))
184
+ print("team context:", r.has_team_context())
185
+
186
+ # 2. Fix units FIRST. Nothing downstream is valid without this
187
+ detection = units.detect_stat_units(df, resolver=r)
188
+ print(detection.evidence)
189
+ df = units.harmonize_units(df, resolver=r, target="season_total",
190
+ detection=detection)
191
+ print(units.verify_scoring_identity(df, resolver=r))
192
+
193
+ # 3. Recover missing raw counts
194
+ r = SchemaResolver(df.columns)
195
+ df = reconstruct.reconstruct_counting_stats(df, resolver=r, report=report)
196
+
197
+ # 4. Clean small-sample artifacts
198
+ r = SchemaResolver(df.columns)
199
+ print(quality.find_impossible_rates(df, resolver=r))
200
+ df = quality.clip_rates(df, resolver=r)
201
+ df = quality.add_sample_size_flag(df, resolver=r)
202
+
203
+ # 5. Compute metrics and prospect features
204
+ df = box_metrics.add_box_plus_minus_linear(df, resolver=r, report=report)
205
+ df = draft.add_prospect_features(df, resolver=r, report=report)
206
+ df = draft.add_age_adjusted_production(df, production_col="bpm", report=report)
207
+
208
+ # 6. Audit what you can trust
209
+ print(report.to_frame())
210
+ print("trusted:", report.trusted())
211
+ ```
212
+
213
+ ### Small-sample correction
214
+
215
+ Three-point percentage needs roughly **750 attempts** to become reliable
216
+ (Blackport 2014). A college season provides a fraction of that, so nearly
217
+ every college 3P% is under-sampled.
218
+
219
+ ```python
220
+ from mclovin import shrinkage
221
+
222
+ df = shrinkage.empirical_bayes_rate(df, made_col="TPM", attempted_col="TPA")
223
+ df = shrinkage.add_reliability_weight(df, "TPA", stat_key="TP_PCT")
224
+ ```
225
+
226
+ A 4-for-7 shooter moves substantially toward the population mean; a
227
+ 200-for-500 shooter barely moves.
228
+
229
+ ---
230
+
231
+ ## Testing
232
+
233
+ ```bash
234
+ pip install -e ".[dev]"
235
+ pytest --cov=mclovin
236
+ ```
237
+
238
+ **91 tests pass**, including:
239
+
240
+ - BPM's steal coefficient is the largest positive weight (Myers)
241
+ - Oliver's weights match the published 40/25/20/15
242
+ - PER normalises to exactly 15.00
243
+ - Turnover recovery round-trips to `rtol=1e-9`
244
+ - Robust z-score resists a single 1072 artifact where classical z-score fails
245
+ - Empirical Bayes shrinks small samples more than large ones
246
+ - Censored careers become `NaN`, not false negatives
247
+
248
+ ---
249
+
250
+ ## Known limitations
251
+
252
+ - **RAPM is not computable** from box scores. It needs play-by-play lineup
253
+ data. BPM is its box-score approximation, and is treated as such here.
254
+ - **Full BPM 2.0 coefficients are only partially published.** This implements
255
+ Myers' simplified *linear* version; correlation with a full-model reference
256
+ is 0.81, not 1.0.
257
+ - **Team-context metrics need team data.** Usage%, AST%, TRB%, ORtg, PER and
258
+ Win Shares all require team/opponent totals absent from a player-only
259
+ dataset. They raise a clear `KeyError` rather than silently approximating.
260
+ - **PER's `normalise` centres against whatever rows you pass**, which
261
+ equals the true league average only if you pass the full league.
262
+ - **Pelton's 0.5/year age penalty is in WARP units.** Applied to another
263
+ production scale it should be rescaled, e.g. by the ratio of that scale's
264
+ standard deviation to WARP's. This package does not fit the penalty
265
+ against outcome labels; doing so is a modelling decision left to the
266
+ caller, consistent with `mclovin` computing statistics rather than
267
+ training predictive models.
268
+ - **Rate ceilings in `quality` are judgement calls**, not published constants.
269
+ Review them against your own data before relying on the clipping.
270
+
271
+ ---
272
+
273
+ ## Key references
274
+
275
+ - Oliver, D. (2004). *Basketball on Paper*. Potomac Books.
276
+ - Kubatko, J., Oliver, D., Pelton, K., & Rosenbaum, D. (2007). "A Starting
277
+ Point for Analyzing Basketball Statistics." *JQAS* 3(3).
278
+ - Myers, D. (2020). "About Box Plus/Minus (BPM)." Basketball-Reference.
279
+ - NBA.com Advanced Stats glossary. "Performance Impact Estimator (PIE)."
280
+ - Sill, J. (2010). "Improved NBA Adjusted +/− Using Regularization and
281
+ Out-of-Sample Testing." MIT Sloan Sports Analytics Conference.
282
+ - Rosenbaum, D. (2004). "Measuring How NBA Players Help Their Teams Win."
283
+ - Pelton, K. "Explaining Kevin Pelton's NBA draft projection system." ESPN.
284
+ - Vashro, L. (2014). "How Do We Assess 'Potential' Among NBA Draft
285
+ Prospects?" Canis Hoopus.
286
+ - Cheng, C. (2020). "Scouting NBA Three-Point Shooting." Harvard Sports
287
+ Analysis Collective.
288
+ - Blackport, D. (2014). "How Long Does It Take For Three Point Shooting To
289
+ Stabilize?" Nylon Calculus.
290
+ - Vaci, N., Cocić, D., Gula, B., & Bilalić, M. (2019). "Large data and
291
+ Bayesian modeling: aging curves of NBA players." *Behavior Research Methods*
292
+ 51(4).
293
+ - Cui, Y., et al. (2019). "Key Anthropometric and Physical Determinants…NBA
294
+ Draft Combine." *Frontiers in Psychology*.
295
+ - Efron, B. & Morris, C. (1975). "Data Analysis Using Stein's Estimator."
296
+ *JASA* 70(350).
297
+ - Casella, G. (1985). "An Introduction to Empirical Bayes Data Analysis."
298
+ *The American Statistician* 39(2), 83-87.
299
+ - Brown, L. D. (2008). "In-season prediction of batting averages: A field
300
+ test of empirical Bayes and Bayes methodologies." *Annals of Applied
301
+ Statistics* 2(1).
302
+ - Gelman, A. & Hill, J. (2007). *Data Analysis Using Regression and
303
+ Multilevel/Hierarchical Models*. Cambridge University Press.
304
+ - Morris, C. N. (1983). "Parametric Empirical Bayes Inference: Theory and
305
+ Applications." *JASA* 78(381), 47-55.
306
+ - Searle, S.R., Casella, G., & McCulloch, C.E. (1992). *Variance
307
+ Components*. Wiley.
308
+ - Jolliffe, I.T. (2002). *Principal Component Analysis* (2nd ed.). Springer
309
+ Series in Statistics.
310
+ - Alagappan, M. (2012). "From 5 to 13: Redefining the Positions in
311
+ Basketball." MIT Sloan Sports Analytics Conference.
312
+ - Dempster, A.P., Laird, N.M. & Rubin, D.B. (1977). "Maximum Likelihood
313
+ from Incomplete Data via the EM Algorithm." *JRSS Series B* 39(1), 1-38.
314
+ - Mahalanobis, P.C. (1936). "On the Generalised Distance in Statistics."
315
+ Proceedings of the National Institute of Sciences of India, 2(1), 49-55.
316
+ - Ledoit, O. & Wolf, M. (2004). "A well-conditioned estimator for
317
+ large-dimensional covariance matrices." Journal of Multivariate
318
+ Analysis, 88(2), 365-411.
319
+ </content>
@@ -0,0 +1,278 @@
1
+ # mclovin
2
+
3
+ [![tests](https://github.com/SidharthJoly/36120-26SP-group10-25664929-package/actions/workflows/tests.yml/badge.svg)](https://github.com/SidharthJoly/36120-26SP-group10-25664929-package/actions/workflows/tests.yml)
4
+ [![codecov](https://codecov.io/gh/SidharthJoly/36120-26SP-group10-25664929-package/branch/main/graph/badge.svg?token=MPNVUWTTKY)](https://codecov.io/gh/SidharthJoly/36120-26SP-group10-25664929-package)
5
+ [![PyPI](https://img.shields.io/pypi/v/mclovin.svg?cacheSeconds=3600)](https://pypi.org/project/mclovin/)
6
+ [![TestPyPI](https://img.shields.io/badge/TestPyPI-mclovin-orange.svg)](https://test.pypi.org/project/mclovin/)
7
+ [![Downloads](https://img.shields.io/pypi/dm/mclovin.svg)](https://pypi.org/project/mclovin/)
8
+ [![Python versions](https://img.shields.io/pypi/pyversions/mclovin.svg?cacheSeconds=3600)](https://pypi.org/project/mclovin/)
9
+ [![License](https://img.shields.io/pypi/l/mclovin.svg)](LICENSE)
10
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
11
+
12
+ Trustworthy features from messy tabular data: schema resolution, provenance
13
+ tiers, small-sample correction and leakage-safe panel features.
14
+
15
+ It began as basketball code. What survived the move to a general-purpose
16
+ library is the part that was never really about basketball. The basketball
17
+ metrics are still here, in `mclovin.basketball`, as one domain pack among
18
+ others -- and the previous release is still on PyPI as
19
+ [moneyBBall 0.6.2](https://pypi.org/project/moneyBBall/).
20
+
21
+ ---
22
+
23
+ ## Design principles
24
+
25
+ **1. Schema-flexible.** Functions resolve canonical names (`PTS`, `STL_PCT`,
26
+ `TEMP_MAX`, …) against whatever your DataFrame actually calls them, using a
27
+ `Vocabulary` you point the resolver at. The resolver itself holds no domain
28
+ knowledge, which is what lets the same machinery work on a box score and a
29
+ weather archive without modification.
30
+
31
+ **2. Honest about provenance.** Many derived quantities are *defined* in terms
32
+ of context a given dataset may not carry -- team totals, station baselines,
33
+ cohort aggregates. Rather than quietly approximating, every metric declares
34
+ its inputs and reports a reliability tier
35
+ (`EXACT`, `APPROXIMATED`, `PASSTHROUGH`, `DERIVED`, `UNAVAILABLE`), so you
36
+ know which numbers can bear weight.
37
+
38
+ **3. Leakage-resistant by default.** Rolling windows require a full window,
39
+ horizon joins match on the key rather than on position, and splits are
40
+ chronological. The defaults are the careful choice, not the convenient one.
41
+
42
+ **4. Research-cited.** Domain formulas carry their sources: Oliver's
43
+ *Basketball on Paper*, Kubatko et al. (2007) *JQAS*, Myers' BPM 2.0,
44
+ Hollinger's PER, Sill's RAPM regularisation, Pelton's age adjustment, Vashro
45
+ on steal rate, and Cheng on free-throw percentage as a shooting-touch proxy.
46
+
47
+ ---
48
+
49
+ ## Modules
50
+
51
+ | Module | Purpose |
52
+ |---|---|
53
+ | `schema` | Canonical-name resolution, coverage auditing, context detection |
54
+ | `vocab` | Domain vocabularies: aliases, context markers, rate ceilings |
55
+ | `core` | Provenance system: `MetricSpec`, `ComputationReport`, `Reliability` |
56
+ | `panel` | Lags, deltas, rolling windows, horizon joins, temporal splits |
57
+ | `units` | Detect and harmonise mixed per-period / cumulative columns |
58
+ | `shrinkage` | Empirical-Bayes (flat and hierarchical) and James-Stein small-sample correction |
59
+ | `quality` | Rate validation, artifact detection, robust standardisation |
60
+ | `similarity` | PCA decomposition, GMM soft archetypes, Mahalanobis-distance neighbours |
61
+ | `survival` | Kaplan-Meier, Cox, censoring-aware label construction |
62
+ | `evaluation` | Shared regression and classification scoring helpers |
63
+ | `basketball` | The domain pack: Four Factors, possessions, BPM/PER, draft features, count reconstruction |
64
+
65
+ ---
66
+
67
+ ## Validation
68
+
69
+ BPM reconstruction, unit harmonisation, and rate-artifact detection have
70
+ been checked against a real NCAA box-score dataset and an independent
71
+ reference. See [VALIDATION.md](VALIDATION.md) for methodology, results,
72
+ and the data-quality issues these modules handle.
73
+
74
+ ---
75
+
76
+ ## Installation
77
+
78
+ Published to [PyPI](https://pypi.org/project/mclovin/) and
79
+ [TestPyPI](https://test.pypi.org/project/mclovin/).
80
+
81
+ ```bash
82
+ # From PyPI (recommended)
83
+ pip install mclovin
84
+
85
+ # From TestPyPI (dependencies still resolve from PyPI)
86
+ pip install --index-url https://test.pypi.org/simple/ \
87
+ --extra-index-url https://pypi.org/simple/ mclovin
88
+
89
+ # Local development
90
+ pip install -e ".[dev]"
91
+
92
+ # With survival analysis (Cox models)
93
+ pip install -e ".[survival]"
94
+
95
+ # With the scoring helpers (scikit-learn)
96
+ pip install -e ".[models]"
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Usage
102
+
103
+ ### Quickstart
104
+
105
+ Resolve your schema once, then call any `add_*` function. Each one returns
106
+ your DataFrame with new columns appended.
107
+
108
+ ```python
109
+ import pandas as pd
110
+ import mclovin.basketball as bb
111
+
112
+ df = pd.read_csv("players.csv")
113
+ resolver = bb.resolver(df.columns) # basketball vocabulary supplied
114
+
115
+ df = bb.draft.add_prospect_features(df, resolver=resolver)
116
+ ```
117
+
118
+ `add_prospect_features` skips any feature whose inputs it can't resolve
119
+ rather than raising, so it's safe to run on a dataset you haven't audited
120
+ yet. For real-world data, though, run it after the fuller pipeline below,
121
+ since features computed on mixed units or unreconstructed counts will be
122
+ wrong.
123
+
124
+ ### Recommended pipeline order
125
+
126
+ Run these steps in order, since later ones depend on earlier ones: units
127
+ first, then reconstruction, then everything else.
128
+
129
+ ```python
130
+ import pandas as pd
131
+ from mclovin import ComputationReport, units, quality
132
+ import mclovin.basketball as bb
133
+ from mclovin.basketball import box_metrics, draft, reconstruct
134
+
135
+ df = pd.read_csv("players.csv")
136
+ df = df.drop_duplicates(subset=["pid", "year"])
137
+
138
+ r = bb.resolver(df.columns)
139
+ report = ComputationReport()
140
+
141
+ # 1. Audit what this dataset supports
142
+ print(r.coverage().query("available"))
143
+ print("team context:", r.has_team_context())
144
+
145
+ # 2. Fix units FIRST. Nothing downstream is valid without this
146
+ detection = units.detect_stat_units(df, resolver=r)
147
+ print(detection.evidence)
148
+ df = units.harmonize_units(df, resolver=r, target="season_total",
149
+ detection=detection)
150
+ print(units.verify_scoring_identity(df, resolver=r))
151
+
152
+ # 3. Recover missing raw counts
153
+ r = SchemaResolver(df.columns)
154
+ df = reconstruct.reconstruct_counting_stats(df, resolver=r, report=report)
155
+
156
+ # 4. Clean small-sample artifacts
157
+ r = SchemaResolver(df.columns)
158
+ print(quality.find_impossible_rates(df, resolver=r))
159
+ df = quality.clip_rates(df, resolver=r)
160
+ df = quality.add_sample_size_flag(df, resolver=r)
161
+
162
+ # 5. Compute metrics and prospect features
163
+ df = box_metrics.add_box_plus_minus_linear(df, resolver=r, report=report)
164
+ df = draft.add_prospect_features(df, resolver=r, report=report)
165
+ df = draft.add_age_adjusted_production(df, production_col="bpm", report=report)
166
+
167
+ # 6. Audit what you can trust
168
+ print(report.to_frame())
169
+ print("trusted:", report.trusted())
170
+ ```
171
+
172
+ ### Small-sample correction
173
+
174
+ Three-point percentage needs roughly **750 attempts** to become reliable
175
+ (Blackport 2014). A college season provides a fraction of that, so nearly
176
+ every college 3P% is under-sampled.
177
+
178
+ ```python
179
+ from mclovin import shrinkage
180
+
181
+ df = shrinkage.empirical_bayes_rate(df, made_col="TPM", attempted_col="TPA")
182
+ df = shrinkage.add_reliability_weight(df, "TPA", stat_key="TP_PCT")
183
+ ```
184
+
185
+ A 4-for-7 shooter moves substantially toward the population mean; a
186
+ 200-for-500 shooter barely moves.
187
+
188
+ ---
189
+
190
+ ## Testing
191
+
192
+ ```bash
193
+ pip install -e ".[dev]"
194
+ pytest --cov=mclovin
195
+ ```
196
+
197
+ **91 tests pass**, including:
198
+
199
+ - BPM's steal coefficient is the largest positive weight (Myers)
200
+ - Oliver's weights match the published 40/25/20/15
201
+ - PER normalises to exactly 15.00
202
+ - Turnover recovery round-trips to `rtol=1e-9`
203
+ - Robust z-score resists a single 1072 artifact where classical z-score fails
204
+ - Empirical Bayes shrinks small samples more than large ones
205
+ - Censored careers become `NaN`, not false negatives
206
+
207
+ ---
208
+
209
+ ## Known limitations
210
+
211
+ - **RAPM is not computable** from box scores. It needs play-by-play lineup
212
+ data. BPM is its box-score approximation, and is treated as such here.
213
+ - **Full BPM 2.0 coefficients are only partially published.** This implements
214
+ Myers' simplified *linear* version; correlation with a full-model reference
215
+ is 0.81, not 1.0.
216
+ - **Team-context metrics need team data.** Usage%, AST%, TRB%, ORtg, PER and
217
+ Win Shares all require team/opponent totals absent from a player-only
218
+ dataset. They raise a clear `KeyError` rather than silently approximating.
219
+ - **PER's `normalise` centres against whatever rows you pass**, which
220
+ equals the true league average only if you pass the full league.
221
+ - **Pelton's 0.5/year age penalty is in WARP units.** Applied to another
222
+ production scale it should be rescaled, e.g. by the ratio of that scale's
223
+ standard deviation to WARP's. This package does not fit the penalty
224
+ against outcome labels; doing so is a modelling decision left to the
225
+ caller, consistent with `mclovin` computing statistics rather than
226
+ training predictive models.
227
+ - **Rate ceilings in `quality` are judgement calls**, not published constants.
228
+ Review them against your own data before relying on the clipping.
229
+
230
+ ---
231
+
232
+ ## Key references
233
+
234
+ - Oliver, D. (2004). *Basketball on Paper*. Potomac Books.
235
+ - Kubatko, J., Oliver, D., Pelton, K., & Rosenbaum, D. (2007). "A Starting
236
+ Point for Analyzing Basketball Statistics." *JQAS* 3(3).
237
+ - Myers, D. (2020). "About Box Plus/Minus (BPM)." Basketball-Reference.
238
+ - NBA.com Advanced Stats glossary. "Performance Impact Estimator (PIE)."
239
+ - Sill, J. (2010). "Improved NBA Adjusted +/− Using Regularization and
240
+ Out-of-Sample Testing." MIT Sloan Sports Analytics Conference.
241
+ - Rosenbaum, D. (2004). "Measuring How NBA Players Help Their Teams Win."
242
+ - Pelton, K. "Explaining Kevin Pelton's NBA draft projection system." ESPN.
243
+ - Vashro, L. (2014). "How Do We Assess 'Potential' Among NBA Draft
244
+ Prospects?" Canis Hoopus.
245
+ - Cheng, C. (2020). "Scouting NBA Three-Point Shooting." Harvard Sports
246
+ Analysis Collective.
247
+ - Blackport, D. (2014). "How Long Does It Take For Three Point Shooting To
248
+ Stabilize?" Nylon Calculus.
249
+ - Vaci, N., Cocić, D., Gula, B., & Bilalić, M. (2019). "Large data and
250
+ Bayesian modeling: aging curves of NBA players." *Behavior Research Methods*
251
+ 51(4).
252
+ - Cui, Y., et al. (2019). "Key Anthropometric and Physical Determinants…NBA
253
+ Draft Combine." *Frontiers in Psychology*.
254
+ - Efron, B. & Morris, C. (1975). "Data Analysis Using Stein's Estimator."
255
+ *JASA* 70(350).
256
+ - Casella, G. (1985). "An Introduction to Empirical Bayes Data Analysis."
257
+ *The American Statistician* 39(2), 83-87.
258
+ - Brown, L. D. (2008). "In-season prediction of batting averages: A field
259
+ test of empirical Bayes and Bayes methodologies." *Annals of Applied
260
+ Statistics* 2(1).
261
+ - Gelman, A. & Hill, J. (2007). *Data Analysis Using Regression and
262
+ Multilevel/Hierarchical Models*. Cambridge University Press.
263
+ - Morris, C. N. (1983). "Parametric Empirical Bayes Inference: Theory and
264
+ Applications." *JASA* 78(381), 47-55.
265
+ - Searle, S.R., Casella, G., & McCulloch, C.E. (1992). *Variance
266
+ Components*. Wiley.
267
+ - Jolliffe, I.T. (2002). *Principal Component Analysis* (2nd ed.). Springer
268
+ Series in Statistics.
269
+ - Alagappan, M. (2012). "From 5 to 13: Redefining the Positions in
270
+ Basketball." MIT Sloan Sports Analytics Conference.
271
+ - Dempster, A.P., Laird, N.M. & Rubin, D.B. (1977). "Maximum Likelihood
272
+ from Incomplete Data via the EM Algorithm." *JRSS Series B* 39(1), 1-38.
273
+ - Mahalanobis, P.C. (1936). "On the Generalised Distance in Statistics."
274
+ Proceedings of the National Institute of Sciences of India, 2(1), 49-55.
275
+ - Ledoit, O. & Wolf, M. (2004). "A well-conditioned estimator for
276
+ large-dimensional covariance matrices." Journal of Multivariate
277
+ Analysis, 88(2), 365-411.
278
+ </content>
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mclovin"
7
+ version = "1.0.0"
8
+ description = "Trustworthy features from messy tabular data: schema resolution, provenance tiers, small-sample correction and leakage-safe panel features."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "SidharthJoly" }]
14
+ keywords = [
15
+ "feature-engineering", "data-quality", "panel-data", "time-series",
16
+ "empirical-bayes", "shrinkage", "provenance", "schema-matching",
17
+ "sports-analytics",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Science/Research",
22
+ "Programming Language :: Python :: 3",
23
+ "Operating System :: OS Independent",
24
+ "Topic :: Scientific/Engineering :: Information Analysis",
25
+ ]
26
+ dependencies = [
27
+ "pandas>=1.5",
28
+ "numpy>=1.23",
29
+ "scipy>=1.9",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ survival = ["lifelines>=0.27"]
34
+ models = ["scikit-learn>=1.0"]
35
+ dev = ["pytest>=7.0", "pytest-cov>=4.0", "build", "twine", "mypy>=1.0", "pandas-stubs", "scipy-stubs; python_version >= '3.10'", "lifelines>=0.27", "scikit-learn>=1.0"]
36
+ docs = ["sphinx>=7.0", "furo", "myst-parser>=2.0", "sphinx-autodoc-typehints"]
37
+
38
+ [project.urls]
39
+ Repository = "https://github.com/SidharthJoly/36120-26SP-group10-25664929-package"
40
+ Documentation = "https://mclovin.readthedocs.io/"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+
45
+ [tool.setuptools.package-data]
46
+ mclovin = ["py.typed"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+