Python · ML · Tabular Data · Verified 2025

Skrub Library — Practitioner Guide

Skrub handles the preprocessing step that eats most of your modeling time — mixed column types, high-cardinality strings, datetimes, fuzzy table joins, multi-table pipelines. Here’s what it actually does, what changed recently, and where it still needs your judgment.

Without skrub ~20 lines
from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from sklearn.compose import ( make_column_transformer, make_column_selector as selector ) ct = make_column_transformer( (StandardScaler(), selector(dtype_include=‘number’)), (OneHotEncoder(handle_unknown=‘ignore’), selector(dtype_include=object)), ) model = make_pipeline( ct, SimpleImputer(), YourEstimator() ) # Doesn’t handle: high-cardinality strings, # datetimes, nulls-as-strings, mixed types
With skrub 1 line
from skrub import tabular_pipelinemodel = tabular_pipeline(‘regressor’)# Automatically handles: # ✓ High-cardinality strings → StringEncoder # ✓ Low-cardinality categories → OneHotEncoder # ✓ Datetimes → DatetimeEncoder (cyclic) # ✓ Numerics → passthrough # ✓ Nulls-as-strings → Cleaner # ✓ Mixed types → auto-detected # Estimator: HistGradientBoostingRegressor
Requirements: Python 3.10+ · pandas or polars · scikit-learn 1.4.2+. Install: pip install skrub. Optional: pip install skrub[transformers] for TextEncoder (HuggingFace-backed).
Verified: Dec 2025 By A.J., UIG Data Lab skrub-data.org · GitHub
Getting started

Quick start — from raw dataframe to cross-validated score

The fastest path from a messy dataframe to a working model baseline. tabular_pipeline() wraps a TableVectorizer (which handles column detection and encoding) and a HistGradientBoostingRegressor or HistGradientBoostingClassifier (which handles nulls natively, so no imputer needed).

API change — deprecation

tabular_learner() is deprecated as of recent releases — use tabular_pipeline() instead. The old function still works but will be removed. Any code or tutorial you find that references tabular_learner needs updating.

Full quick start — employee salary prediction
from skrub import tabular_pipeline
from skrub.datasets import fetch_employee_salaries
from sklearn.model_selection import cross_val_score

dataset = fetch_employee_salaries()
df, y = dataset.X, dataset.y

# One line baseline: auto-encodes job titles, division names,
# date columns, and numeric features without any manual setup
model = tabular_pipeline('regressor')

scores = cross_val_score(model, df, y, cv=5)
print(f"R²: {scores.mean():.3f} ± {scores.std():.3f}")
# R²: 0.912 ± 0.013

To explore the dataframe before fitting, use TableReport. In a Jupyter notebook, skrub.patch_display() replaces the default pandas/polars repr with a TableReport automatically, so every dataframe cell renders interactively:

Interactive exploration
import skrub
from skrub import TableReport

# Option A: explicit report
report = TableReport(df)
report  # renders in Jupyter: distributions, correlations, missing values

# Option B: patch all dataframes in the notebook
skrub.patch_display()
df  # now renders as TableReport automatically

# Statistical dependencies between columns
from skrub import column_associations
column_associations(df)  # pairwise statistical dependence matrix
Core API

Core components

Exploration

TableReport

Interactive HTML summary of any dataframe — distributions, correlations, missing value patterns, high-cardinality flags, sorted column detection. Supports pandas, polars, and numpy arrays. Use patch_display() to make it the default in Jupyter.

Encoding

TableVectorizer

Detects column types and routes each to the right encoder: StringEncoder for high cardinality (≥40 unique values), OneHotEncoder for low cardinality, DatetimeEncoder for datetimes, passthrough for numerics. Produces a consistent float32 output matrix.

Baseline

tabular_pipeline()

One-line scikit-learn Pipeline combining a TableVectorizer and a HistGradientBoosting estimator. Replaces the deprecated tabular_learner(). Pass 'regressor', 'classifier', or any sklearn estimator as the argument.

