Skip to content

Plotting in 2D with one non-dimension coordinate given behaves contrary to documentation and can cause cryptic ValueError #4702

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

Open
stanwest opened this issue Dec 16, 2020 · 2 comments

Comments

@stanwest
Copy link
Contributor

What happened:

I called a 2-D plot method with one coordinate specified as a non-dimension coordinate through the x or y argument. The method raised a ValueError that complained about a tuple of duplicate coordinates that I had not provided.

Also, when I specified x and omitted y, the method took y to be the second dimension of the DataArray.

What you expected to happen:

I expected the unspecified x or y argument to behave as when I specified neither argument, so that, for a suitable DataArray da, calling da.plot(x="lon") would have the same Y axis as da.plot(). That expectation is consistent with the docstring common to 2-D plotting methods, where the default values for the x and y arguments are da.dims[1] and da.dims[0], respectively. However, as discussed below, the docstring does not mention that xarray tries to guess the omitted coordinate.

Minimal Complete Verifiable Example:

Executing

import numpy as np
import xarray as xr

da = xr.DataArray(
    np.arange(20).reshape(4, 5),
    dims=("v", "u"),
    coords={"lat": ("v", np.linspace(0, 30, 4)), "lon": ("u", np.linspace(-20, 20, 5))},
)
da.plot(x="lon")

causes (with file paths abbreviated)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\xarray\plot\plot.py", line 446, in __call__
    return plot(self._da, **kwargs)
  File "...\xarray\plot\plot.py", line 200, in plot
    return plotfunc(darray, **kwargs)
  File "...\xarray\plot\plot.py", line 707, in newplotfunc
    darray = darray.transpose(*dims, transpose_coords=True)
  File "...\xarray\core\dataarray.py", line 2036, in transpose
    dims = tuple(utils.infix_dims(dims, self.dims))
  File "...\xarray\core\utils.py", line 725, in infix_dims
    raise ValueError(
ValueError: ('u', 'u') must be a permuted list of ('v', 'u'), unless `...` is included

Executing da.plot(y="lon") causes the same exception.

Anything else we need to know?:

Two aspects of this issue that I see are the behavior and the documentation.

The behavior in the above example involves xarray trying to guess the other dimension [#1291 in v. 0.9.2]. The guessing logic tests whether the given coordinate equals one of the array's dimension names; here, the given coordinate is a non-dimension coordinate and fails to match. When the match fails, the other coordinate is set to the second dimension, whether the given coordinate was x or y. One possible improvement would be to set the y coordinate to the first dimension instead, as in the following replacement for the last line of the guessing logic:

y = darray.dims[1] if x == darray.dims[0] else darray.dims[0]

With that change, da.plot(x="lon") and da.plot(y="lat") would succeed and produce the plots I expect, while da.plot(x="lat") and da.plot(y="lon") would raise ValueError. Also, I would find it more helpful if the exception message were more clearly related to the arguments of the call. The _infer_xy_labels function might need to notice that it was about to set x and y to the same dimension name and raise an exception at that point.

The documentation seems not to have been updated for #1291 and does not mention the guessing behavior.

Environment:

Output of xr.show_versions()
INSTALLED VERSIONS
------------------
commit: None
python: 3.8.5 (default, Sep  3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: English_United States.1252
libhdf5: 1.10.4
libnetcdf: 4.7.3

xarray: 0.16.1
pandas: 1.1.3
numpy: 1.19.2
scipy: None
netCDF4: 1.5.3
pydap: None
h5netcdf: 0.8.1
h5py: 2.10.0
Nio: None
zarr: None
cftime: 1.3.0
nc_time_axis: None
PseudoNetCDF: None
rasterio: None
cfgrib: None
iris: None
bottleneck: None
dask: 2.30.0
distributed: 2.30.1
matplotlib: 3.3.2
cartopy: None
seaborn: None
numbagg: None
pint: None
setuptools: 50.3.0.post20201006
pip: 20.2.4
conda: None
pytest: None
IPython: 7.18.1
sphinx: None
@mathause
Copy link
Collaborator

Thanks for the clear report and the example! Note that da.plot(x="lon", y="lat") and da.plot(x="lon", y="v") both work. So you have at least a workaround. But yes I think it would be good to extend the logic to work when a non-dimension coordinate is passed.

@stanwest
Copy link
Contributor Author

Note that da.plot(x="lon", y="lat") and da.plot(x="lon", y="v") both work. So you have at least a workaround.

Yes. I didn't readily see that I needed to specify both x and y, partly because of the documentation and partly because I was deceived by the traceback. (Although xr.core.utils.infix_dims() raises the exception, xr.plot.utils._infer_xy_labels() causes the problem, and the responsible calls are separated by tens of lines in xr.plot.plot._plot2d().

But yes I think it would be good to extend the logic to work when a non-dimension coordinate is passed.

I agree that, if xarray is going to guess, handling non-dimension coordinates would be an improvement. It might be specified to work only with 1-D coordinates and to do nothing for multi-dimensional coordinates.

Alternatively, is it at all preferable to remove the guessing feature? Although the behavior has been present for years, I found mention of it in the documentation only in "What's New." The motivating use case in #1290 could have been addressed instead simply with z.transpose().plot(), which the guessing causes under the hood anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants