pydata / xarray

N-D labeled arrays and datasets in Python
https://xarray.dev
Apache License 2.0
3.56k stars 1.07k forks source link

col_wrap arg to da.plot() can be a string? #5614

Open dschneiderch opened 3 years ago

dschneiderch commented 3 years ago

What happened: if you try to plot a data array with 3 dimensions but the third dim has length 1, then col_wrap accepts a dim name and plots facets with pcolormesh. according to the documentation col_wrap argument should be an int so I assume this is unintentional http://xarray.pydata.org/en/stable/generated/xarray.DataArray.plot.html

if dim 3 has length >1, then col_wrap still takes a str but uses hist()

What you expected to happen: I expected an error if col_wrap is not an int. I also expected da.plot(col='3rd_dim') to give me one facet if the dim has length 1 - this (mis)behavior is described in #620

all a bit confusing :). I thought I understood plotting but then ran into bug #620 and started fumbling around. I am trying to plot a dataarray in a function but I don't know ahead of time how many values 3rd dim has. I also wanted to avoid limiting my function to pcolormesh() only. da.plot.pcolormesh(col='3rd_dim') works to plot the images regardless of dim length.

Minimal Complete Verifiable Example:

da_list = []
for i in np.arange(1,3):
    data = np.ones((64, 48), dtype = 'uint8') * (200-i*15)
    oneframe = xr.DataArray(
        data=data[..., None],
        dims=('x', 'y','measurement'),
        coords={'measurement':[f't{i*40}']}
    )

    da_list.append(oneframe)

twoframes = xr.concat(da_list, 'measurement')
oneframe.plot(col_wrap='measurement') # Oddly, facets with pcolormesh()
twoframes.plot(col_wrap='measurement') # Oddly, a single facet with hist()
oneframe.plot(col='measurement') # ValueError: IndexVariable objects must be 1-dimensional ala issue #620
twoframes.plot(col='measurement') # facets with pcolormesh()
oneframe.plot.pcolormesh(col='measurement') # facets with pcolormesh()
twoframes.plot.pcolormesh(col='measurement') #facets with pcolormesh()

Anything else we need to know?: @nfahlgren noted a workaround using FacetGrid directly

from xarray.plot.facetgrid import FacetGrid
gd = FacetGrid(data=oneframe, col="measurement")
gd.map_dataarray(xr.plot.imshow, "x", "y")

is consistent with:

gd = FacetGrid(data=twoframes, col="measurement")
gd.map_dataarray(xr.plot.imshow, "x", "y")

Environment: Windows 10, python 3.7

Output of xr.show_versions() INSTALLED VERSIONS ------------------ commit: None python: 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 15:37:01) [MSC v.1916 64 bit (AMD64)] python-bits: 64 OS: Windows OS-release: 10 machine: AMD64 processor: Intel64 Family 6 Model 158 Stepping 10, GenuineIntel byteorder: little LC_ALL: None LANG: None LOCALE: None.None libhdf5: None libnetcdf: None xarray: 0.17.0 pandas: 1.2.3 numpy: 1.20.1 scipy: 1.6.0 netCDF4: None pydap: None h5netcdf: None h5py: None Nio: None zarr: 2.7.0 cftime: None nc_time_axis: None PseudoNetCDF: None rasterio: None cfgrib: None iris: None bottleneck: None dask: 2021.02.0 distributed: 2021.02.0 matplotlib: 3.3.4 cartopy: None seaborn: None numbagg: None pint: None setuptools: 49.6.0.post20210108 pip: 21.1.2 conda: 4.10.1 pytest: 6.2.2 IPython: 7.24.1 sphinx: None
dcherian commented 3 years ago

col_wrap should be an int. So we should raise an error there if col_wrap is not None and (col is None or row is None) as you say. And another if not isinstance(col_wrap, int). Pull requests are very welcome!

We will have to go through a deprecation cycle to fix #620

dschneiderch commented 3 years ago

small correction above, this first 2 cases are not being facetted and I think col_wrap is just being ignored. oneframe.plot(col_wrap='measurement') outputs: <matplotlib.collections.QuadMesh at 0x16ea840f7c8> compared with oneframe.plot.pcolormesh(col='measurement') outputs: <xarray.plot.facetgrid.FacetGrid at 0x1b26a1f5ec8>

i'll see if i can make sense of the code base and submit a PR.