proxyml 0.3.0__tar.gz → 0.4.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.
- {proxyml-0.3.0 → proxyml-0.4.0}/PKG-INFO +2 -2
- {proxyml-0.3.0 → proxyml-0.4.0}/pyproject.toml +2 -2
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml/__init__.py +5 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml/local/__init__.py +2 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml/local/challenger.py +26 -8
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml.egg-info/PKG-INFO +2 -2
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml.egg-info/requires.txt +1 -1
- {proxyml-0.3.0 → proxyml-0.4.0}/tests/test_local_challenger.py +42 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/LICENSE +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/README.md +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/setup.cfg +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml/client.py +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml/schema_builder.py +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml.egg-info/SOURCES.txt +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml.egg-info/dependency_links.txt +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/src/proxyml.egg-info/top_level.txt +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/tests/test_client.py +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/tests/test_dependency_boundaries.py +0 -0
- {proxyml-0.3.0 → proxyml-0.4.0}/tests/test_schema_builder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxyml
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Python SDK for calling the ProxyML API
|
|
5
5
|
Author-email: ProxyML <contact@proxyml.ai>
|
|
6
6
|
License: Apache License
|
|
@@ -211,7 +211,7 @@ Requires-Dist: pandas>=1.5
|
|
|
211
211
|
Requires-Dist: orjson>=3.9
|
|
212
212
|
Requires-Dist: proxyml-core>=0.1
|
|
213
213
|
Provides-Extra: local
|
|
214
|
-
Requires-Dist: proxyml-core[modeling]>=0.
|
|
214
|
+
Requires-Dist: proxyml-core[modeling]>=0.2; extra == "local"
|
|
215
215
|
Provides-Extra: dev
|
|
216
216
|
Requires-Dist: build; extra == "dev"
|
|
217
217
|
Requires-Dist: twine; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proxyml"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Python SDK for calling the ProxyML API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {file = "LICENSE"}
|
|
@@ -35,7 +35,7 @@ dependencies = [
|
|
|
35
35
|
|
|
36
36
|
[project.optional-dependencies]
|
|
37
37
|
local = [
|
|
38
|
-
"proxyml-core[modeling]>=0.
|
|
38
|
+
"proxyml-core[modeling]>=0.2",
|
|
39
39
|
]
|
|
40
40
|
dev = [
|
|
41
41
|
"build",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from importlib.metadata import version as _pkg_version
|
|
2
|
+
|
|
1
3
|
from proxyml.client import (
|
|
2
4
|
health_check,
|
|
3
5
|
put_schema,
|
|
@@ -27,7 +29,10 @@ from proxyml.client import (
|
|
|
27
29
|
)
|
|
28
30
|
from proxyml.schema_builder import get_schema
|
|
29
31
|
|
|
32
|
+
__version__ = _pkg_version("proxyml")
|
|
33
|
+
|
|
30
34
|
__all__ = [
|
|
35
|
+
"__version__",
|
|
31
36
|
"health_check",
|
|
32
37
|
"put_schema",
|
|
33
38
|
"fetch_schema",
|
|
@@ -10,6 +10,7 @@ from proxyml.local.challenger import (
|
|
|
10
10
|
Complexity,
|
|
11
11
|
Rung,
|
|
12
12
|
TrainedChallenger,
|
|
13
|
+
score_champion,
|
|
13
14
|
train_auto_challenger,
|
|
14
15
|
train_challenger,
|
|
15
16
|
)
|
|
@@ -17,6 +18,7 @@ from proxyml.local.challenger import (
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
"train_challenger",
|
|
19
20
|
"train_auto_challenger",
|
|
21
|
+
"score_champion",
|
|
20
22
|
"Complexity",
|
|
21
23
|
"Rung",
|
|
22
24
|
"TrainedChallenger",
|
|
@@ -10,7 +10,7 @@ training target was real ground truth or a black box's predictions.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
from dataclasses import dataclass
|
|
13
|
+
from dataclasses import dataclass, replace
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from typing import Any, Callable, Literal
|
|
@@ -19,7 +19,6 @@ import numpy as np
|
|
|
19
19
|
import pandas as pd
|
|
20
20
|
from sklearn.base import BaseEstimator
|
|
21
21
|
from sklearn.linear_model import LogisticRegressionCV, RidgeCV
|
|
22
|
-
from sklearn.metrics import f1_score, r2_score
|
|
23
22
|
from sklearn.model_selection import train_test_split
|
|
24
23
|
from sklearn.pipeline import Pipeline
|
|
25
24
|
|
|
@@ -34,6 +33,7 @@ from proxyml_core.modeling.estimators import (
|
|
|
34
33
|
)
|
|
35
34
|
from proxyml_core.modeling.extract import extract_export_data
|
|
36
35
|
from proxyml_core.modeling.preprocess import build_preprocessor
|
|
36
|
+
from proxyml_core.modeling.scoring import score_predictions
|
|
37
37
|
from proxyml_core.schema import Feature, FeatureSchema
|
|
38
38
|
|
|
39
39
|
|
|
@@ -184,14 +184,10 @@ def train_challenger(
|
|
|
184
184
|
hyperparameters = extract_hyperparameters(pipeline.named_steps["estimator"])
|
|
185
185
|
y_pred = pipeline.predict(X_test)
|
|
186
186
|
|
|
187
|
-
metrics
|
|
188
|
-
if classification:
|
|
189
|
-
metrics["f1"] = float(f1_score(y_test, y_pred, average="weighted", zero_division=0))
|
|
190
|
-
metrics["accuracy"] = float((y_test == y_pred).mean())
|
|
191
|
-
else:
|
|
192
|
-
metrics["r2"] = float(r2_score(y_test, y_pred))
|
|
187
|
+
metrics = score_predictions(y_test, y_pred, task=resolved_task)
|
|
193
188
|
|
|
194
189
|
export = extract_export_data(pipeline, features, resolved_task)
|
|
190
|
+
export = replace(export, hyperparameters=hyperparameters, metrics=metrics)
|
|
195
191
|
|
|
196
192
|
return TrainedChallenger(
|
|
197
193
|
pipeline=pipeline,
|
|
@@ -202,6 +198,28 @@ def train_challenger(
|
|
|
202
198
|
)
|
|
203
199
|
|
|
204
200
|
|
|
201
|
+
def score_champion(
|
|
202
|
+
labels: np.ndarray | list,
|
|
203
|
+
predictions: np.ndarray | list,
|
|
204
|
+
*,
|
|
205
|
+
task: Literal["classification", "regression"],
|
|
206
|
+
) -> dict[str, float]:
|
|
207
|
+
"""Score a champion model's predictions against real labels, locally.
|
|
208
|
+
|
|
209
|
+
Uses the exact same scoring code as ``train_challenger()``'s internal
|
|
210
|
+
fidelity metrics, so ``champion_metrics`` and a paired
|
|
211
|
+
``TrainedChallenger.metrics`` are computed identically — required for an
|
|
212
|
+
apples-to-apples champion-vs-challenger comparison. Pass the same task
|
|
213
|
+
the paired challenger resolved to (e.g. ``result.task``); there's no
|
|
214
|
+
``task="auto"`` here, since letting the two resolve independently risks
|
|
215
|
+
them silently diverging.
|
|
216
|
+
|
|
217
|
+
Returns ``{"f1":..., "accuracy":...}`` for classification or ``{"r2":...}``
|
|
218
|
+
for regression — the same shape as ``TrainedChallenger.metrics``.
|
|
219
|
+
"""
|
|
220
|
+
return score_predictions(np.asarray(labels), np.asarray(predictions), task=task)
|
|
221
|
+
|
|
222
|
+
|
|
205
223
|
def train_auto_challenger(
|
|
206
224
|
data: str | Path | pd.DataFrame,
|
|
207
225
|
target_col: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxyml
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Python SDK for calling the ProxyML API
|
|
5
5
|
Author-email: ProxyML <contact@proxyml.ai>
|
|
6
6
|
License: Apache License
|
|
@@ -211,7 +211,7 @@ Requires-Dist: pandas>=1.5
|
|
|
211
211
|
Requires-Dist: orjson>=3.9
|
|
212
212
|
Requires-Dist: proxyml-core>=0.1
|
|
213
213
|
Provides-Extra: local
|
|
214
|
-
Requires-Dist: proxyml-core[modeling]>=0.
|
|
214
|
+
Requires-Dist: proxyml-core[modeling]>=0.2; extra == "local"
|
|
215
215
|
Provides-Extra: dev
|
|
216
216
|
Requires-Dist: build; extra == "dev"
|
|
217
217
|
Requires-Dist: twine; extra == "dev"
|
|
@@ -6,6 +6,7 @@ from proxyml.local import (
|
|
|
6
6
|
Complexity,
|
|
7
7
|
LADDERS,
|
|
8
8
|
TrainedChallenger,
|
|
9
|
+
score_champion,
|
|
9
10
|
train_auto_challenger,
|
|
10
11
|
train_challenger,
|
|
11
12
|
)
|
|
@@ -139,6 +140,47 @@ def test_train_auto_challenger_matches_manual_schema_and_train():
|
|
|
139
140
|
)
|
|
140
141
|
|
|
141
142
|
|
|
143
|
+
def test_score_champion_matches_train_challenger_metric_shape_classification():
|
|
144
|
+
schema = _schema()
|
|
145
|
+
df = _df(seed=9, n=300)
|
|
146
|
+
target = np.where(df["age"] > 50, "senior", "junior")
|
|
147
|
+
|
|
148
|
+
result = train_challenger(df, target, schema, complexity=Complexity.MODERATE, task="classification")
|
|
149
|
+
champion_metrics = score_champion(target, target, task="classification")
|
|
150
|
+
assert set(champion_metrics) == set(result.metrics)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_score_champion_matches_train_challenger_metric_shape_regression():
|
|
154
|
+
schema = _schema()
|
|
155
|
+
df = _df(seed=10)
|
|
156
|
+
target = df["age"] * 0.5 + df["income"] * 0.0001
|
|
157
|
+
|
|
158
|
+
result = train_challenger(df, target, schema, complexity=Complexity.MODERATE, task="regression")
|
|
159
|
+
champion_metrics = score_champion(target, target, task="regression")
|
|
160
|
+
assert set(champion_metrics) == set(result.metrics)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_score_champion_uses_same_scoring_as_train_challenger():
|
|
164
|
+
# Feed score_champion the exact labels/predictions a regression fit produced
|
|
165
|
+
# internally, and assert numeric equality with the challenger's own r2 —
|
|
166
|
+
# proving the two aren't drifting copies of the same formula.
|
|
167
|
+
schema = _schema()
|
|
168
|
+
df = _df(seed=11, n=300)
|
|
169
|
+
target = df["age"] * 0.5 + df["income"] * 0.0001
|
|
170
|
+
|
|
171
|
+
result = train_challenger(df, target, schema, complexity=Complexity.MODERATE, task="regression")
|
|
172
|
+
X = df[["age", "income"]].to_numpy(dtype=object)
|
|
173
|
+
y_pred = result.pipeline.predict(X)
|
|
174
|
+
reproduced_metrics = score_champion(target, y_pred, task="regression")
|
|
175
|
+
|
|
176
|
+
# Not identical to result.metrics (that was scored on a held-out test split,
|
|
177
|
+
# this is scored on the full data) but both must use the same r2 formula —
|
|
178
|
+
# verify by comparing against sklearn directly.
|
|
179
|
+
from sklearn.metrics import r2_score
|
|
180
|
+
|
|
181
|
+
assert reproduced_metrics["r2"] == pytest.approx(r2_score(target, y_pred))
|
|
182
|
+
|
|
183
|
+
|
|
142
184
|
def test_train_auto_challenger_passes_immutable_cols_to_get_schema():
|
|
143
185
|
from unittest.mock import patch
|
|
144
186
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|