Skip to content

[MRG+2?] Metaestimator delegation #3982

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 27, 2014

Conversation

amueller
Copy link
Member

Slight change to #2854.

@ogrisel
Copy link
Member

ogrisel commented Dec 18, 2014

if we are to use the factory directly in the decorating lines, I would rather change its name, for instance, have:

@make_delegation_decorator('_final_estimator')
def predict(self, X):
    ...

replaced by:

@if_delegate_has_method(delegate='_final_estimator')
def predict(self, X):
    ...

WDYT?

I am not 100% decided about this either.

@ogrisel
Copy link
Member

ogrisel commented Dec 18, 2014

@amueller can you rebase on master and fix the conflicts to get travis to run?

@amueller amueller force-pushed the metaestimator_delegation branch from 6c607ae to 86f4ee1 Compare December 18, 2014 22:49
@amueller
Copy link
Member Author

rebased.
I like your naming suggestion.

@jnothman
Copy link
Member

Thanks for working on this. I like @ogrisel's naming suggestion, and I think @amueller is right that naming the decorators was an unnecessary indirection.

However, I probably should have tested the following: #2854 will transfer the docstring from the function defining Pipeline.transform to the descriptor that takes its place. That currently doesn't happen here, as shown by:

from sklearn.pipeline import Pipeline
help(Pipeline)

(comparing both PRs)

We could write a test along the lines of:

def foo():
   """str"""
   pass

assert foo.__doc__ == my_wrapper(foo).__doc__

In either case the function signature isn't shown which is unfortunate, and may need to be compensated for by writing out the signature within the docstring.

A related problem, that I didn't test for in #2854, and which might not be so easy to fix, is that none of those methods render in the sphinx documentation :(((

@larsmans
Copy link
Member

How about delegate_to?

@amueller
Copy link
Member Author

Hum, that is odd. I checked the docstring on the decision function and it was transferred.
I'll add a test. The documentation is an interesting problem. I'll have to think about it. Do you know how that works for properties usually?

@amueller
Copy link
Member Author

@larsmans It doesn't delegate to that object necessarily. For the gridsearch, the parameter is estimator but it is delegated to best_estimator_.

@amueller
Copy link
Member Author

@jnothman
So I don't entirely understand what happens with the docstrings.

from sklearn.pipeline import Pipeline
from sklearn.svm import SVC
help(Pipeline([('svm', SVC)]).decision_function)

gives the right docstring, though help(Pipeline) does not.
Can you explain?
I'm a bit slow today, I think I got it now.

@amueller
Copy link
Member Author

What I still don't understand: what is it that help(Pipeline) displays. It is not __doc__.
I just fixed my PR, but that didn't change Pipeline.__doc__ as that does not include the function definitions. I have no idea how to test this, as I don't know how to access this docstring.

@amueller
Copy link
Member Author

it seems you can use Pipeline.__dict__['decision_function'].__doc__.... alright then

@amueller
Copy link
Member Author

Fixed the docstrings in sphinx.
You can now do
Pipeline.decision_function.__doc__.

@amueller
Copy link
Member Author

The somewhat odd fix with checking if the object is None would also be necessary for the @property based solution FYI, so that needs to be:

    @property
    def predict_proba(self):
            if self is not None:
                self.final_estimator.predict_proba
            return self._predict_proba

On the other hand, I think the @property based solution might not break the github source links.

@amueller amueller force-pushed the metaestimator_delegation branch 2 times, most recently from dc86a16 to 3c8df92 Compare December 19, 2014 22:11
@amueller
Copy link
Member Author

@GaelVaroquaux It would be great if you had time to have a look. The actual implementation is really short and not that meta any more. The only downside that I can currently see is that introspection doesn't find the right line numbers for the function any more.

@amueller amueller force-pushed the metaestimator_delegation branch from 3c8df92 to 41e2c10 Compare December 19, 2014 22:27
@jnothman
Copy link
Member

help(Pipeline([('svm', SVC)]).decision_function)
gives the right docstring, though help(Pipeline) does not.
Can you explain?

Sure. In the first case you retrieved the attribute (i.e. executed the __get__ of the delegation descriptor) and read it return value's __doc__. In the second case you viewed the __doc__ of the descriptor instance.



class _IffHasAttrDescriptor(object):
"""Implements a conditional propery using the descriptor protocol.
Copy link
Member

Choose a reason for hiding this comment

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

*property

Copy link
Member

Choose a reason for hiding this comment

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

this typo has not yet been fixed.

@jnothman
Copy link
Member

Perhaps in the future we will hack sphinx to show the correct signature and source link.

In the meantime I think this is a moderately serious bug that results in silent misbehaviour, and we should merge it ASAP. Although there are concerns about code quality, as expressed by @GaelVaroquaux, the perfect solution can wait, as the bug fix cannot.

I give @amueller's work a +1, and he has implicitly given my contributions to this a +1... @larsmans has given an earlier version of this a +1, so as far as I'm concerned this has +2. If an external party would like to give this another look over, that would be great, but otherwise we should aim to merge.

@jnothman jnothman changed the title [MRG] Metaestimator delegation [MRG+2?] Metaestimator delegation Dec 24, 2014
... def __init__(self, sub_est):
... self.sub_est = sub_est
...
... @iff_sub_est_has_attr
Copy link
Member

Choose a reason for hiding this comment

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

I would put @if_delegate_has_method(delegate='sub_est') directly here instead of introducing the iff_sub_est_has_attr indirection that does not only bring complexity to the example.

Copy link
Member Author

Choose a reason for hiding this comment

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

sure.

@ogrisel
Copy link
Member

ogrisel commented Dec 24, 2014

I think I like this solution as well.

@amueller
Copy link
Member Author

I think we should merge. As @jnothman points out, this is a fix to a long-standing bug, and does not break or introduce any public changes. Changing the implementation if we are unhappy with it should therefore be easy.

@jnothman
Copy link
Member

and does not break or introduce any public changes.

except sphinx ;)

larsmans added a commit that referenced this pull request Dec 27, 2014
Metaestimator delegation/ducktyping. Fixes #1805.
@larsmans larsmans merged commit 8e3f21d into scikit-learn:master Dec 27, 2014
@amueller
Copy link
Member Author

Thanks a lot for all the work you put into this @jnothman, and thanks for the reviews :)
Some of the other branches @jnothman had still have some more docstrings I think. I'll try to get them after the holidays.

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.

4 participants