Skip to content

[ENH] Add keywords parameter to StringSimilarityEncoder - #550

Merged
solegalli merged 37 commits into
feature-engine:mainfrom
glevv:sim-enc-kwds
Nov 10, 2022
Merged

[ENH] Add keywords parameter to StringSimilarityEncoder#550
solegalli merged 37 commits into
feature-engine:mainfrom
glevv:sim-enc-kwds

Conversation

@glevv

@glevv glevv commented Oct 28, 2022

Copy link
Copy Markdown
Contributor

Added keywords support for StringSimilarityEncoder.

Keywords allow user to introduce some domain knowledge (if any) into encoder

@glevv glevv changed the title Sim enc kwds [WIP] Add keywords parameter to StringSimilarityEncoder Oct 28, 2022
@codecov

codecov Bot commented Oct 28, 2022

Copy link
Copy Markdown

Codecov Report

Merging #550 (7c230f3) into main (22e6fbf) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##             main     #550   +/-   ##
=======================================
  Coverage   97.47%   97.47%           
=======================================
  Files          90       90           
  Lines        3480     3492   +12     
  Branches      681      687    +6     
=======================================
+ Hits         3392     3404   +12     
  Misses         32       32           
  Partials       56       56           
Impacted Files Coverage Δ
feature_engine/encoding/similarity_encoder.py 98.82% <100.00%> (+0.19%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@glevv

glevv commented Oct 29, 2022

Copy link
Copy Markdown
Contributor Author

I added a bunch of tests, seems good, but maybe it needs more

@glevv
glevv marked this pull request as ready for review October 29, 2022 18:39
@glevv glevv changed the title [WIP] Add keywords parameter to StringSimilarityEncoder [ENH] Add keywords parameter to StringSimilarityEncoder Oct 29, 2022

@solegalli solegalli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @glevv

Thank you for the enhancement. It looks good to me. I have mostly some minor comments.

And one question regarding how Nan in the train set should be handled. My understanding is:

  • "impute", replaces nan by "". So when creating the dummies, would "" be one of them?
  • "raise"; raises an error
  • "ignore": nan remain nan. But the current logic, won't make "" part of the dummies?

Thanks a lot for the contribution!

to create similarity variables. Useful when someone has domain knowledge of the
problem. Could be defined only for several features; in this case for specified
features keywords will be used and most common categories will be used for
unspecified.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the last sentence of this description. Would you be able to re-phrase it?

)
elif self.missing_values == "impute":
for var in self.variables_:
for var in self.variables_:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we removing the functionality to handle missing data? I guess, not because the tests pass.

But regarding this code:

Are we not making nan part of the categories to appear as dummies?

assert tr.get_feature_names_out(input_features=input_features) == out


def test_keywords_bad_type():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we test more than just a string by using parametrize? maybe tuples, integers?

StringSimilarityEncoder(keywords="hola")


def test_keywords_bad_items():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, could we use parametrize and test more options?

StringSimilarityEncoder(keywords={"column": "hola"})


def test_keywords_dont_match(df_enc_big):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we either add columns that are in the data to the dictionary, or even better, test a second dictionary with a combination of columns that are in the data, and a column that is not?

Also, here we are testing that the variables/columns don't match, correct? can we adjust the name of the test?

assert "var_B_F" not in X.columns


def test_encode_full_keywords():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean by "full_keywords"?

@solegalli

Copy link
Copy Markdown
Collaborator

Hi @glevv

This is looking really good. Thank you so much for the quick turnaround.

I've got a couple of questions about the logic in fit:

fit logic:

  • self.encoder_dict_ is a dictionary of variables as keys and a list of dummies (or keywords in this case) as values.
  • self.keywords is the same thing, if I understand correctly: variables as keys and list of keywords as dummies.

So if the user defined all categorical variables, or the variables in the parameter variables in the self.keywords dict, in essence, self.encoder_dict_ and self.keyword dict are the same. In which case, we could just copy one to the other? instead of looping?
Would that not be faster?

At the moment, we've got the keywords dictionary after all the logic in fit():

if self.keywords:
for var in self.keywords.keys():
self.encoder_dict_[var] = self.keywords[var]

This means that for the variables in the dictionary, we are fitting twice.

I wonder if there is a way in which we could avoid running the logic twice for those variables that are in the keywords dictionary?

Something like, if self.keyword exists and all the keys are in variables_ then just copy, alternatively, define the variables not in self.keywords.

imputation logic

Could we add a few tests to corroborate that:

  • when handle_missing="impute" and the fit() data has nan,, then "" is part of the dummies? is this the expected behaviour?
  • when handle_missing="ignore" and the fit() data has nan,, then no dummy is generated at the back of it?

In short, we need a test to differentiate the behaviour between handle_missing is impute or ignore, and the train set has nan values.

Finally, we've got another PR coming up that modifies the strings of this class: #547

Thanks a lot for your time!

@glevv

glevv commented Nov 1, 2022

Copy link
Copy Markdown
Contributor Author

Done

@solegalli solegalli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @glevv

Thank you for the code changes! I think this is almost there.

Would you mind rebasing to incorporate #547 and also have a look at my comment below?

Thanks a lot!

Comment thread feature_engine/encoding/similarity_encoder.py
.head(self.top_categories)
.index.tolist()
)
if self.keywords:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we update the dictionary here, and the dictionary keys contain all the variables in variables_, we are running all of the logic between 226 and this line, for no reason. Because those keywords will not be used.

Would it not be better to check, at around line 223 that:

  • if, all of variables_ are in keywords.keys(), then just copy dict.
  • If some of variables_ are in keywords.keys(), then apply the logic to find keywords in the remaining variables
  • If keywords is None, then apply the logic in all variables_

What do you think about this?

@glevv glevv Nov 7, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it will be worse. It will make logic more complicated (which in turn will mess up coverage and tests) and won't give any speed up.

This way it is straightforward and readable.

But I will look into it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you

Comment thread tests/test_encoding/test_similarity_encoder.py
Comment thread tests/test_encoding/test_similarity_encoder.py
@glevv

glevv commented Nov 8, 2022

Copy link
Copy Markdown
Contributor Author

I implemented this logic, but tests are failing because some other transformers are not using Optional typing

see #555

@solegalli

Copy link
Copy Markdown
Collaborator

Sorry for the delay, I just merged #555

Logic changes look good to me!

@solegalli

Copy link
Copy Markdown
Collaborator

Hi @glevv

FYI: glevv#4

and we are then good to go.

Thanks for the contribution!!!

@solegalli
solegalli merged commit 9a6fd8d into feature-engine:main Nov 10, 2022
@glevv
glevv deleted the sim-enc-kwds branch November 10, 2022 11:33
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.

2 participants