Cleaning

Cleaner

Lightweight pre-processor that normalizes column dtypes, handles null strings, and casts to consistent numeric types. Used internally by TableVectorizer but available standalone. The old SimpleCleaner was renamed to Cleaner.

Joining

Joiner & AggJoiner

Joiner merges tables on fuzzy string keys using approximate matching. AggJoiner aggregates a detail-level table (e.g., transactions) and joins the result onto the main table. Both are sklearn-compatible transformers.

Spatial / temporal

InterpolationJoiner

Joins on non-exact spatial or temporal keys using a learned interpolation model. Useful for enriching datasets with nearby weather readings, geospatial lookups, or any numeric key where exact matching isn’t possible.

String & text encoding

Encoders for strings — which one to use

The choice of string encoder significantly affects both performance and memory footprint. Skrub provides four options with different tradeoffs, and TableVectorizer picks automatically — but understanding when to override the default is worth the few seconds it takes.

EncoderHow it worksBest for
StringEncoderTF-IDF on character n-grams, reduced with truncated SVD. Normalized output. Default for high-cardinality columns since 0.6.0 (replaced GapEncoder).Most string columns. Fast, memory-efficient, solid baseline. Use this unless you have a specific reason to switch.
TextEncoderDense embeddings from a HuggingFace sentence-transformer model (local or Hub). Requires pip install skrub[transformers].Free-text columns with diverse, sentence-like entries — product descriptions, customer feedback, notes. Semantically-aware but slower.
GapEncoderSoft topic model on character n-grams — each component corresponds to an interpretable pattern. Older default, still available.When feature interpretability matters more than speed. Slower and more memory-intensive than StringEncoder.
MinHashEncoderFast approximate n-gram similarity via MinHash. Very low memory footprint.Very large datasets where speed and memory dominate. Slightly lower quality than StringEncoder.
Overriding the default encoder in TableVectorizer
from skrub import TableVectorizer, TextEncoder, SquashingScaler
from skrub import DatetimeEncoder

# Swap in TextEncoder for free-text columns,
# SquashingScaler (clips outliers, more robust than StandardScaler),
# and circular encoding for datetime seasonality
table_vec = TableVectorizer(
    high_cardinality=TextEncoder(),             # LLM embeddings for text
    numeric=SquashingScaler(),                  # robust numeric scaling
    datetime=DatetimeEncoder(
        periodic_encoding="circular"           # sin/cos for seasonality
    ),
)
SquashingScaler — new in recent releases

SquashingScaler clips extreme values and scales, making it more robust than StandardScaler when numeric columns have outliers. It’s the default numeric encoder in tabular_pipeline() when using linear models.

Major new capability

DataOps — multi-table pipelines with hyperparameter tuning

DataOps is the significant architectural addition in recent skrub releases. Where tabular_pipeline() handles a single table, DataOps handles the realistic case: multiple related tables, transformations applied to different subsets of columns, choices between different pipeline configurations, and the need to export a trained pipeline for deployment — all within a framework that tracks every operation for reproducibility.

The core idea is that you declare operations on placeholder variables using skrub.var(). These operations are recorded but not immediately executed. Once you’ve described the full pipeline, you export it as a SkrubLearner — a standalone, picklable object that accepts a dictionary of inputs (your tables) and applies the recorded transformations in the correct order.

DataOps — basic pipeline construction and export
import skrub
from skrub import TableVectorizer
from sklearn.linear_model import Ridge

# 1. Declare placeholder variables — operations are recorded, not yet run
orders = skrub.var("orders", orders_df)      # use preview data for type inference
ratings = skrub.var("ratings", ratings_df)

# 2. Describe transformations using normal Python
agg_ratings = (
    ratings
    .groupby("item_id")["score"]
    .agg(skrub.choose_from(["mean", "median"], name="agg_func"))  # tunable!
    .reset_index()
)

