mclovin 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mclovin/__init__.py +118 -0
- mclovin/basketball/__init__.py +65 -0
- mclovin/basketball/box_metrics.py +916 -0
- mclovin/basketball/coefficients.py +30 -0
- mclovin/basketball/draft.py +954 -0
- mclovin/basketball/four_factors.py +527 -0
- mclovin/basketball/identities.py +55 -0
- mclovin/basketball/possessions.py +247 -0
- mclovin/basketball/reconstruct.py +201 -0
- mclovin/core.py +177 -0
- mclovin/evaluation.py +125 -0
- mclovin/panel/__init__.py +29 -0
- mclovin/panel/features.py +204 -0
- mclovin/panel/splits.py +93 -0
- mclovin/py.typed +0 -0
- mclovin/quality.py +309 -0
- mclovin/schema.py +167 -0
- mclovin/shrinkage.py +615 -0
- mclovin/similarity.py +625 -0
- mclovin/survival.py +221 -0
- mclovin/units.py +253 -0
- mclovin/vocab/__init__.py +128 -0
- mclovin/vocab/basketball.py +175 -0
- mclovin-1.0.0.dist-info/METADATA +319 -0
- mclovin-1.0.0.dist-info/RECORD +28 -0
- mclovin-1.0.0.dist-info/WHEEL +5 -0
- mclovin-1.0.0.dist-info/licenses/LICENSE +21 -0
- mclovin-1.0.0.dist-info/top_level.txt +1 -0
mclovin/__init__.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mclovin
|
|
3
|
+
=======
|
|
4
|
+
|
|
5
|
+
Trustworthy features from messy tabular data.
|
|
6
|
+
|
|
7
|
+
This package started life as basketball code. What survived the move to a
|
|
8
|
+
general-purpose library is the part that was never really about basketball:
|
|
9
|
+
resolving whatever a dataset calls its columns, saying honestly how reliable
|
|
10
|
+
each derived number is, correcting rates computed on small samples, and
|
|
11
|
+
building panel features that do not leak the future into the past.
|
|
12
|
+
|
|
13
|
+
Design principles
|
|
14
|
+
-----------------
|
|
15
|
+
1. **Schema-flexible.** Functions resolve canonical names against whatever
|
|
16
|
+
your DataFrame actually calls them, via :class:`SchemaResolver` pointed at
|
|
17
|
+
a :class:`~mclovin.vocab.Vocabulary`. Point it at a new dataset, extend the
|
|
18
|
+
vocabulary, and everything downstream works unchanged.
|
|
19
|
+
|
|
20
|
+
2. **Honest about provenance.** Many derived quantities need context columns
|
|
21
|
+
(team totals, station baselines, cohort aggregates) that a given dataset
|
|
22
|
+
may not carry. Rather than quietly approximating, every metric declares
|
|
23
|
+
what it needed and reports a reliability tier -- EXACT, APPROXIMATED,
|
|
24
|
+
PASSTHROUGH, DERIVED or UNAVAILABLE -- so you know which numbers can bear
|
|
25
|
+
weight.
|
|
26
|
+
|
|
27
|
+
3. **Leakage-resistant by default.** Rolling windows require a full window,
|
|
28
|
+
horizon joins match on the key rather than on position, and splits are
|
|
29
|
+
chronological. The defaults are the careful choice, not the convenient one.
|
|
30
|
+
|
|
31
|
+
Modules
|
|
32
|
+
-------
|
|
33
|
+
`schema` Canonical-name resolution and dataset coverage auditing
|
|
34
|
+
`vocab` Domain vocabularies: aliases, context markers, rate ceilings
|
|
35
|
+
`core` Shared helpers; the provenance and reliability-tier system
|
|
36
|
+
`panel` Lags, deltas, rolling windows, horizon joins, temporal splits
|
|
37
|
+
`quality` Rate validation, artifact detection, robust standardisation
|
|
38
|
+
`shrinkage` Empirical-Bayes and James-Stein small-sample correction
|
|
39
|
+
`similarity` PCA decomposition, GMM soft archetypes, Mahalanobis neighbours
|
|
40
|
+
`survival` Kaplan-Meier, Cox, and censoring-aware label construction
|
|
41
|
+
`units` Detect and harmonise mixed per-period / cumulative columns
|
|
42
|
+
`evaluation` Shared regression and classification scoring helpers
|
|
43
|
+
`basketball` The original domain pack: Four Factors, BPM/PER, draft features
|
|
44
|
+
|
|
45
|
+
Quick start
|
|
46
|
+
-----------
|
|
47
|
+
import pandas as pd
|
|
48
|
+
from mclovin import SchemaResolver, ComputationReport
|
|
49
|
+
from mclovin.vocab import Vocabulary
|
|
50
|
+
|
|
51
|
+
WEATHER = Vocabulary(
|
|
52
|
+
name="weather",
|
|
53
|
+
aliases={"TEMP_MAX": ["temperature_2m_max", "tmax"]},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
df = pd.read_csv("daily.csv")
|
|
57
|
+
resolver = SchemaResolver(df.columns, vocabulary=WEATHER)
|
|
58
|
+
print(resolver.coverage().query("available"))
|
|
59
|
+
|
|
60
|
+
The basketball metrics that were moneyBBall live on in `mclovin.basketball`
|
|
61
|
+
and are unchanged:
|
|
62
|
+
|
|
63
|
+
import mclovin.basketball as bb
|
|
64
|
+
|
|
65
|
+
resolver = bb.resolver(df.columns)
|
|
66
|
+
df = bb.draft.add_prospect_features(df, resolver=resolver)
|
|
67
|
+
|
|
68
|
+
A caution on interpretation
|
|
69
|
+
---------------------------
|
|
70
|
+
A composite that summarises what already happened is not the same thing as a
|
|
71
|
+
predictor of what happens next, and the two are easy to confuse because they
|
|
72
|
+
are computed from the same columns. Check the reliability tier, check what
|
|
73
|
+
the metric needed, and do not mistake a good description for a good forecast.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
__version__ = version("mclovin")
|
|
80
|
+
except PackageNotFoundError: # pragma: no cover - local/dev install
|
|
81
|
+
__version__ = "0.0.0-dev"
|
|
82
|
+
|
|
83
|
+
from . import (
|
|
84
|
+
core,
|
|
85
|
+
evaluation,
|
|
86
|
+
panel,
|
|
87
|
+
quality,
|
|
88
|
+
schema,
|
|
89
|
+
shrinkage,
|
|
90
|
+
similarity,
|
|
91
|
+
survival,
|
|
92
|
+
units,
|
|
93
|
+
vocab,
|
|
94
|
+
)
|
|
95
|
+
from .core import ComputationReport, MetricSpec, Reliability
|
|
96
|
+
from .schema import SchemaResolver
|
|
97
|
+
from .vocab import Vocabulary
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
# modules
|
|
101
|
+
"schema",
|
|
102
|
+
"vocab",
|
|
103
|
+
"core",
|
|
104
|
+
"panel",
|
|
105
|
+
"quality",
|
|
106
|
+
"shrinkage",
|
|
107
|
+
"similarity",
|
|
108
|
+
"survival",
|
|
109
|
+
"units",
|
|
110
|
+
"evaluation",
|
|
111
|
+
# most-used names
|
|
112
|
+
"SchemaResolver",
|
|
113
|
+
"Vocabulary",
|
|
114
|
+
"ComputationReport",
|
|
115
|
+
"MetricSpec",
|
|
116
|
+
"Reliability",
|
|
117
|
+
"__version__",
|
|
118
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Basketball metrics -- the domain pack.
|
|
2
|
+
|
|
3
|
+
Everything here is basketball and only basketball: Oliver's Four Factors,
|
|
4
|
+
possession estimation, the box-score composites (BPM, PER, Game Score, PIE,
|
|
5
|
+
VORP), the NCAA-to-NBA draft features, and reconstruction of counting stats
|
|
6
|
+
from rate-only college exports.
|
|
7
|
+
|
|
8
|
+
It sits on top of the generic machinery in :mod:`mclovin` rather than being
|
|
9
|
+
mixed into it. Nothing in the core imports this subpackage, which is the
|
|
10
|
+
property that lets the core be used on data that has nothing to do with sport.
|
|
11
|
+
|
|
12
|
+
Provenance and formula citations are unchanged from moneyBBall 0.6.2:
|
|
13
|
+
Oliver's *Basketball on Paper*, Kubatko et al. (2007) in *JQAS*, Myers' BPM
|
|
14
|
+
2.0, Hollinger's PER, Sill's RAPM regularisation, Pelton's age adjustment.
|
|
15
|
+
|
|
16
|
+
import pandas as pd
|
|
17
|
+
import mclovin.basketball as bb
|
|
18
|
+
|
|
19
|
+
df = pd.read_csv("train.csv", encoding="latin1")
|
|
20
|
+
resolver = bb.resolver(df.columns) # basketball vocabulary supplied
|
|
21
|
+
df = bb.draft.add_prospect_features(df, resolver=resolver)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from typing import Iterable
|
|
27
|
+
|
|
28
|
+
from ..schema import SchemaResolver
|
|
29
|
+
from ..vocab import register
|
|
30
|
+
from ..vocab.basketball import BASKETBALL
|
|
31
|
+
from . import box_metrics, draft, four_factors, possessions, reconstruct
|
|
32
|
+
from .identities import verify_scoring_identity
|
|
33
|
+
from .coefficients import (
|
|
34
|
+
FTA_POSSESSION_COEFFICIENT_NBA,
|
|
35
|
+
FTA_POSSESSION_COEFFICIENT_NCAA,
|
|
36
|
+
fta_coefficient,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
register(BASKETBALL)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def resolver(columns: Iterable[str], **kwargs) -> SchemaResolver:
|
|
43
|
+
"""A :class:`~mclovin.schema.SchemaResolver` loaded with the basketball
|
|
44
|
+
vocabulary.
|
|
45
|
+
|
|
46
|
+
Shorthand for ``SchemaResolver(columns, vocabulary=BASKETBALL)``. Pass
|
|
47
|
+
``extra_aliases=`` to handle a dataset-specific quirk, exactly as before.
|
|
48
|
+
"""
|
|
49
|
+
kwargs.setdefault("vocabulary", BASKETBALL)
|
|
50
|
+
return SchemaResolver(columns, **kwargs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"box_metrics",
|
|
55
|
+
"draft",
|
|
56
|
+
"four_factors",
|
|
57
|
+
"possessions",
|
|
58
|
+
"reconstruct",
|
|
59
|
+
"verify_scoring_identity",
|
|
60
|
+
"resolver",
|
|
61
|
+
"BASKETBALL",
|
|
62
|
+
"fta_coefficient",
|
|
63
|
+
"FTA_POSSESSION_COEFFICIENT_NBA",
|
|
64
|
+
"FTA_POSSESSION_COEFFICIENT_NCAA",
|
|
65
|
+
]
|