Skip to content
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

Clean up and enhance plot method docstrings #5285

Merged
merged 17 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
"hashable": ":term:`hashable <name>`",
# matplotlib terms
"color-like": ":py:func:`color-like <matplotlib.colors.is_color_like>`",
"matplotlib colormap name": ":doc:matplotlib colormap name <Colormap reference>",
"matplotlib colormap name": ":doc:`matplotlib colormap name <matplotlib:gallery/color/colormap_reference>`",
"matplotlib axes object": ":py:class:`matplotlib axes object <matplotlib.axes.Axes>`",
"colormap": ":py:class:`colormap <matplotlib.colors.Colormap>`",
# objects without namespace
Expand Down
3 changes: 3 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Bug fixes

Documentation
~~~~~~~~~~~~~
- Clean up and enhance docstrings for the :py:class:`DataArray.plot` and ``Dataset.plot.*``
families of methods (:pull:`5285`).
By `Zach Moon <https://github.com/zmoon>`_.


Internal Changes
Expand Down
112 changes: 70 additions & 42 deletions xarray/plot/dataset_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,82 +196,98 @@ def _dsplot(plotfunc):

ds : Dataset
x, y : str
Variable names for x, y axis.
Variable names for the *x* and *y* grid positions.
u, v : str, optional
Variable names for quiver or streamplot plots only
Variable names for the *u* and *v* velocities
(in *x* and *y* direction, respectively; quiver/streamplot plots only).
hue: str, optional
Variable by which to color scattered points or arrows
hue_style: str, optional
Can be either 'discrete' (legend) or 'continuous' (color bar).
Variable by which to color scatter points or arrows.
hue_style: {'continuous', 'discrete'}, optional
How to use the ``hue`` variable:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe "how to color" instead of "how to use"?


- ``'continuous'`` -- continuous color scale
(default for numeric ``hue`` variables)
- ``'discrete'`` -- a color for each unique value, using the default color cycle
(default for non-numeric ``hue`` variables)
markersize: str, optional
scatter only. Variable by which to vary size of scattered points.
size_norm: optional
Either None or 'Norm' instance to normalize the 'markersize' variable.
Variable by which to vary the size of scattered points (scatter plot only).
size_norm: matplotlib.colors.Normalize or tuple, optional
Used to normalize the ``markersize`` variable.
If a tuple is passed, the values will be passed to
:py:class:`matplotlib:matplotlib.colors.Normalize` as arguments.
Default: no normalization (``vmin=None``, ``vmax=None``, ``clip=False``).
scale: scalar, optional
Quiver only. Number of data units per arrow length unit.
Use this to control the length of the arrows: larger values lead to
smaller arrows
add_guide: bool, optional
Add a guide that depends on hue_style
- for "discrete", build a legend.
This is the default for non-numeric `hue` variables.
- for "continuous", build a colorbar
smaller arrows.
add_guide: bool, optional, default: True
Copy link
Contributor

Choose a reason for hiding this comment

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

This should also control the quiverkey for quiver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That seems like it could make sense.

Add a guide that depends on ``hue_style``:

