API Reference¶
Import public classes and exceptions from polars_learn. This reference describes
the implementation in this checkout. See the getting started guide for complete
installation and a complete prediction workflow. See Development for repository setup and documentation builds.
Choosing a component¶
| What you want to do | Component | How it works |
|---|---|---|
| Describe a feature calculation | FeatureSpec |
Stores Polars expressions, optional aliases, and drops; execution happens in FeatureEngineer. |
| Apply calculations inside a model pipeline | FeatureEngineer |
Applies specs in entry order, records input/output schema during fit, and replays and checks preparation during transform. |
| Enforce an application-defined schema | SchemaSpec |
Checks exact required names and dtypes without casting; records the fitted output layout. |
| Enforce the training data's schema | SchemaValidator |
Learns names/order/dtypes during fit, then validates and performs only the casts allowed by its mode. |
| Enforce row-level input rules | ValueValidator |
Applies named Boolean Polars expressions; false and null results raise ValueValidationError. |
| Select and optionally rename model inputs | ColumnSelector |
Selects configured columns in entry order and checks fitted output names; values are unchanged. |
| Retain selected features unchanged | FeaturePassthrough |
Uses the same selection behavior without renaming; useful for unchanged branches. |
The class and method references below are generated from the source docstrings. You can also use help(FeatureEngineer) or help(FeatureEngineer.transform) in Python to read the same behavior details locally. Each class explains when to use it, what fitting records, how transformation works, and its input/output choices.
Class reference index¶
Choose a class for its purpose, input/output examples, constructor parameters, attributes, and methods. The same pages are listed under Classes in the documentation navigation.
| Class | Purpose | Detailed reference |
|---|---|---|
| FeatureEngineer | Ordered feature construction and fitted output-schema checks. | Parameters · Reference |
| FeatureSpec | One expression or sequence, aliases, drops, and diagnostic labels. | Parameters · Reference |
| SchemaSpec | An explicitly declared required schema. | Parameters · Reference |
| SchemaValidator | A learned training schema and conversion policy. | Parameters · Reference |
| ValueValidator | Named row-level checks for nulls, bounds, and allowed values. | Parameters · Reference |
| ColumnSelector | Column selection, ordering, and optional renaming. | Parameters · Reference |
| FeaturePassthrough | Explicit unchanged-column selection. | Parameters · Reference |
Shared Transformer Behavior¶
ColumnSelector, FeaturePassthrough, SchemaSpec, SchemaValidator,
ValueValidator, and FeatureEngineer implement the scikit-learn
transformer/estimator interface.
They compose through fit/transform and scikit-learn's parameter conventions,
following the same estimator pattern used by libraries such as XGBoost.
FeatureSpec describes an operation and is not an estimator.
Input and Output¶
Accepted input is a polars.DataFrame or polars.LazyFrame. A pandas DataFrame
is accepted only with convert_pandas=True. NumPy arrays, Series, lists, and
sparse arrays are unsupported. Original inputs are never mutated.
| Output configuration | Eager input, including converted pandas | Lazy input |
|---|---|---|
Initial setting or set_output(transform="default") |
DataFrame | LazyFrame |
set_output(transform="polars") |
DataFrame | DataFrame, collected by the transformer |
set_output(transform="polars-lazy") |
LazyFrame | LazyFrame |
set_output(transform="pandas") |
pandas DataFrame, converted from Polars | pandas DataFrame, collected and converted |
set_output(transform=None) keeps the current choice. "pandas" output requires
the optional pandas extra and uses Polars to_pandas() defaults. Prefer native
Polars for speed and to avoid conversion overhead; pandas output is a compatibility
option. Input conversion remains separately controlled by convert_pandas.
Global scikit-learn output settings do not change these native
transformers' returns. Explicit output choices survive cloning.
Default return type follows the input of each transform call, not the input
used during fitting. Lazy schema checks use metadata and do not evaluate rows;
source metadata access can still occur. ValueValidator is an explicit
value-check boundary: fit resolves check schemas only; transform materializes
lazy input and evaluates checks, then returns a lazy wrapper over the checked in-memory
frame by default. Downstream collection will not reread the source. Other
transformers defer value-dependent failures until query execution. Requesting
lazy output for eager input can wrap an already evaluated result.
Estimator Conventions¶
Preprocessing transformers use fit to validate or learn input contracts and
transform to apply the fitted transformation. Predictive estimators use fit
to learn model parameters and predict to apply the fitted model. Libraries
such as XGBoost expose this same estimator pattern through their sklearn API.
See the estimator guide.
The common interface lets a Pipeline fit its preprocessing steps, pass prepared
features to the final estimator, and reuse those fitted steps for prediction.
It does not make feature containers or dtypes interchangeable. The final model
must accept the prepared output. Use set_output(transform="polars") at the
last native step when eager output is needed, or add an explicit conversion if
the model requires another container. "polars-lazy" is this package's own
output setting, not a general model option.
Common Constructor Options¶
These options are keyword-only on all six transformers:
| Option | Default | Meaning |
|---|---|---|
convert_pandas |
False |
Permit pandas DataFrames and convert them to Polars. Must be a bool. |
warn_on_conversion |
True |
Emit PandasConversionWarning on conversion. Must be a bool; independent of verbosity. |
verbosity |
0 |
Print progress at 1 and additional metadata/operation details at 2. Must be an integer 0, 1, or 2; bool is rejected. |
Native Polars input needs neither pandas nor PyArrow. Pandas conversion may need
PyArrow depending on dtype; install polars-learn[pandas] for both dependencies.
Conversion follows Polars' pandas conversion semantics and discards the index.
Common Methods¶
| Method | Return | Behavior |
|---|---|---|
fit(X, y=None) |
self |
Validate or learn the class-specific contract. Ignores y. Repeated fitting replaces learned state after validation succeeds. |
transform(X) |
DataFrame, LazyFrame, or pandas DataFrame | Apply a fitted transformer; output follows the Input and Output configuration. |
fit_transform(X, y=None, **fit_params) |
DataFrame, LazyFrame, or pandas DataFrame | Call fit then transform. Extra keyword parameters are unsupported and raise TypeError. |
get_feature_names_out(input_features=None) |
NumPy object array of strings | Return a copy of fitted output names. If supplied, input names must exactly match the fitted input names and order. |
get_params(deep=True) |
dict | Read constructor parameters using scikit-learn's estimator API. Output configuration is set separately with set_output. |
set_params(**params) |
self |
Update constructor parameters. Does not validate immediately or clear fitted state; refit after contract changes. |
set_output(*, transform=None) |
self |
Choose native Polars or pandas compatibility output for subsequent transforms. Configuration does not fit or evaluate data; transformation collects when eager output is requested. |
transform and get_feature_names_out require fitting and raise
sklearn.exceptions.NotFittedError if fitted state is unavailable.
Invalid options changed through set_params are checked at the next relevant
fit/transform operation. A failed refit can leave a previous successful fitted
state in place; use a fresh instance if that state should be discarded.
All six transformers expose these fitted attributes:
| Attribute | Meaning |
|---|---|
feature_names_in_ |
NumPy object array of all input names observed during fitting, in input order. |
n_features_in_ |
Number of columns observed during fitting, including extras and unselected columns. |
output_features_ |
NumPy object array of output names, with the extra-column behavior documented in each class reference. |
Constructor parameters are retained for cloning. Cloning discards fitted attributes. Configuration that changes names or schemas should be followed by fitting again rather than assuming existing fitted attributes were reset.
ColumnSelector¶
class polars_learn.ColumnSelector(
columns,
*,
rename=None,
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)| Parameter | Contract |
|---|---|
columns |
Nonempty sequence of unique, nonempty string column names. Determines output order. A bare string is invalid. |
rename |
Optional dict mapping selected source names to nonempty string output names. Keys must be selected columns, and final output names must be unique, including names left unchanged. |
Fitting checks that every selected column exists and records its output name.
The input can contain other columns, and its order need not match columns.
Transform selects the fitted columns_, applies the rename mapping, and verifies
that output names still match fitting. Input and output dtypes are not checked.
Unselected columns are omitted.
Additional fitted attribute: columns_, a tuple of selected source names.
output_features_ lists the selected output names after renaming.
Wrong argument types raise TypeError; duplicate names, invalid rename mappings,
missing selected columns, or changed output names raise ValueError.
Source query errors can propagate from Polars.
import polars as pl
from polars_learn import ColumnSelector
X = pl.DataFrame({"a": [1], "b": [2], "unused": [3]})
selector = ColumnSelector(["b", "a"], rename={"b": "value"})
result = selector.fit_transform(X.lazy())
assert result.collect().columns == ["value", "a"]
assert selector.get_feature_names_out().tolist() == ["value", "a"]
FeaturePassthrough¶
class polars_learn.FeaturePassthrough(
columns,
*,
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)columns is a nonempty sequence of unique, nonempty string names, in output
order. A bare string is invalid. Only the selected columns are returned, with
Polars values, names, dtypes, nulls, and row order preserved. Unselected input
columns are omitted. There is no rename parameter; use ColumnSelector for
renaming.
This class specializes ColumnSelector and shares its methods and fitted
attributes, including columns_. Fitting checks selected columns exist;
transform requires fitting. Input order and dtypes may change without rejection.
feature_names_in_ and n_features_in_ describe the whole training input,
including unselected columns; output_features_ contains unchanged selected names.
The common input conversion, verbosity, cloning, and output policies apply.
Invalid selection types raise TypeError; duplicate names, missing columns,
and invalid verbosity levels raise ValueError. Transform before fitting raises
NotFittedError. get_params exposes only its constructor parameters, with no
rename option. Source/schema-resolution and collection errors are not wrapped.
Refit after changing columns through set_params.
A sequential pipeline step selects its output subset. Use an eager
ColumnTransformer branch when selected unchanged columns should be combined
with another branch's transformed columns. Configure the outer transformer
with set_output(transform="polars") to preserve separate column dtypes, and
verbose_feature_names_out=False to keep unprefixed names when they are unique.
See the branch example.
import polars as pl
from polars_learn import FeaturePassthrough
X = pl.DataFrame({"id": [1, None], "flag": [True, False], "unused": [9, 8]})
keep = FeaturePassthrough(["flag", "id"])
plan = keep.fit_transform(X.lazy())
assert plan.collect().equals(X.select("flag", "id"))
assert keep.get_feature_names_out().tolist() == ["flag", "id"]
SchemaSpec¶
class polars_learn.SchemaSpec(
schema,
*,
allow_extra_columns=False,
enforce_column_order=True,
extra_columns='preserve',
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)| Parameter | Contract |
|---|---|
schema |
Nonempty dict from nonempty string names to Polars dtypes, in required column order. Accepts dtype classes such as pl.Int64 and instances such as pl.Datetime("ms"). |
allow_extra_columns |
Bool; whether input may include names absent from the explicit schema. |
enforce_column_order |
Bool; require the relative order of required input columns to match the schema's key order. |
extra_columns |
"preserve" or "drop"; treatment of extras if allowed. Does not itself allow extras. |
Fit and transform both require all schema columns and exact required dtypes; there is no casting policy. Output always orders required columns by schema key order. Preserved extras follow in their input order.
Fitting checks the declared contract, not an inferred replacement. Additional
fitted attribute: schema_, a copy of the required schema.
output_features_ contains every fitted output column, including preserved extras.
Subsequent output names and order must match exactly. Extra dtypes are not checked.
Use extra_columns="drop" for a fixed output set containing only required columns.
Missing required columns, unexpected extras, disallowed order, wrong required
dtypes, and output-name mismatches raise ValueError. An invalid schema
container or empty schema raises ValueError; invalid schema keys/values raise
TypeError. Source/collection errors are not wrapped.
import polars as pl
from polars_learn import SchemaSpec
X = pl.DataFrame({"extra": [9], "x": [1]})
contract = SchemaSpec({"x": pl.Int64}, allow_extra_columns=True, extra_columns="drop")
assert contract.fit_transform(X).columns == ["x"]
SchemaValidator¶
class polars_learn.SchemaValidator(
mode='strict',
*,
allow_extra_columns=False,
enforce_column_order=True,
extra_columns='preserve',
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)| Parameter | Contract |
|---|---|
mode |
"strict", "semi-strict", or "permissive". Defaults to "strict"; None is invalid. |
allow_extra_columns |
Bool; allow input names not observed during fitting. All fitted names remain required. |
enforce_column_order |
Bool; require the relative order of fitted columns to match training. False normalizes output order. |
extra_columns |
"preserve" or "drop"; only affects extras that are allowed. |
Fit learns every input column's name and dtype, regardless of mode. Additional
fitted attribute: schema_, a dict in training column order. output_features_
contains those required names, excluding inference-only extras.
Transform validates structure and dtypes, collecting multiple schema mismatches
into one SchemaValidationError. Policies for differing dtypes:
| Mode | Accepted conversion |
|---|---|
| Strict | None; dtype must equal the fitted dtype. |
| Semi-strict | Integer widening based on entire dtype ranges; Float32 to Float64; integer to Float64 only when every evaluated non-null value round-trips exactly. |
| Permissive | Any successful Polars cast(strict=True) to the fitted dtype; successful casts can be lossy. |
Semi-strict rejects integer narrowing even when current values would fit, Float64 to Float32, string to numeric, and Null dtype to a different dtype. Compatible null values are preserved. Nullability is not learned. Permissive mode can cast Null dtype columns, but invalid cast values raise rather than becoming null silently.
With enforced order and preserved extras, output keeps input column order. With order enforcement disabled, output uses required training order followed by preserved extras in input order. Dropping extras returns required columns in training order.
Lazy structural and dtype-policy failures raise during transform. Lazy
permissive casts execute when the plan is collected and can raise native Polars
errors. Lazy semi-strict integer-to-float value checks also run at execution and
raise SchemaValidationError on precision loss. An explicit eager output request
wraps Polars collection errors in SchemaValidationError with their causes.
import polars as pl
from polars_learn import SchemaValidator
train = pl.DataFrame({"x": [1]})
incoming = pl.DataFrame({"extra": [7], "x": ["2"]})
validator = SchemaValidator(
mode="permissive", allow_extra_columns=True, enforce_column_order=False
).fit(train.lazy())
result = validator.transform(incoming.lazy()).collect()
assert result.columns == ["x", "extra"]
assert result["x"].to_list() == [2]
assert validator.get_feature_names_out().tolist() == ["x"]
ValueValidator¶
class polars_learn.ValueValidator(
checks,
*,
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)checks is a nonempty dict of diagnostic names to Boolean Polars expressions.
False or null results raise ValueValidationError. The input's names, order,
and dtypes must match fitting; passing data is returned unchanged.
Fit resolves check schemas only. Checks run during transform, including for
lazy inputs. Lazy sources are materialized once at the validation call, so a later collection reads the
checked in-memory frame and does not rerun the source. This uses memory
proportional to the materialized batch.
import polars as pl
from polars_learn import ValueValidationError, ValueValidator
check = ValueValidator({"positive": pl.col("x") > 0}).fit(pl.DataFrame({"x": [1, 2]}))
try:
check.transform(pl.DataFrame({"x": [1, -1]}))
except ValueValidationError as error:
assert "positive" in str(error)
else:
raise AssertionError("Invalid value was accepted")
FeatureSpec¶
class polars_learn.FeatureSpec(
expression,
*,
alias=None,
drop=None,
name=None,
verbosity=0,
)| Parameter | Contract |
|---|---|
expression |
Required single pl.Expr or nonempty sequence of pl.Expr objects evaluated together. Normalized to a tuple; strings and generators are invalid. |
alias |
Optional nonempty string overriding the output name of a single expression with exactly one output. |
drop |
Optional sequence of nonempty string names to remove after evaluating the expressions. A bare string is invalid. |
name |
Optional nonempty string used to label feature execution errors and detailed progress. |
verbosity |
Integer 0, 1, or 2. Reports construction validation independently of an engineer's verbosity. |
Stored attributes are expression (canonical tuple, including an explicit
alias), drop (tuple), name, and verbosity. There are no fitted attributes
or transformer methods. Pass specifications to FeatureEngineer for execution.
Native expression names and aliases are used unless alias overrides them.
Expressions with one output need a determinable name. Selectors expanding to
multiple outputs are resolved against input by the engineer. Existing columns
can be overwritten. Multiple expressions within this specification cannot use
new columns created by siblings; put dependent expressions in later specifications.
Bad expression or drop types raise TypeError. Empty expression
sequences, invalid alias/name, ambiguous alias, and detectable duplicate output
names raise ValueError. Invalid verbosity types/levels also fail at construction.
repr(spec) shows its canonical expressions, drops, and name.
FeatureEngineer¶
Use FeatureEngineer as a preprocessing step before a predictive estimator. Define feature expressions once, fit the full pipeline on raw training data, and pass new raw observations to pipeline.predict. The pipeline calls the engineer's transform before the estimator's prediction method, generating the same named features in the same specification order. This keeps preparation inside the pipeline instead of requiring a separate feature-building function at each call site. See the raw-data prediction example.
The fitted raw input names/order and resulting feature schema remain the contract. Prediction callers provide required raw columns, not precomputed derived columns. FeatureEngineer replays expressions and validates schemas; learned preprocessing statistics must come from other fitted pipeline steps.
class polars_learn.FeatureEngineer(
features,
*,
convert_pandas=False,
warn_on_conversion=True,
verbosity=0,
)features accepts a single FeatureSpec, a dict of FeatureSpec keyword
arguments, or an ordered sequence mixing these two forms. Single definitions are
normalized to one-element tuples for execution. Dicts are unpacked with
FeatureSpec(**config). Generators and bare Polars expressions are unsupported.
An empty sequence is valid and produces an identity transformation. All
specifications are checked before processing input. The original parameter
object is retained unchanged for estimator cloning and get_params.
set_params(features=...) accepts the same forms; refit after changes.
Config keys are expression, alias, drop, name, and
verbosity, with the same types and validation as the FeatureSpec constructor.
The required expression accepts a single expression or a nonempty sequence.
Missing arguments, unknown keys, and invalid expression types fail with
TypeError; an empty expression sequence fails with ValueError. Config errors identify features or features[index] and preserve
the original exception as their cause. Real Polars expressions are required;
strings are not parsed or evaluated.
Configs are validated at construction and converted again once per fit or
transform call. This picks up parameter changes without mutating caller-owned
dicts. A config's own verbosity can emit specification construction summaries
on those conversions, independently of the engineer's verbosity. For optional
static annotations, FeatureSpecConfig is available from polars_learn.types;
it is a TypedDict describing an ordinary dict, with validation applied by the
engineer at runtime.
import polars as pl
from polars_learn import FeatureEngineer, FeatureSpec
X = pl.DataFrame({"x": [1, 2]})
spec = FeatureSpec(pl.col("x") * 2, alias="doubled")
engineer = FeatureEngineer(spec)
assert engineer.get_params()["features"] is spec
assert engineer.fit_transform(X)["doubled"].to_list() == [2, 4]
import polars as pl
from polars_learn import FeatureEngineer, FeatureSpec
from polars_learn.types import FeatureSpecConfig
X = pl.DataFrame({"x": [1, 2]})
config: FeatureSpecConfig = {"expression": pl.col("x") * 2, "alias": "doubled"}
engineer = FeatureEngineer(
[
config,
FeatureSpec(pl.col("doubled") + 1, alias="next", drop=["doubled"]),
]
)
assert engineer.fit_transform(X.lazy()).collect()["next"].to_list() == [3, 5]
Additional fitted attributes are input_schema_ and output_schema_, dicts
containing input and resulting output names/dtypes in order. output_features_
lists all resulting columns, including retained input columns and new features.
Fit builds the ordered plan, checking each specification's expression schema and drops. With eager input it executes the plan before recording state; with lazy input it records schemas without evaluating rows. Transform requires exact fitted input names and order, and an exact resulting output schema. Input dtypes can differ only if that final schema still matches.
fit_transform currently calls fit then transform; eager feature expressions
are therefore evaluated twice, and pandas conversion can happen twice. Lazy
input remains unevaluated during those calls unless eager output is requested.
Feature expressions do not learn training statistics. Avoid nondeterministic
expressions when consistent training and inference are required.
Plan dependency, drop, and output-schema failures raise FeatureEngineeringError.
Eager execution failures are wrapped in that exception with their causes.
Execution errors from a returned lazy plan can be native Polars exceptions;
an explicit "polars" output request wraps them within transform.
import polars as pl
from polars_learn import FeatureEngineer, FeatureSpec
X = pl.DataFrame({"x": [1, 2]})
engineer = FeatureEngineer(
[
FeatureSpec(pl.col("x") + 1, alias="a", name="increment"),
FeatureSpec(pl.col("a") * 2, alias="b", drop=["a"], name="double"),
]
)
plan = engineer.fit_transform(X.lazy())
assert plan.collect().to_dict(as_series=False) == {"x": [1, 2], "b": [4, 6]}
Persistence¶
All seven main classes support standard Python pickle round trips. Native pl.Expr objects use Polars' built-in serialization, including expressions in single specifications, dict configs, and mixed sequences. Transformer parameters, fitted input/output schemas and names, pandas-conversion settings, and explicit output-container settings are retained. Unfitted instances remain unfitted after loading. The package does not introduce a separate expression encoding or compatibility aliases.
Tests cover every pickle protocol available to the test interpreter, eager and lazy fitting, native arithmetic/conditional/selector/literal expressions, and Python callback closures. Full fitted pipelines containing the transformer classes and a model are loaded and used to predict raw observations in fresh Python processes, using pickle, joblib, and cloudpickle. See the save/load example.
Cloudpickle is an explicit runtime dependency for Python callbacks embedded in Polars expressions. Closures and lambdas with serializable state are covered; arbitrary captured locks, open resources, plugin binaries, or missing external dependencies cannot be guaranteed portable. These limits also apply to caller-supplied custom parameter containers and final estimators.
Saved objects record Python, polars-learn, Polars, scikit-learn, NumPy, and cloudpickle versions. Each mismatch emits InconsistentVersionWarning before the object's state is decoded, then loading is attempted. Its attributes are class_name, dependency, original_version, and current_version. Use warnings.simplefilter("error", InconsistentVersionWarning) to stop before state restoration on a mismatch. To accept mismatches quietly, use warnings.simplefilter("ignore", InconsistentVersionWarning) inside warnings.catch_warnings() around loading. For application-wide suppression, use warnings.filterwarnings("ignore", category=InconsistentVersionWarning). Other warning categories remain active, and incompatible state still raises SerializationError. These filters apply equally to pickle, joblib, and cloudpickle. Final estimators may emit their own version warning categories. Older artifacts without this metadata cannot report creating versions.
Expressions use native binary serialization for matching Polars versions and native JSON for cross-version attempts. Required expression operations, dtypes, callbacks, and other objects must remain compatible and available. Unavailable or rejected representations raise SerializationError, preserving version context and the underlying cause; old binary layouts are never used as a cross-version fallback. CI exercises both directions between Polars 1.35.2 and the locked version, including all seven main classes and a fitted prediction pipeline.
Load trusted artifacts only: pickle and embedded callbacks can execute code. Polars does not guarantee expression formats across releases, and arbitrary dependency upgrades cannot be guaranteed compatible. Successful loading does not establish identical feature values or predictions; validate these after upgrading and retain pinned original dependencies for recovery. This contract preserves supported objects and behavior, not byte-identical pickle output.
Exceptions and Warnings¶
Import each of these from polars_learn:
| Name | Base | Use |
|---|---|---|
PolarsLearnError |
Exception |
Base for package-specific processing and persistence errors. |
SchemaValidationError |
PolarsLearnError |
Learned schema mismatches, eager cast failures, and lazy lossless widening failures. |
FeatureEngineeringError |
PolarsLearnError |
Feature planning/schema failures and wrapped eager execution errors. |
SerializationError |
PolarsLearnError |
Saving unsupported state or restoring incompatible serialized state; retains the underlying exception as its cause. |
InconsistentVersionWarning |
UserWarning |
A saved object was created with a different Python, package, or dependency version; loading is attempted unless the warning is promoted to an error. |
PandasConversionWarning |
UserWarning |
Enabled pandas input conversion when warnings are enabled. |
Configuration errors and fixed-contract/selector/passthrough validation use ordinary
TypeError or ValueError. Missing fitted state uses scikit-learn's
NotFittedError. Those exceptions, native Polars exceptions, and source errors
are not covered by catching PolarsLearnError. If handling lazy value errors,
place the handler around query execution as well as plan creation.