Skip to content

Encoders: cache mappings/indexes in transform for fast small batches transform - #15

Open
cakedev0 wants to merge 3 commits into
encoders/pandas_fast_pathfrom
encoders/opt/small_batches
Open

Encoders: cache mappings/indexes in transform for fast small batches transform#15
cakedev0 wants to merge 3 commits into
encoders/pandas_fast_pathfrom
encoders/opt/small_batches

Conversation

@cakedev0

@cakedev0 cakedev0 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Reference Issues/PRs

Based on top of scikit-learn#34678, that's why I open this on my fork for now.

Supersedes scikit-learn#32368 and #13

What does this implement/fix? Explain your changes.

Speeds up repeated small-batch transform calls (e.g. scoring one row at a time in a serving loop) for OneHotEncoder, OrdinalEncoder and TargetEncoder.

To achieve that, I cache the mapping/index used by _encode for the two non-numerical paths (objects array, string/categorical Series). This mapping/index costs O(n_categories) to build, it's what makes this path particularly slow for small batches.

The numerical path doesn't suffer from such an O(n_categories) cost, so it's left unchanged.

AI usage disclosure

moderate

Benchmarks

Using benchmarks/bench_encoders.py, this branch vs its base (scikit-learn#34678), transform_single_ms only (repeated single-row transform calls on an encoder). Measured with PYTHONHASHSEED=0 (str hashing is randomized per-process by default, which is otherwise a real source of noise on the string/object columns).

base | branch, in ms:

dataset n_columns object string categorical
amazon_employee_access 9 2.5 | 0.3 2.9 | 0.6 1.9 | 0.5
kick 18 1.0 | 0.6 4.5 | 1.4 3.1 | 1.2
kddcup09_churn 38 15.1 | 1.1 17.5 | 2.9 13.9 | 4.7
house_prices 43 1.3 | 1.3 8.6 | 2.9 5.5 | 2.2
bank_marketing 223 5.8 | 5.6 32.4 | 10.8 21.3 | 9.1

No regressions anywhere in this matrix. The speed-up tracks category count (n_categories), as expected since that's exactly the cost being amortized away: biggest on amazon_employee_access/kddcup09_churn (highest-cardinality columns), smallest on bank_marketing/house_prices (mostly very low-cardinality columns, where the O(n_categories) table-building cost was already small).

Open question

This PR is a memory-performance trade-off case. In most cases, the memory cost is quite negligible (n_categories << n_rows) and when it's not, it's when the speed-up is the bigger (high n_categories). This is what makes me confident

Still, maybe the users should have the choice? We could imagine introducing a cache_transform=False/True parameter to each impacted encoder. In that case, the transform cache could be initialized eagerly (instead of lazily) when the user opted for cache_transform=True.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant