proxyml 0.1.6__tar.gz → 0.1.7__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.1.6 → proxyml-0.1.7}/PKG-INFO +3 -1
- {proxyml-0.1.6 → proxyml-0.1.7}/README.md +2 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/pyproject.toml +1 -1
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml/client.py +200 -10
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml/schema.py +36 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml.egg-info/PKG-INFO +3 -1
- {proxyml-0.1.6 → proxyml-0.1.7}/LICENSE +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/setup.cfg +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml/__init__.py +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml.egg-info/SOURCES.txt +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml.egg-info/dependency_links.txt +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml.egg-info/requires.txt +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/src/proxyml.egg-info/top_level.txt +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/tests/test_client.py +0 -0
- {proxyml-0.1.6 → proxyml-0.1.7}/tests/test_schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxyml
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Python SDK for calling the ProxyML API
|
|
5
5
|
Author-email: ProxyML <contact@proxyml.ai>
|
|
6
6
|
License: Apache License
|
|
@@ -223,6 +223,8 @@ Python SDK for the [ProxyML API](https://proxyml.ai).
|
|
|
223
223
|
> **Status:** Early access — server endpoints coming soon.
|
|
224
224
|
> [Request early access](mailto:contact@proxyml.ai) or star this repo to follow progress.
|
|
225
225
|
|
|
226
|
+
<img width="577" height="115" alt="creditg_car_counterfactual" src="https://github.com/user-attachments/assets/8f913c95-1190-4b3c-ba7d-6690e9ebb3e0" />
|
|
227
|
+
|
|
226
228
|
## Why ProxyML?
|
|
227
229
|
|
|
228
230
|
Most explainability tools require sending your data to a third-party server.
|
|
@@ -5,6 +5,8 @@ Python SDK for the [ProxyML API](https://proxyml.ai).
|
|
|
5
5
|
> **Status:** Early access — server endpoints coming soon.
|
|
6
6
|
> [Request early access](mailto:contact@proxyml.ai) or star this repo to follow progress.
|
|
7
7
|
|
|
8
|
+
<img width="577" height="115" alt="creditg_car_counterfactual" src="https://github.com/user-attachments/assets/8f913c95-1190-4b3c-ba7d-6690e9ebb3e0" />
|
|
9
|
+
|
|
8
10
|
## Why ProxyML?
|
|
9
11
|
|
|
10
12
|
Most explainability tools require sending your data to a third-party server.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
logger = logging.getLogger(__name__)
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
import requests
|
|
5
6
|
import os
|
|
@@ -14,6 +15,9 @@ _BOOL_STRINGS = {"true", "false"}
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def _headers() -> dict:
|
|
18
|
+
"""
|
|
19
|
+
Constructs the request headers required for making calls to the ProxyML API.
|
|
20
|
+
"""
|
|
17
21
|
api_key = os.getenv("PROXYML_API_KEY", "")
|
|
18
22
|
if not api_key:
|
|
19
23
|
raise EnvironmentError(
|
|
@@ -27,6 +31,16 @@ def _headers() -> dict:
|
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
def post(endpoint: str, payload: dict) -> requests.models.Response:
|
|
34
|
+
"""
|
|
35
|
+
POSTs a request to a ProxyML API endpoint.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
endpoint (str): ProxyML API endpoint. PROXYML_BASE_URL is prepended e.g. use endpoint='/schemas' not 'https://api.proxyml.ai/api/v1/schemas'.
|
|
39
|
+
payload (dict): JSON payload to POST.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
requests Response object.
|
|
43
|
+
"""
|
|
30
44
|
r = requests.post(
|
|
31
45
|
url=f'{PROXYML_BASE_URL}{endpoint}',
|
|
32
46
|
json=payload,
|
|
@@ -36,6 +50,16 @@ def post(endpoint: str, payload: dict) -> requests.models.Response:
|
|
|
36
50
|
|
|
37
51
|
|
|
38
52
|
def put(endpoint: str, payload: dict) -> requests.models.Response:
|
|
53
|
+
"""
|
|
54
|
+
PUTs a request to a ProxyML API endpoint.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
endpoint (str): ProxyML API endpoint. PROXYML_BASE_URL is prepended e.g. use endpoint='/schemas' not 'https://api.proxyml.ai/api/v1/schemas'.
|
|
58
|
+
payload (dict): JSON payload to POST.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
requests Response object.
|
|
62
|
+
"""
|
|
39
63
|
r = requests.put(
|
|
40
64
|
url=f'{PROXYML_BASE_URL}{endpoint}',
|
|
41
65
|
json=payload,
|
|
@@ -45,6 +69,16 @@ def put(endpoint: str, payload: dict) -> requests.models.Response:
|
|
|
45
69
|
|
|
46
70
|
|
|
47
71
|
def get(endpoint: str, params: dict) -> requests.models.Response:
|
|
72
|
+
"""
|
|
73
|
+
GETs a request to a ProxyML API endpoint.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
endpoint (str): ProxyML API endpoint. PROXYML_BASE_URL is prepended e.g. use endpoint='/schemas' not 'https://api.proxyml.ai/api/v1/schemas'.
|
|
77
|
+
params (dict): JSON payload to POST.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
requests Response object.
|
|
81
|
+
"""
|
|
48
82
|
r = requests.get(
|
|
49
83
|
url=f'{PROXYML_BASE_URL}{endpoint}',
|
|
50
84
|
headers=_headers(),
|
|
@@ -54,6 +88,15 @@ def get(endpoint: str, params: dict) -> requests.models.Response:
|
|
|
54
88
|
|
|
55
89
|
|
|
56
90
|
def delete(endpoint: str) -> requests.models.Response:
|
|
91
|
+
"""
|
|
92
|
+
DELETE request to a ProxyML API endpoint.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
endpoint (str): ProxyML API endpoint. PROXYML_BASE_URL is prepended e.g. use endpoint='/schemas' not 'https://api.proxyml.ai/api/v1/schemas'.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
requests Response object.
|
|
99
|
+
"""
|
|
57
100
|
r = requests.delete(
|
|
58
101
|
url=f'{PROXYML_BASE_URL}{endpoint}',
|
|
59
102
|
headers=_headers()
|
|
@@ -61,7 +104,16 @@ def delete(endpoint: str) -> requests.models.Response:
|
|
|
61
104
|
return r
|
|
62
105
|
|
|
63
106
|
|
|
64
|
-
def put_schema(schema: dict, name: str = "default"):
|
|
107
|
+
def put_schema(schema: dict, name: str = "default") -> Any:
|
|
108
|
+
"""
|
|
109
|
+
Uploads a data schema.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
schema (dict): data schema object
|
|
113
|
+
name (str): optional name for the schema, defaults to "default".
|
|
114
|
+
Returns:
|
|
115
|
+
API response JSON object, or None if the return code was not 200.
|
|
116
|
+
"""
|
|
65
117
|
r = put(endpoint=f'/schema/{name}', payload=schema)
|
|
66
118
|
if r.status_code == 200:
|
|
67
119
|
logger.info("Schema '%s' uploaded successfully", name)
|
|
@@ -117,6 +169,15 @@ def delete_schema(name: str) -> bool:
|
|
|
117
169
|
|
|
118
170
|
|
|
119
171
|
def _cast_column(series: pd.Series, ftype: str) -> pd.Series:
|
|
172
|
+
"""
|
|
173
|
+
Casts a pandas Series to a Python type based on a specified ftype.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
series (pd.Series): data to cast
|
|
177
|
+
ftype (str): type of conversion, one of "continuous", "count", "numeric_ordinal", "categorical", or "categorical_ordinal".
|
|
178
|
+
Returns:
|
|
179
|
+
pd.Series cast to specific ftype.
|
|
180
|
+
"""
|
|
120
181
|
if ftype in ("continuous",):
|
|
121
182
|
return series.astype(float)
|
|
122
183
|
if ftype in ("count", "numeric_ordinal"):
|
|
@@ -129,7 +190,18 @@ def _cast_column(series: pd.Series, ftype: str) -> pd.Series:
|
|
|
129
190
|
return series
|
|
130
191
|
|
|
131
192
|
|
|
132
|
-
def synthesize_data(num_points: int = 100, sample: list | None = None, as_df: bool = True, schema_name: str = "default"):
|
|
193
|
+
def synthesize_data(num_points: int = 100, sample: list | None = None, as_df: bool = True, schema_name: str = "default") -> Any:
|
|
194
|
+
"""
|
|
195
|
+
Synthesizes data based on a data schema.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
num_points (int): number of samples to synthesize, defaults to 100.
|
|
199
|
+
sample (list): if specified, synthesized data will be a blend of samples generated from the schema and a number of perturbations around this sample. If not specified (default), all synthesized data is generated by sampling from the data schema.
|
|
200
|
+
as_df (bool): if True (default), data are returned as a pandas DataFrame.
|
|
201
|
+
schema_name (str): name of the data schema to use to generate the data, defaults to "default."
|
|
202
|
+
Returns:
|
|
203
|
+
JSON object representing the synthesized data if as_df=False, a pandas DataFrame if as_df=True, or None if an error occurred.
|
|
204
|
+
"""
|
|
133
205
|
if sample is None:
|
|
134
206
|
r = post(endpoint='/synthesize/neighbors', payload={'n': num_points, 'schema_name': schema_name})
|
|
135
207
|
else:
|
|
@@ -159,7 +231,22 @@ def train_surrogate(
|
|
|
159
231
|
schema_name: str = "default",
|
|
160
232
|
name: str | None = None,
|
|
161
233
|
comments: str | None = None,
|
|
162
|
-
):
|
|
234
|
+
) -> Any:
|
|
235
|
+
"""
|
|
236
|
+
Trains a surrogate model to predict a "black box" model's predictions.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
samples (list): list of samples that were provided to the black box for inference, e.g. the output from synthesize_data().
|
|
240
|
+
predictions (list): the inferences on the samples, made by the black box model.
|
|
241
|
+
feature_names (list): names of the features (columns) in the data.
|
|
242
|
+
task (str): specifies the modeling task, one of "classification," "regression," or "auto" in which case ProxyML will attempt to automatically determine the modeling task.
|
|
243
|
+
test_size (float): fraction of the data to set aside for test data, defaults to 0.2.
|
|
244
|
+
schema_name (str): name of the data schema to use, defaults to "default."
|
|
245
|
+
name (str): an optional name for the surrogate.
|
|
246
|
+
comments (str): an optional comment string for the surrogate.
|
|
247
|
+
Returns:
|
|
248
|
+
JSON object denoting surrogate training result, or None if an error occurred.
|
|
249
|
+
"""
|
|
163
250
|
payload = {
|
|
164
251
|
'samples': samples,
|
|
165
252
|
'predictions': predictions,
|
|
@@ -183,7 +270,16 @@ def train_surrogate(
|
|
|
183
270
|
return None
|
|
184
271
|
|
|
185
272
|
|
|
186
|
-
def predict(sample: list, version: str | None = None):
|
|
273
|
+
def predict(sample: list, version: str | None = None) -> Any:
|
|
274
|
+
"""
|
|
275
|
+
Calls a surrogate model to make a single prediction.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
sample (list): a single sample
|
|
279
|
+
version (str): name of the surrogate model to use.
|
|
280
|
+
Returns:
|
|
281
|
+
JSON object denoting result of the surrogate prediction, or None if an error occurred.
|
|
282
|
+
"""
|
|
187
283
|
payload = {'inputs': sample}
|
|
188
284
|
if version is not None:
|
|
189
285
|
payload['version'] = version
|
|
@@ -198,7 +294,21 @@ def predict(sample: list, version: str | None = None):
|
|
|
198
294
|
return None
|
|
199
295
|
|
|
200
296
|
|
|
201
|
-
def find_counterfactual(sample, target, n_neighbors: int = 10000, perturbation_scale: float = 0.1, version: str | None = None, as_df: bool = True):
|
|
297
|
+
def find_counterfactual(sample, target, n_neighbors: int = 10000, perturbation_scale: float = 0.1, version: str | None = None, as_df: bool = True) -> Any:
|
|
298
|
+
"""
|
|
299
|
+
Attempts to find a counterfactual sample: a sample that is near to a given sample in featurespace, but with a specified prediction. Counterfactuals are
|
|
300
|
+
not guaranteed i.e., it's possible to not be able to find a nearby sample with the desired label / target value.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
sample (list): point around which neighbors will be found.
|
|
304
|
+
target (Any): the desired prediction outcome, can be a string or number for classification or a target predicted value for regression.
|
|
305
|
+
n_neighbors (int): the number of neighbors to search, defaults to 10000.
|
|
306
|
+
pertubation_scale (float): controls the "wiggle" in the features around the provided sample. Defaults to 0.1.
|
|
307
|
+
version (str): name of the surrogate model to run, defaults to "default."
|
|
308
|
+
as_df (bool): if True, return the counterfactual as a dataframe. If False, returns JSON object.
|
|
309
|
+
Returns:
|
|
310
|
+
JSON counterfactual if as_df=False, pandas DataFrame if as_df=True, None if an error occurred.
|
|
311
|
+
"""
|
|
202
312
|
payload = {
|
|
203
313
|
'instance': sample,
|
|
204
314
|
'target_label': target.item() if hasattr(target, 'item') else target,
|
|
@@ -233,6 +343,17 @@ def interpret_counterfactual(
|
|
|
233
343
|
prediction_changed: bool,
|
|
234
344
|
exclude_from_diff: list[str] | None
|
|
235
345
|
) -> str:
|
|
346
|
+
"""
|
|
347
|
+
Simple string interpretation of a counterfactual result. No API calls are required.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
sample (dict): the original prediction i.e. the sample from which counterfactuals were drawn.
|
|
351
|
+
counterfactual (dict): the counterfactual returned by ProxyML.
|
|
352
|
+
prediction_changed (bool): True if the prediction changed, False if it did not (or didn't change as much as desired).
|
|
353
|
+
exclude_from_diff (list): list of keys that should not be considered in interpreting the counterfactual.
|
|
354
|
+
Returns:
|
|
355
|
+
String summarizing the differences (if any) observed between sample and counterfactual.
|
|
356
|
+
"""
|
|
236
357
|
if not exclude_from_diff:
|
|
237
358
|
exclude_from_diff = list()
|
|
238
359
|
diffs = {
|
|
@@ -264,7 +385,16 @@ def interpret_counterfactual(
|
|
|
264
385
|
)
|
|
265
386
|
|
|
266
387
|
|
|
267
|
-
def predict_batch(samples: list[list], version: str | None = None):
|
|
388
|
+
def predict_batch(samples: list[list], version: str | None = None) -> Any:
|
|
389
|
+
"""
|
|
390
|
+
Calls a surrogate model to make batch predictions.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
sample (list): a list of samples
|
|
394
|
+
version (str): name of the surrogate model to use.
|
|
395
|
+
Returns:
|
|
396
|
+
JSON object denoting result of the surrogate prediction, or None if an error occurred.
|
|
397
|
+
"""
|
|
268
398
|
payload = {'inputs': samples}
|
|
269
399
|
if version is not None:
|
|
270
400
|
payload['version'] = version
|
|
@@ -286,7 +416,21 @@ def find_counterfactuals(
|
|
|
286
416
|
perturbation_scale: float = 0.1,
|
|
287
417
|
version: str | None = None,
|
|
288
418
|
as_dfs: bool = True,
|
|
289
|
-
):
|
|
419
|
+
) -> Any:
|
|
420
|
+
"""
|
|
421
|
+
Attempts to find counterfactual samples: samples that are near a given sample in featurespace, but with a specified prediction. Counterfactuals are
|
|
422
|
+
not guaranteed i.e., it's possible to not be able to find a nearby sample with the desired label / target value.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
samples (list): list of points around which neighbors will be found.
|
|
426
|
+
target (Any): the desired prediction outcome, can be a string or number for classification or a target predicted value for regression.
|
|
427
|
+
n_neighbors (int): the number of neighbors to search, defaults to 10000.
|
|
428
|
+
pertubation_scale (float): controls the "wiggle" in the features around the provided sample. Defaults to 0.1.
|
|
429
|
+
version (str): name of the surrogate model to run, defaults to "default."
|
|
430
|
+
as_df (bool): if True, return the counterfactual as a dataframe. If False, returns JSON object.
|
|
431
|
+
Returns:
|
|
432
|
+
JSON counterfactuals if as_df=False, pandas DataFrame if as_df=True, None if an error occurred.
|
|
433
|
+
"""
|
|
290
434
|
payload = {
|
|
291
435
|
'instances': samples,
|
|
292
436
|
'target_label': target.item() if hasattr(target, 'item') else target,
|
|
@@ -322,7 +466,15 @@ def find_counterfactuals(
|
|
|
322
466
|
return None
|
|
323
467
|
|
|
324
468
|
|
|
325
|
-
def get_model_summary(version: str | None = None):
|
|
469
|
+
def get_model_summary(version: str | None = None) -> Any:
|
|
470
|
+
"""
|
|
471
|
+
Retrieves a summary of a given surrogate.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
version (str): name of the surrogate model to retrieve.
|
|
475
|
+
Returns:
|
|
476
|
+
JSON object summarizing the surrogate, or None if an error occurred.
|
|
477
|
+
"""
|
|
326
478
|
params = {'version': version} if version is not None else {}
|
|
327
479
|
r = get(endpoint='/explain/summary', params=params)
|
|
328
480
|
if r.status_code == 200:
|
|
@@ -335,7 +487,37 @@ def get_model_summary(version: str | None = None):
|
|
|
335
487
|
return None
|
|
336
488
|
|
|
337
489
|
|
|
338
|
-
def
|
|
490
|
+
def get_model_schema(version: str) -> Any:
|
|
491
|
+
"""
|
|
492
|
+
Retrieves the schema associated with a particular surrogate.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
version (str): name of the surrogate model.
|
|
496
|
+
Returns:
|
|
497
|
+
JSON object representation of the model's schema, or None if an error occurred.
|
|
498
|
+
"""
|
|
499
|
+
r = get(endpoint=f'/surrogate/models/{version}/schema', params=dict())
|
|
500
|
+
if r.status_code == 200:
|
|
501
|
+
return r.json()
|
|
502
|
+
logger.error(
|
|
503
|
+
"Schema retrieval failed with status %s: %s",
|
|
504
|
+
r.status_code,
|
|
505
|
+
r.text,
|
|
506
|
+
)
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def diff_models(version_a: str, version_b: str) -> Any:
|
|
511
|
+
"""
|
|
512
|
+
Returns a summary of the differences between two surrogates. The surrogates must have the same model task (i.e. a classification model can't be compared with a regression model), and they must
|
|
513
|
+
have at least one feature in common.
|
|
514
|
+
|
|
515
|
+
Args:
|
|
516
|
+
version_a (str): name of one surrogate
|
|
517
|
+
version_b (str): name of the other surrogate
|
|
518
|
+
Returns:
|
|
519
|
+
JSON object summarizing the difference between version_a and version_b, or None if an error occurred.
|
|
520
|
+
"""
|
|
339
521
|
r = get(endpoint='/explain/diff', params={'version_a': version_a, 'version_b': version_b})
|
|
340
522
|
if r.status_code == 200:
|
|
341
523
|
return r.json()
|
|
@@ -347,7 +529,15 @@ def diff_models(version_a: str, version_b: str):
|
|
|
347
529
|
return None
|
|
348
530
|
|
|
349
531
|
|
|
350
|
-
def get_feature_importances(version: str | None = None):
|
|
532
|
+
def get_feature_importances(version: str | None = None) -> Any:
|
|
533
|
+
"""
|
|
534
|
+
Convenience function to directly retrieve feature importances from a surrogate model.
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
version (str): ID of the surrogate
|
|
538
|
+
Returns:
|
|
539
|
+
JSON object of feature importances, or None if an error occurred.
|
|
540
|
+
"""
|
|
351
541
|
params = {'version': version} if version is not None else {}
|
|
352
542
|
r = get(endpoint='/explain/importance', params=params)
|
|
353
543
|
if r.status_code == 200:
|
|
@@ -8,6 +8,15 @@ from pandas.api.types import is_float_dtype, is_integer_dtype
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def gen_continuous_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Generates a schema entry for continuous data.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
s (pandas Series): data to characterize
|
|
16
|
+
name (str): optional name for the entry, defaults to s.name.
|
|
17
|
+
Returns:
|
|
18
|
+
schema dict
|
|
19
|
+
"""
|
|
11
20
|
return {
|
|
12
21
|
'type': 'continuous',
|
|
13
22
|
'name': name or s.name,
|
|
@@ -19,6 +28,15 @@ def gen_continuous_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
|
19
28
|
|
|
20
29
|
|
|
21
30
|
def gen_categorical_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
31
|
+
"""
|
|
32
|
+
Generates a schema entry for categorical data.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
s (pandas Series): data to characterize
|
|
36
|
+
name (str): optional name for the entry, defaults to s.name.
|
|
37
|
+
Returns:
|
|
38
|
+
schema dict
|
|
39
|
+
"""
|
|
22
40
|
counts = s.value_counts(normalize=True)
|
|
23
41
|
return {
|
|
24
42
|
'type': 'categorical',
|
|
@@ -28,6 +46,15 @@ def gen_categorical_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
|
28
46
|
|
|
29
47
|
|
|
30
48
|
def gen_discrete_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
49
|
+
"""
|
|
50
|
+
Generates a schema entry for discrete data.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
s (pandas Series): data to characterize
|
|
54
|
+
name (str): optional name for the entry, defaults to s.name.
|
|
55
|
+
Returns:
|
|
56
|
+
schema dict
|
|
57
|
+
"""
|
|
31
58
|
return {
|
|
32
59
|
'type': 'count',
|
|
33
60
|
'name': name or s.name,
|
|
@@ -37,6 +64,15 @@ def gen_discrete_schema(s: pd.Series, name: str | None = None) -> dict:
|
|
|
37
64
|
|
|
38
65
|
|
|
39
66
|
def get_schema(df: pd.DataFrame, immutable_cols: list[str] | None) -> dict:
|
|
67
|
+
"""
|
|
68
|
+
Generates a data schema for a pandas DataFrame, based on the data types of the columns.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
df (pandas DataFrame): data to characterize
|
|
72
|
+
immutable_cols (list): list of columns to consider immutable. These columns will have schema entries, but the surrogate will _not_ use them for inference.
|
|
73
|
+
Returns:
|
|
74
|
+
data schema as dict. Review and adjust as needed.
|
|
75
|
+
"""
|
|
40
76
|
schema = {
|
|
41
77
|
'features': list(),
|
|
42
78
|
'_note': (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxyml
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Python SDK for calling the ProxyML API
|
|
5
5
|
Author-email: ProxyML <contact@proxyml.ai>
|
|
6
6
|
License: Apache License
|
|
@@ -223,6 +223,8 @@ Python SDK for the [ProxyML API](https://proxyml.ai).
|
|
|
223
223
|
> **Status:** Early access — server endpoints coming soon.
|
|
224
224
|
> [Request early access](mailto:contact@proxyml.ai) or star this repo to follow progress.
|
|
225
225
|
|
|
226
|
+
<img width="577" height="115" alt="creditg_car_counterfactual" src="https://github.com/user-attachments/assets/8f913c95-1190-4b3c-ba7d-6690e9ebb3e0" />
|
|
227
|
+
|
|
226
228
|
## Why ProxyML?
|
|
227
229
|
|
|
228
230
|
Most explainability tools require sending your data to a third-party server.
|
|
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
|