- ``'continuous'`` -- build a colorbar
- ``'discrete'`` -- build a legend
row : str, optional
If passed, make row faceted plots on this dimension name
If passed, make row faceted plots on this dimension name.
col : str, optional
If passed, make column faceted plots on this dimension name
If passed, make column faceted plots on this dimension name.
col_wrap : int, optional
Use together with ``col`` to wrap faceted plots
Use together with ``col`` to wrap faceted plots.
ax : matplotlib axes object, optional
If None, uses the current axis. Not applicable when using facets.
If ``None``, use the current axes. Not applicable when using facets.
subplot_kws : dict, optional
Dictionary of keyword arguments for matplotlib subplots. Only applies
to FacetGrid plotting.
Dictionary of keyword arguments for Matplotlib subplots
(see :py:meth:`matplotlib:matplotlib.figure.Figure.add_subplot`).
Only applies to FacetGrid plotting.
aspect : scalar, optional
Aspect ratio of plot, so that ``aspect * size`` gives the width in
Aspect ratio of plot, so that ``aspect * size`` gives the *width* in
inches. Only used if a ``size`` is provided.
size : scalar, optional
If provided, create a new figure for the plot with the given size.
Height (in inches) of each plot. See also: ``aspect``.
norm : ``matplotlib.colors.Normalize`` instance, optional
If the ``norm`` has vmin or vmax specified, the corresponding kwarg
must be None.
If provided, create a new figure for the plot with the given size:
*height* (in inches) of each plot. See also: ``aspect``.
norm : matplotlib.colors.Normalize, optional
If the :py:class:`~matplotlib.colors.Normalize` instance
has ``vmin`` or ``vmax`` specified, the corresponding
kwarg must be ``None``.
vmin, vmax : float, optional
Values to anchor the colormap, otherwise they are inferred from the
data and other keyword arguments. When a diverging dataset is inferred,
setting one of these values will fix the other by symmetry around
``center``. Setting both values prevents use of a diverging colormap.
If discrete levels are provided as an explicit list, both of these
values are ignored.
cmap : str or colormap, optional
cmap : matplotlib colormap name or colormap, optional
The mapping from data values to color space. Either a
matplotlib colormap name or object. If not provided, this will
be either ``viridis`` (if the function infers a sequential
dataset) or ``RdBu_r`` (if the function infers a diverging
dataset). When `Seaborn` is installed, ``cmap`` may also be a
`seaborn` color palette. If ``cmap`` is seaborn color palette,
Matplotlib colormap name or object. If not provided, this will
be either ``'viridis'`` (if the function infers a sequential
dataset) or ``'RdBu_r'`` (if the function infers a diverging
dataset).
See :doc:`Choosing Colormaps in Matplotlib <matplotlib:tutorials/colors/colormaps>`
for more information.

If *seaborn* is installed, ``cmap`` may also be a
`seaborn color palette <https://seaborn.pydata.org/tutorial/color_palettes.html>`_.
Note: if ``cmap`` is a seaborn color palette,
``levels`` must also be specified.
colors : color-like or list of color-like, optional
colors : str or array-like of color-like, optional
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think that's right, color-like doesn't have to be a str, it can also be a tuple of floats describing RGB or RGBA values:

Suggested change
colors : str or array-like of color-like, optional
colors : color-like or array-like of color-like, optional

Copy link
Contributor Author

@zmoon zmoon May 13, 2021

Choose a reason for hiding this comment

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

From pyplot.contour:

As a shortcut, single color strings may be used in place of one-element lists, i.e. 'red' instead of ['red'] to color all levels with the same color. This shortcut does only work for color strings, not for other ways of specifying colors.

I think I tried and found it to be the case with xarray.plot.contour as well. That is, it can be a string, or it can be an array-like of color-like values.

Edit: just checked it again. For example, colors=(0, 0, 0) raises ValueError: Invalid RGBA argument: 0, but colors=[(0, 0, 0)] or colors="black" are fine.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ds.plot.scatter(..., colors=(1, 0, 0)) does not complain, but I can't get it to change the color of the scatter marks (not even with colors="r") so I might be missing something.

Copy link
Contributor Author

@zmoon zmoon May 15, 2021

Choose a reason for hiding this comment

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

