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.
Files changed (39) hide show
  1. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/PKG-INFO +1 -1
  2. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/pyproject.toml +1 -1
  3. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/compiler.py +33 -4
  4. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_compiler.py +82 -0
  5. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/.github/workflows/publish-pypi.yml +0 -0
  6. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/.gitignore +0 -0
  7. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/README.md +0 -0
  8. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/dag-pipeline-proposal.md +0 -0
  9. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/feature-pipeline-architectures.md +0 -0
  10. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/docs/feature-store-proposal.md +0 -0
  11. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/__init__.py +0 -0
  12. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/pipeline.py +0 -0
  13. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/preview.py +0 -0
  14. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/registry.py +0 -0
  15. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/sandbox.py +0 -0
  16. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/schema.py +0 -0
  17. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/serialization.py +0 -0
  18. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/__init__.py +0 -0
  19. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/category_condense.py +0 -0
  20. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/clip.py +0 -0
  21. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/datetime_extract.py +0 -0
  22. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/drop_columns.py +0 -0
  23. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/expression.py +0 -0
  24. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/fill_missing.py +0 -0
  25. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/groupby_agg.py +0 -0
  26. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/grouped_lag.py +0 -0
  27. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/missing_flag.py +0 -0
  28. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/rename_columns.py +0 -0
  29. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/rolling_agg.py +0 -0
  30. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/text_clean.py +0 -0
  31. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/src/xplainable_preprocessing/transformers/type_cast.py +0 -0
  32. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/__init__.py +0 -0
  33. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_preview.py +0 -0
  34. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_sandbox.py +0 -0
  35. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_schema.py +0 -0
  36. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_serialization.py +0 -0
  37. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/__init__.py +0 -0
  38. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/test_all_transformers.py +0 -0
  39. {xplainable_preprocessing-0.2.2 → xplainable_preprocessing-0.2.3}/tests/test_transformers/test_expression.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xplainable-preprocessing
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Shared preprocessing pipeline package for xplainable
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: cloudpickle>=3.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "xplainable-preprocessing"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Shared preprocessing pipeline package for xplainable"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = [
@@ -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
- """Convert list params to tuples where the constructor expects them.
17
+ """Adapt spec params to the constructor's signature.
15
18
 
16
- JSON has no tuple type, so specs always contain lists. sklearn transformers
17
- like MinMaxScaler expect tuples for params like feature_range.
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 step.columns:
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"