Implemented adaptive squashing#1310
Conversation
|
Here is an implementation of the robust scaling + smooth clipping from my paper (https://arxiv.org/abs/2407.04491). It could be used as a robust numerical preprocessing methods for neural network based methods. Some considerations:
|
Vincent-Maladiere
left a comment
There was a problem hiding this comment.
Hey @dholzmueller thank you for this PR!
If you want to make your class public, you should add an entry in doc/reference/index.rst, maybe in the "cleaning a dataframe" section?
Also, I think having a smallish example that shows when and where to use your class would be great. WDYT?
Co-authored-by: Vincent M <maladiere.vincent@yahoo.fr>
Sounds good, where would you put it? |
…ion, removed copy, removed column rename
|
I addressed everything except from the example now. |
|
I removed the test that tested if a column with name |
jeromedockes
left a comment
There was a problem hiding this comment.
a first few comments, I'll add more. thanks this is great!! 🚀
@dholzmueller, good question, I'm not sure. What would be the best way to illustrate its usefulness? Do you have a small benchmark or experiment that could fit into an example? Otherwise we might need to be creative |
|
We could try it on a dataset with an MLP and see if it improves something (compared to StandardScaler? or QuantileTransformer?). Don't know what would be the best one to do this. |
|
In what setup (dataset + model) have you observed a sharp improvement in your experiments? Otherwise, we could try on any skrub dataset with an MLP as you suggest |
…columns, cleaned up the example, renamed lower_quantile_alpha to lower_quantile and upper_quantile_alpha to upper_quantile
|
Updates:
|
jeromedockes
left a comment
There was a problem hiding this comment.
Great, thanks a lot @dholzmueller . I just have a small comment on the fitted attributes but LGTM otherwise :)
| return result | ||
|
|
||
|
|
||
| class SquashingScaler(BaseEstimator, TransformerMixin): |
There was a problem hiding this comment.
even though it wouldn't make a difference here normally we would put the transformermixin first because we want methods to be looked up there before looking in baseestimator
| class SquashingScaler(BaseEstimator, TransformerMixin): | |
| class SquashingScaler(TransformerMixin, BaseEstimator): |
| self: SquashingScaler | ||
| The current object. | ||
| """ | ||
| self.tfm_ = SingleColumnSquashingScaler( |
There was a problem hiding this comment.
maybe we could make the transformer private by renaming it to self._transformer and exposing its learned attributes directly by copying them to the outer object's dict with something like
if isinstance(self._transformer, OnEachColumn):
self.median_ = [t.median_ for t in self._transformer.transformers_.values()]
self.scale_ = [t.median_ for t in self._transformer.transformers_.values()]
else:
self.median_ = self._transformer.median_
self.scale_ = self._transformer.scale_There was a problem hiding this comment.
(in this case we could add a section like
Attributes
----------
median_ : float or list of float
median of the training data
scale_ : ...
just below the 'parameters' section in the docstring )
|
@GaelVaroquaux was suggesting to implement SquashingScaler directly instead of with the indirection through SingleColumnSquashingScaler. I'll need to check how easy that is... |
There was a problem hiding this comment.
Overall, I think that the code in this PR is not documented well enough, as there are a lot of parts that "do stuff" in a non-transparent way, and that would benefit from being commented more.
Besides being a problem to maintain (I had to try and reverse engineer most of the code and the tests to figure out how it worked), I think that the scaling could have some unexpected results that may surprise users, and that are not documented or explained properly in the docstrings or in the code itself.
|
Hey @rcap107 thanks for reviewing. I iterated on the documentation; let me know what you think |
rcap107
left a comment
There was a problem hiding this comment.
Thanks for adding all the comments @Vincent-Maladiere, now the code is much clearer 👍
It's much better, thanks a lot! I fixed a small typo, but aside from that I think we can merge this |
|
Great! Waiting for other people to give their feedback |
Co-authored-by: Riccardo Cappuzzo <7548232+rcap107@users.noreply.github.com>
|
Interface looks good to me! Did you test what happens if you pass in an all-nan column? |
|
I'm waiting for @Vincent-Maladiere 's reply to @dholzmueller 's above, and then merge |
Good question. We get a column full of nans, since they are left untouched. Is that what you would expect? Reproducer: import numpy as np
import skrub
import pandas as pd
skrub.SquashingScaler().fit_transform(
pd.DataFrame(dict(a=[np.nan, np.nan]))
) |
|
Yes, that is what I would expect. Just wasn't sure if it's going to work with the nanmax/nanmin operations. |
|
Just a thought, having quantile_range as a tuple instead of two separate values might make it harder for people to tune it in some hyperparameter optimization. I think it's probably not an important hyperparameter to tune, though. |
This can be addressed in another PR |
you mean, another one for the same release? because it's not as convenient to change the parametes after release, right? |
It's not a very big deal, and it should not be a big change, up to you @Vincent-Maladiere (I'd rather have this PR in the release and open a new one later than risk not having it) |
|
That's a good point, both sklearn's RobustScaler and MinMaxScaler have a tuple range as a hyperparameter, so I think this is fine. |
|
okay, then let's leave it like it is. |
|
OK, I suggest that we merge, and worry about the NaN thing later: it can be addressed later
|
|
I think the NaN thing is not an issue, since Vincent tested it and it worked. |
|
Merged! Thanks everyone |
WIP