Yeah, it seems like the colors argument is not used in ds.plot.scatter. But no error is raised, colors is just silently dropped (ax.scatter would raise AttributeError if it weren't) in most cases. I did find that you can get the marker colors to change by passing color or c with a discrete hue_style.

import xarray as xr
import numpy as np
ds = xr.tutorial.scatter_example_dataset()
ds["c"] = (ds.A.dims, np.random.choice(("r", "g", "b"), ds.A.shape))
ds.plot.scatter("A", "B", hue="c", c=(0, 0, 0))  # works but prints (doesn't raise) warning
ds.plot.scatter("A", "B", hue="c", color=(0, 0, 0))  # works

However, if you have a continuous hue_style, passing c is ignored (since this is done internally), and passing color raises ValueError since it conflicts with c.

It seems like at the moment, colors is really only intended to be used with contour(f) for levels, like how it is in Matplotlib. Maybe in the future it could be used to allow passing colors to be used in the color cycle for discrete hue_style, but it doesn't currently do that. So for now, maybe in the docstring we should note this current behavior (doing nothing or raising error).

levels is also unused in _dsplot functions, maybe could be removed actually I was able to get colors to sort of work with discrete hue_style by also providing levels, but levels only makes sense for numeric type:

ds["c2"] = (ds.A.dims, np.random.choice((1, 2, 3), ds.A.shape))
ds.plot.scatter("A", "B", hue="c2", colors=["r", "g", "b"], levels=[1, 2, 3, 4])  # works (rgb)
ds.plot.scatter("A", "B", hue="c2", hue_style="discrete", colors=["r", "g", "b"])  # `colors` does nothing

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can address this by raising appropriate errors (or implementing it) in a future PR.

Copy link
Contributor Author

@zmoon zmoon May 18, 2021

Choose a reason for hiding this comment

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

Ok, I am interested in working on this colors stuff. If that is ok, I will open new issue from my comment above and go from there.

Copy link
Contributor

Choose a reason for hiding this comment

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

sounds great.

A single color or a list of colors. The ``levels`` argument
is required.
center : float, optional
The value at which to center the colormap. Passing this value implies
use of a diverging colormap. Setting it to ``False`` prevents use of a
diverging colormap.
robust : bool, optional
If True and ``vmin`` or ``vmax`` are absent, the colormap range is
If ``True`` and ``vmin`` or ``vmax`` are absent, the colormap range is
computed with 2nd and 98th percentiles instead of the extreme values.
extend : {"neither", "both", "min", "max"}, optional
extend : {'neither', 'both', 'min', 'max'}, optional
How to draw arrows extending the colorbar beyond its limits. If not
provided, extend is inferred from vmin, vmax and the data limits.
levels : int or list-like object, optional
Split the colormap (cmap) into discrete color intervals. If an integer
provided, ``extend`` is inferred from ``vmin``, ``vmax`` and the data limits.
levels : int or array-like, optional
Split the colormap (``cmap``) into discrete color intervals. If an integer
is provided, "nice" levels are chosen based on the data range: this can
imply that the final number of levels is not exactly the expected one.
Setting ``vmin`` and/or ``vmax`` with ``levels=N`` is equivalent to
setting ``levels=np.linspace(vmin, vmax, N)``.
**kwargs : optional
Additional keyword arguments to matplotlib
Additional keyword arguments to wrapped Matplotlib function.
"""

# Build on the original docstring
Expand Down Expand Up @@ -463,9 +479,11 @@ def plotmethod(


@_dsplot
def scatter(ds, x, y, ax, u, v, **kwargs):
def scatter(ds, x, y, ax, **kwargs):
"""
Scatter Dataset data variables against each other.

Wraps :py:func:`matplotlib:matplotlib.pyplot.scatter`.
"""

if "add_colorbar" in kwargs or "add_legend" in kwargs:
Expand All @@ -482,6 +500,10 @@ def scatter(ds, x, y, ax, u, v, **kwargs):
size_norm = kwargs.pop("size_norm", None)
size_mapping = kwargs.pop("size_mapping", None) # set by facetgrid

# Remove `u` and `v` so they don't get passed to `ax.scatter`
kwargs.pop("u", None)
kwargs.pop("v", None)

# need to infer size_mapping with full dataset
data = _infer_scatter_data(ds, x, y, hue, markersize, size_norm, size_mapping)

Expand Down Expand Up @@ -519,7 +541,10 @@ def scatter(ds, x, y, ax, u, v, **kwargs):

@_dsplot
def quiver(ds, x, y, ax, u, v, **kwargs):
"""Quiver plot with Dataset variables."""
"""Quiver plot of Dataset variables.

Wraps :py:func:`matplotlib:matplotlib.pyplot.quiver`.
"""
import matplotlib as mpl

if x is None or y is None or u is None or v is None:
Expand Down Expand Up @@ -548,7 +573,10 @@ def quiver(ds, x, y, ax, u, v, **kwargs):

@_dsplot
def streamplot(ds, x, y, ax, u, v, **kwargs):
"""Quiver plot with Dataset variables."""
"""Plot streamlines of Dataset variables.

Wraps :py:func:`matplotlib:matplotlib.pyplot.streamplot`.
"""
import matplotlib as mpl

if x is None or y is None or u is None or v is None:
Expand Down
Loading