xplainable-preprocessing 0.2.2__tar.gz → 0.2.3__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.
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/PKG-INFO +1 -1
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/pyproject.toml +1 -1
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/compiler.py +33 -4
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_compiler.py +82 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/.github/workflows/publish-pypi.yml +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/.gitignore +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/README.md +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/dag-pipeline-proposal.md +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/feature-pipeline-architectures.md +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/feature-store-proposal.md +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/__init__.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/pipeline.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/preview.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/registry.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/sandbox.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/schema.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/serialization.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/__init__.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/category_condense.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/clip.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/datetime_extract.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/drop_columns.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/expression.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/fill_missing.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/groupby_agg.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/grouped_lag.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/missing_flag.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/rename_columns.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/rolling_agg.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/text_clean.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/type_cast.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/__init__.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_preview.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_sandbox.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_schema.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_serialization.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/__init__.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/test_all_transformers.py +0 -0
- {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/test_expression.py +0 -0
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
6
9
|
|
|
7
10
|
from xplainable_preprocessing.pipeline import DataFrameColumnTransformer, DataFramePipeline
|
|
8
11
|
from xplainable_preprocessing.registry import REGISTRY
|
|
@@ -11,14 +14,29 @@ from xplainable_preprocessing.schema import PipelineSpec
|
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
def _coerce_params(cls, params: dict) -> dict:
|
|
14
|
-
"""
|
|
17
|
+
"""Adapt spec params to the constructor's signature.
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
- Converts list params to tuples where the constructor expects them
|
|
20
|
+
(JSON has no tuple type, so specs always contain lists; sklearn
|
|
21
|
+
transformers like MinMaxScaler expect tuples for feature_range).
|
|
22
|
+
- Drops params the constructor does not accept, with a warning. Specs
|
|
23
|
+
may be generated in an env with a newer sklearn than the one
|
|
24
|
+
compiling them (2026-07: sklearn 1.7 emitted
|
|
25
|
+
KBinsDiscretizer(quantile_method=...), which 1.6 rejects).
|
|
18
26
|
"""
|
|
19
27
|
sig = inspect.signature(cls.__init__)
|
|
28
|
+
accepts_kwargs = any(
|
|
29
|
+
p.kind is inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
|
|
30
|
+
)
|
|
20
31
|
coerced = {}
|
|
21
32
|
for key, value in params.items():
|
|
33
|
+
if key not in sig.parameters and not accepts_kwargs:
|
|
34
|
+
logger.warning(
|
|
35
|
+
"%s does not accept param '%s' in this environment "
|
|
36
|
+
"(likely a library-version mismatch with the spec's "
|
|
37
|
+
"generator); dropping it", cls.__name__, key
|
|
38
|
+
)
|
|
39
|
+
continue
|
|
22
40
|
if isinstance(value, list) and key in sig.parameters:
|
|
23
41
|
param = sig.parameters[key]
|
|
24
42
|
if param.default is not inspect.Parameter.empty and isinstance(param.default, tuple):
|
|
@@ -48,6 +66,8 @@ def compile_spec(spec: PipelineSpec) -> DataFramePipeline:
|
|
|
48
66
|
steps = []
|
|
49
67
|
|
|
50
68
|
for step in spec.steps:
|
|
69
|
+
wrap = bool(step.columns)
|
|
70
|
+
|
|
51
71
|
if step.type == "custom":
|
|
52
72
|
transformer = compile_custom(step.params)
|
|
53
73
|
else:
|
|
@@ -58,9 +78,18 @@ def compile_spec(spec: PipelineSpec) -> DataFramePipeline:
|
|
|
58
78
|
)
|
|
59
79
|
cls = REGISTRY[step.type]
|
|
60
80
|
params = _coerce_params(cls, step.params)
|
|
81
|
+
|
|
82
|
+
# Column-aware transformers (constructor takes `columns`) must
|
|
83
|
+
# receive the step-level columns directly and must NOT be wrapped:
|
|
84
|
+
# DataFrameColumnTransformer feeds them only their declared columns
|
|
85
|
+
# and re-attaches the result, which turns e.g. a drop into a no-op.
|
|
86
|
+
if step.columns and "columns" in inspect.signature(cls.__init__).parameters:
|
|
87
|
+
params.setdefault("columns", step.columns)
|
|
88
|
+
wrap = False
|
|
89
|
+
|
|
61
90
|
transformer = cls(**params)
|
|
62
91
|
|
|
63
|
-
if
|
|
92
|
+
if wrap:
|
|
64
93
|
transformer = DataFrameColumnTransformer(transformer, step.columns)
|
|
65
94
|
|
|
66
95
|
steps.append((step.id, transformer))
|
|
@@ -34,6 +34,40 @@ class TestCompileSpec:
|
|
|
34
34
|
name, transformer = pipeline.steps[0]
|
|
35
35
|
assert not isinstance(transformer, DataFrameColumnTransformer)
|
|
36
36
|
|
|
37
|
+
def test_step_level_columns_on_drop_actually_drops(self):
|
|
38
|
+
# Specs commonly declare columns at the step level with empty params.
|
|
39
|
+
# DropColumnsTransformer is column-aware: wrapping it in
|
|
40
|
+
# DataFrameColumnTransformer turns the drop into a no-op (the wrapper
|
|
41
|
+
# re-attaches the "transformed" — i.e. untouched — columns).
|
|
42
|
+
spec = PipelineSpec(steps=[
|
|
43
|
+
StepSpec(id="drop", type="DropColumnsTransformer",
|
|
44
|
+
columns=["id", "count"], params={}),
|
|
45
|
+
])
|
|
46
|
+
pipeline = compile_spec(spec)
|
|
47
|
+
df = pd.DataFrame({"id": [1, 2], "count": [3, 4], "value": [5.0, 6.0]})
|
|
48
|
+
result = pipeline.fit_transform(df)
|
|
49
|
+
assert list(result.columns) == ["value"]
|
|
50
|
+
|
|
51
|
+
def test_step_level_columns_injected_into_column_aware_transformer(self):
|
|
52
|
+
spec = PipelineSpec(steps=[
|
|
53
|
+
StepSpec(id="drop", type="DropColumnsTransformer",
|
|
54
|
+
columns=["id"], params={}),
|
|
55
|
+
])
|
|
56
|
+
pipeline = compile_spec(spec)
|
|
57
|
+
_, transformer = pipeline.steps[0]
|
|
58
|
+
assert not isinstance(transformer, DataFrameColumnTransformer)
|
|
59
|
+
assert transformer.columns == ["id"]
|
|
60
|
+
|
|
61
|
+
def test_params_columns_take_precedence_over_step_columns(self):
|
|
62
|
+
spec = PipelineSpec(steps=[
|
|
63
|
+
StepSpec(id="drop", type="DropColumnsTransformer",
|
|
64
|
+
columns=["id"], params={"columns": ["count"]}),
|
|
65
|
+
])
|
|
66
|
+
pipeline = compile_spec(spec)
|
|
67
|
+
_, transformer = pipeline.steps[0]
|
|
68
|
+
assert not isinstance(transformer, DataFrameColumnTransformer)
|
|
69
|
+
assert transformer.columns == ["count"]
|
|
70
|
+
|
|
37
71
|
def test_unknown_type_raises(self):
|
|
38
72
|
spec = PipelineSpec(steps=[
|
|
39
73
|
StepSpec(id="bad", type="FakeTransformer"),
|
|
@@ -118,3 +152,51 @@ class TestCompileSpec:
|
|
|
118
152
|
assert "id" not in result.columns
|
|
119
153
|
assert "value" in result.columns
|
|
120
154
|
assert not result["value"].isna().any()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TestUnsupportedParamFiltering:
|
|
158
|
+
"""Specs may be generated in an env with a newer sklearn than the one
|
|
159
|
+
compiling them (2026-07: autotrain on sklearn 1.7 emitted
|
|
160
|
+
KBinsDiscretizer(quantile_method=...), which the API's sklearn 1.6
|
|
161
|
+
constructor rejects). Unknown constructor params must be dropped with a
|
|
162
|
+
warning instead of crashing compilation."""
|
|
163
|
+
|
|
164
|
+
def test_unknown_param_is_dropped_with_warning(self, caplog):
|
|
165
|
+
import logging
|
|
166
|
+
|
|
167
|
+
spec = PipelineSpec(steps=[
|
|
168
|
+
StepSpec(
|
|
169
|
+
id="bin",
|
|
170
|
+
type="KBinsDiscretizer",
|
|
171
|
+
columns=["age"],
|
|
172
|
+
params={
|
|
173
|
+
"n_bins": 4,
|
|
174
|
+
"encode": "ordinal",
|
|
175
|
+
"strategy": "quantile",
|
|
176
|
+
"definitely_not_a_real_sklearn_param": "x",
|
|
177
|
+
},
|
|
178
|
+
),
|
|
179
|
+
])
|
|
180
|
+
with caplog.at_level(logging.WARNING):
|
|
181
|
+
pipeline = compile_spec(spec)
|
|
182
|
+
|
|
183
|
+
_, transformer = pipeline.steps[0]
|
|
184
|
+
inner = transformer.transformer
|
|
185
|
+
assert inner.n_bins == 4
|
|
186
|
+
assert not hasattr(inner, "definitely_not_a_real_sklearn_param")
|
|
187
|
+
assert any("definitely_not_a_real_sklearn_param" in r.message
|
|
188
|
+
for r in caplog.records)
|
|
189
|
+
|
|
190
|
+
def test_supported_params_pass_through_unchanged(self):
|
|
191
|
+
spec = PipelineSpec(steps=[
|
|
192
|
+
StepSpec(
|
|
193
|
+
id="bin",
|
|
194
|
+
type="KBinsDiscretizer",
|
|
195
|
+
columns=["age"],
|
|
196
|
+
params={"n_bins": 3, "encode": "ordinal"},
|
|
197
|
+
),
|
|
198
|
+
])
|
|
199
|
+
pipeline = compile_spec(spec)
|
|
200
|
+
_, transformer = pipeline.steps[0]
|
|
201
|
+
assert transformer.transformer.n_bins == 3
|
|
202
|
+
assert transformer.transformer.encode == "ordinal"
|
{xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/.github/workflows/publish-pypi.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/dag-pipeline-proposal.md
RENAMED
|
File without changes
|
|
File without changes
|
{xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/feature-store-proposal.md
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_serialization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|