# 3. Join and vectorize
merged = orders.merge(agg_ratings, on="item_id", how="left")
X = merged.skb.apply(TableVectorizer()).skb.mark_as_X()
y = merged["revenue"].skb.mark_as_y()

# 4. Add model as a choice (enable tuning between estimators)
pred = X.skb.apply(
    skrub.choose_from(
        [Ridge(alpha=1.0), Ridge(alpha=0.1)],
        name="model"
    )
)

# 5. Tune over all choices at once
search = pred.skb.make_randomized_search(fitted=True)
search.results_   # DataFrame: mean_test_score, agg_func, model, ...

# 6. Export the best pipeline as a deployable SkrubLearner
learner = pred.skb.make_learner(fitted=True)
learner.predict({"orders": new_orders_df, "ratings": new_ratings_df})

The choices defined with skrub.choose_from() aren’t limited to scikit-learn hyperparameters. You can make any argument to any operation a tunable choice — aggregation functions, which columns to include, whether to apply a specific transformer — and the search explores all combinations in a single call.

SkrubLearner for deployment

SkrubLearner is a picklable, sklearn-compatible object that accepts a dictionary of tables rather than a single design matrix. This means the entire multi-table pipeline — joins, aggregations, encoding, model — is bundled into one artifact. You can call .fit(env_dict), .predict(env_dict), and .transform(env_dict) where env_dict maps table names to dataframes. It can also be fitted at export time by passing fitted=True to make_learner().

Column selection

Selectors — precise column targeting

Skrub’s selectors module lets you define column groups based on properties rather than hardcoded names. Combined with DataOps, this handles the common pattern of applying different transformers to different column subsets without duplicating logic.

Selectors — applying different encoders to different column groups
from skrub import selectors as s
import skrub

# Build selectors using set-like operations
high_card   = s.string() - s.cardinality_below(40)   # high-cardinality strings
has_nulls   = s.has_nulls()                            # columns with any missing values
leftover    = s.all() - high_card - has_nulls           # everything else

df_var = skrub.var("df", df)

# Apply different transformers based on column properties
encoded_strings  = df_var.skb.select(cols=high_card).skb.apply(
    skrub.StringEncoder(n_components=30)
)
rest = df_var.skb.select(cols=leftover).skb.apply(
    skrub.TableVectorizer()
)

result = rest.skb.concat([encoded_strings], axis=1)

# Available selectors
# s.string(), s.numeric(), s.has_nulls(), s.all()
# s.cardinality_below(n), s.has_dtype(dtype), s.glob("prefix_*")
Multi-table

Fuzzy joining — when keys don’t match exactly

Joiner handles the realistic case where two tables should be merged on a string key but the strings don’t match exactly — department names with different abbreviations, city names with typos, product codes with varying formatting. It uses approximate string matching rather than exact equality, and is a full sklearn transformer so it fits on training data and transforms consistently at inference.

Fuzzy join — department names with inconsistent formatting
from skrub import Joiner

# main_table has "dept_name"; aux_table has "department"
# The values are similar but not identical ("IT Dept" vs "IT Department")
joiner = Joiner(
    aux_table=budget_df,
    main_key="dept_name",
    aux_key="department",
)
enriched = joiner.fit_transform(main_df)
# budget_df columns now appended to main_df rows
AggJoiner — aggregate a detail table before joining
from skrub import AggJoiner

# transactions_df has one row per transaction.
# AggJoiner aggregates to one row per customer, then joins onto customers_df.
agg_joiner = AggJoiner(
    aux_table=transactions_df,
    main_key="customer_id",
    aux_key="customer_id",
    operations=["sum", "mean", "max"],
)
customers_enriched = agg_joiner.fit_transform(customers_df)
# Adds columns: amount_sum, amount_mean, amount_max, etc.
Fuzzy join threshold

