[SYSTEMDS-3645] FTBench extension: Polars - #5
Open
LasseHenrich wants to merge 1 commit into
Open
Conversation
LasseHenrich
marked this pull request as ready for review
July 11, 2026 08:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(For more details and the history of commits, ref. my working repo)
issue paper
For review, I recommend starting at
T1_polars.pyand/ortransformUtils.py, working yourself up the transformations in numerical order. TheT10_polars.pyshown first in the preview is probably the most complex and not very instructive to start with.Purpose
(ref. issue)
FTBench currently implements only a subset of the 15 transformations (T1 through T15) listed in the paper, on only a certain number of libraries. Our goal is to extend FTBench by completing transformations for the currently implemented libraries and/or adding transformations in new libraries.
E.g. Keras is missing T4-T9 and T13-T15, and other libraries like Pandas, polars, should be implemented.
Current implementations (before this PR with polars)
Why polars
The benchmark should implement the current state-of-the-art for fast transformations to be of value. We therefore decided to add polars to the benchmark, which is written in Rust, heavily multi-threaded (in contrast to Pandas) and therefore very fast → It's an aggressive competitor for SystemDS, beating it will be difficult.
Implementation description
The polars implementation is heavily inspired by the sklearn implementation, since the latter covers by far the most transformations – aside from SystemDS, which works very differently. It's also the cleanest implementation, in the sense that it implements a
transformUtils.pythat (i) handles the most common transformation techniques centrally so that they don't have to be repeated, and (ii) directly reads from the spec files that SystemDS uses as well, making the code arguably the most maintainable implementation.For maximizing comparability, I copied as much from the sklearn implementation as I could and stuck closesly to the overall structure. That e.g. means that (i) the polars implementation has some comments that the sklearn one has as well, (ii) there are a lot of "mirrors"-annotations referring to the sklearn code, and (iii) sometimes polars code is already refactorable (e.g. feature hashing is just implemented in
T9_polars.pyinstead oftransformUtils.py, mirroring sklearn). Much of sklearn's functionality (likeCountVectorizerandFeatureHasherclasses) is not supported in polars; when I had to implement own solutions using polars, I again focused on comparability.There are only a few cases where I intentionally developed code that behaves slightly differently, mostly in cases where the sklearn implementation worked in a way I deemed incorrect or unnecessary – ref. problems.
Test results
Some test results comparing polars and sklearn:
\wregex expression counts "¾" as a token boundary like punctuation.Missing
Still missing are the implementations for T11, T12, and T15.
Problems
1. KBinsDiscretizer is applied to all numeric columns in sklearn
sklearn's
num_pipeSince
numeric=pt∪binsand the pipeline is sequential,KBinsDiscretizeris applied to every numeric column that reaches it – includingptcolumns, not just the ones listed inencoders['bins'].Example:
adult_spec2.jsonhaspt = [4],bins = [0, 2, 10, 11, 12]andisBinDCisTrue(all bin columns are also indummycode), sopostBin = 'onehot'→ sklearn'snum_pipeone-hot-encodes 6 columns into 5 bins each (30 output columns total), where 5 of those 30 columns come from binning+one-hot-encoding the passthrough column. My polars implementation treats them separately and therefore produces 5 fewer columns than sklearn, skipping the (in sklearn's case, ultimately discarded/overwritten) extra binning work.This is likely a bug of the sklearn reference (ref. "TODO: support column specific methods and numbins" / "TODO: support mixed encoders" comments). Implications: (a) output shapes differ between the two implementations, and (b) sklearn performs more transformation work in this case.
2.
scale=Truescales categorical/binned output in sklearnsklearn implementation when
scale=True:num_pipegets aStandardScaler()(mean+std) inserted before the binning step, applied to all ofpt ∪ bins.cat_pipegetsStandardScaler(with_mean=False)appended after the one-hot/ordinal encoder, scaling the encoded categorical output.Both specs that are worked on with
scale=True(kdd_spec1.json,criteo_spec2.json) haveisBin=True. As mentioned in the first issue,KBinsDiscretizeris applied to bothptandbins, overwritingStandardScalerwith bin indices/one-hot →num_pipe'sStandardScalerhas no effect on sklearn's output for these two specs and is just dead computation.So, the effect of
scale=Truecomes just fromcat_pipe'sStandardScaler(with_mean=False)and standardizes the recode/dummy-coded categorical output → So "scale" in this benchmark suite means "standardize the encoded categorical/dummy output", which is exactly whatcat_pipedoes.3. Mixed recode/dummycode handling in sklearn
sklearn's
cat_pipeapplies one encoder to allcatCols(rcanddc) and is constructed like so:If any
dccolumn exists, every categorical column (including pure-rcones) gets one-hot encoded – as is also stated via "TODO: support mixed encoders". My Polars implementation instead (correctly) applies ordinal encoding torccolumns andto_dummiestodccolumns independently.However, I don't think this is a problem at the moment. I let AI check all specs in
systemds/specs/: in every spec whererecodeanddummycodeare non-empty,recode ⊆ dummycode→ so there is currently no "rc-only" column that would be mis-encoded by sklearn's shortcut.4. Empty equi-width bins drop
to_dummiescolumns in polarsFor a binned column that's also dummy-coded, a different number of one-hot columns is produced when some bins are empty:
KBinsDiscretizerreserves a column for every bin, including ones that contain zero rowsto_dummiesemits a column only for bins that actually occur in the data – empty bins therefore produce no column.Measured on
KDD98.csvforkdd_spec1: sklearn produces1670bin columns, polars1629. 29 binned columns miss 1 bin, 6 miss two, so41fewer columns are produced.Since empty bins carry no information, I don't care about trying to align the two implementations.