-
-
Notifications
You must be signed in to change notification settings - Fork 26.2k
ENH Add a tooltip for the RocCurveDisplay #31860
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
base: main
Are you sure you want to change the base?
Conversation
sklearn/utils/_plotting.py
Outdated
# Set an attribute on the axes annotation to be able to keep only one visible | ||
# at a time when there are multiple display instances that share an axes. | ||
setattr(self.line_tooltip_, "_skl_line_tooltip", True) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not ideal, we set an attribute on a matplotlib object. An alternative could be to have a class attribute that is a mapping from axes to something and reuse the same matplotlib annotation if a new display instance reuses the axes. But it's not very clean either...
Another alternative could be to subclass matplotlib.axes.Axes to add some metadata, but then the display.ax_
won't be a proper matplotlib.axes.Axes instance, so still not ideal...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
won't be a proper matplotlib.axes.Axes instance, so still not ideal
Why do you think that would be a problem? Subclassing sounds a bit cleaner, unless the matplotlib documentation explicitly discourages subclassing for one reason or another.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did the subclassing in aea3c92. (I meant subclassing Annotation
). I also find it a bit cleaner. What I find problematic is not visible in this PR yet because I didn't document the new attribute. But it would be something like:
line_tooltip_ : matplotlib.text.Annotation instance (or now) LineTooltip instance
Annotation along the roc curve...
And that annotation is customizable using the Annotation API. A bit like we expose ax_
, figure_
, line_
for the user to be able to customize the plot. This is not clear if it's not documented as an Annotation object.
Maybe we can mention explicitly in the line_tooltip_
description that it's a subclass of Annotation and that's enough. What do you think ?
# Compute an offset for the text depending on the quadrant where the cursor is | ||
# to keep the tooltip somewhat inside the axes. | ||
xlim, ylim = self.ax_.get_xlim(), self.ax_.get_ylim() | ||
x_offset = 20 if x < (xlim[0] + xlim[1]) / 2 else -160 | ||
y_offset = 20 if y < (ylim[0] + ylim[1]) / 2 else -30 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These hardcoded numbers are not perfect for all cases. We can have a constant offset and let the tooltip be outside the axes sometimes, or I can try to find a way to use the text length to have a matching offset.
@lucyleeow could you please maybe have a look? |
Could you update the ROC curve display examples to set |
Agreed that it would be nice to see how this works inside a notebook with |
011bd70
to
d6d6998
Compare
How can I add |
Maybe the easiest way to try it yourself is to fetch my branch and run one of these:
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import RocCurveDisplay
import matplotlib.pyplot as plt
X, y = make_classification(n_samples=200, class_sep=0.5)
clf = LogisticRegression().fit(X, y)
RocCurveDisplay.from_estimator(clf, X, y)
plt.show()
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.metrics import RocCurveDisplay
from sklearn.model_selection import cross_validate
from sklearn.linear_model import LogisticRegression
X, y = make_classification(n_samples=200, class_sep=0.5, random_state=0)
clf = LogisticRegression()
cv_results = cross_validate(
clf, X, y, cv=3, return_estimator=True, return_indices=True
)
RocCurveDisplay.from_cv_results(cv_results, X, y)
plt.show()
from sklearn.metrics import RocCurveDisplay
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
X, y = make_classification(n_samples=200, class_sep=0.5)
X_, y_ = make_classification(n_samples=200, class_sep=0.85)
clf = LogisticRegression().fit(X, y)
clf2 = LogisticRegression().fit(X_, y_)
disp = RocCurveDisplay.from_estimator(clf, X, y)
RocCurveDisplay.from_estimator(clf2, X_, y_, ax=disp.ax_)
plt.show() |
That's a good question, I spoke too quickly because I am used to working with jupytext/jupyterbook on other projects. For the scikit-learn doc, we would probably need non-trivial changes sphinx-gallery to allow for this. I know that jupytext for instance can convert python script with commented out lines that start with |
contain values for a single curve. If plotting multiple curves, list should be | ||
of same length as `fpr` and `tpr`. | ||
Only used to display the threshold values along the curve as a tooltip. If None, | ||
only the fpr and tpr values are displayed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this docstring should mention that it's necessary to install the ipympl
package and use the %matplotlib widget
magic to use this feature in jupyter notebooks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user guide could also benefit from a new paragraph to explain how to enable such interactive tooltips.
BTW, I tried in a VS Code interactive window with |
Can you try to add it to at least one other display (e.g. PR curve) to check that the design is generic enough to be reused? |
Done in e916a77. |
Looks like there's a way but in the SG config file (see d0735d5). It's not visible in the HTML rendered examples but is there in the generated notebook. In all generated notebooks actually. I wanted to check the jupyterlite button from the generated example but the tooltip doesn't show up :( Also I didn't find where to request ipympl to be installed for jupyterlite. @lesteve can you show me ? |
By the way, sphing-gallery supports examples using |
What would be required for an interactive plot to be rendered - i.e. what would the rst look like? Plotly simply uses the .. raw:: html
<div class="output_subarea output_html rendered_html output_result"> followed by the html of the figure. Note that matplotlib animations are also supported. Depending on what is set as the format in config, either raw html is embedded (similar to above) or a video file is saved and a video directive (I think this needs the sphinxcontrib-video extension) is used. |
This PR proposes to add a tooltip along the curves of

RocCurveDisplay
displaying the fpr, tpr and threshold values.Regarding implementation, it's done through a Mixin class that is responsible for creating the annotation and handle the mouse events occurring on the curve. The mixin is in _plotting and uses generic variable names because it's not dedicated to RocCurveDisplay, but is instead ready to be used in other displays.
Currently, the tooltip will always be visible on hovering a curve. We could add a parameter to enable it or not. Not sure it's necessary but why not. Or maybe a global config ?
The tooltip is displayed in notebooks if
%matplotlib widget
is set. Otherwise, the plot works as usual, but the tooltip is not visible.cc/ @glemaitre