Skrub’s fuzzy matching uses its own approximate string similarity — no external dependency like fuzzywuzzy is needed. The Joiner has a threshold parameter to control how loose the matching is. Start with the default and inspect the skrub_Joiner_score column added to the output — low scores indicate uncertain matches that may need manual review.

Ecosystem fit

Skrub vs other tools

ToolWhat it does wellWhere it doesn’t help
pandas / polarsGeneral-purpose dataframe manipulation, large ecosystem, performance at scaleNo ML preprocessing automation, no fuzzy joins, no sklearn compatibility layer
scikit-learn onlyStrong model variety, mature Pipeline APIColumnTransformer is verbose for mixed-type tables; no fuzzy join, no multi-table support, no auto type detection
skrubAuto-encodes mixed column types, fuzzy table joins, multi-table DataOps with tunable choices, SkrubLearner for deploymentLarge tables may need sampling for TableReport; fuzzy join quality depends on key similarity; DataOps adds learning curve over sklearn pipelines

Skrub is additive rather than competitive — it wraps scikit-learn rather than replacing it. The primary audience is teams who spend meaningful time writing column-type detection, high-cardinality encoding, and table-joining boilerplate for each new project.

Common questions

FAQ – Skrub Library

tabular_learner vs tabular_pipeline — which should I use?
tabular_pipeline()tabular_learner() is deprecated and will be removed in a future release. The function signatures and behavior are the same; it’s a rename. Update any existing code by replacing tabular_learner with tabular_pipeline.
When should I use DataOps instead of tabular_pipeline?
Use tabular_pipeline() when you have a single flat table and want a quick baseline. Move to DataOps when you need any of: multiple input tables, column-specific transformers beyond TableVectorizer’s defaults, hyperparameter choices across the entire pipeline (not just sklearn params), or a deployable SkrubLearner that accepts a dictionary of tables rather than a single matrix.
Does skrub require fuzzywuzzy or any external string matching library?
No. Skrub’s fuzzy matching uses its own approximate string similarity implementation — no fuzzywuzzy, rapidfuzz, or other external library is needed as a dependency. The only optional dependency is transformers (via pip install skrub[transformers]) if you want to use TextEncoder with HuggingFace models.
What changed between GapEncoder and StringEncoder as the default?
StringEncoder became the default high-cardinality encoder in skrub 0.6.0, replacing GapEncoder. StringEncoder is faster, uses less memory, and produces comparable or better downstream accuracy for most tasks. GapEncoder is still available and remains useful when you need interpretable features — its components correspond to character n-gram patterns that can be read and understood. For most production pipelines where you care about performance, stick with StringEncoder.
Does skrub work with polars, or only pandas?
Both. TableVectorizer, TableReport, and most skrub components accept both pandas and polars DataFrames. The DataOps system also works with either, passing the dataframe through the declared operations without forcing a conversion. Check the specific component’s documentation for any exceptions — most advanced components are tested against both backends.
Can I use skrub with PyTorch or deep learning models?
Yes. The DataOps system is engine-agnostic, and there’s an official example in the skrub documentation showing PyTorch integration via skorch (which wraps PyTorch models with an sklearn interface). TextEncoder uses HuggingFace sentence-transformers for embedding generation. For deep learning models not wrapped by skorch, you’d typically use DataOps for the feature engineering stages and hand the encoded output off to your own training loop.
Where to go next

Resources

Accuracy note: API names, default encoder changes, and deprecations are sourced from the skrub release history and official documentation at skrub-data.org. Python 3.10+ and scikit-learn 1.4.2+ requirements are from the release notes. UIG Data Lab is independent and not affiliated with the skrub project or its contributors.

AJ
A.J. Data Engineering Researcher & Technical Writer · UIG Data Lab

A.J. covers data engineering, Python ML tooling, Microsoft Fabric, and cloud data platforms for UIG Data Lab. Content is built from official documentation and release notes rather than reconstructed from blog summaries.

Python Machine Learning scikit-learn Skrub Feature Engineering Tabular Data DataOps

Scroll to Top