Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Imshow breaks if given a unyt_array input #18077

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

Closed
neutrinoceros opened this issue Jul 26, 2020 · 6 comments · Fixed by #18286
Closed

Imshow breaks if given a unyt_array input #18077

neutrinoceros opened this issue Jul 26, 2020 · 6 comments · Fixed by #18286

Comments

@neutrinoceros
Copy link
Contributor

Bug report

Bug summary
matplotlib.pyplot.imshow doesn't seem to play nicely with unyt_arrays like other plotting functions do.

Description

This problem was originally reported on unyt (yt-project/unyt#161)
According to unyt's documentation matplotlib is "unyt_aware", but it seems that some plotting functions are not well behaved.

What I Did

Code for reproduction

import unyt
import numpy as np
from matplotlib import pyplot as plt

arr = unyt.unyt_array(np.ones((16, 16)), "g/cm**3")
plt.pcolormesh(arr) # works
plt.contourf(arr) # works
plt.imshow(arr) # breaks
>> UnitOperationError: The <ufunc 'add'> operator for unyt_arrays with units "g/cm**3" 
(dimensions "(mass)/(length)**3") and "dimensionless" (dimensions "1") is not well defined.

Actual outcome

Full trace

UnitOperationError                        Traceback (most recent call last)
<ipython-input-9-7192cbd0f805> in <module>
----> 1 plt.savefig("hello")

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/pyplot.py in savefig(*args, **kwargs)
    840 def savefig(*args, **kwargs):
    841     fig = gcf()
--> 842     res = fig.savefig(*args, **kwargs)
    843     fig.canvas.draw_idle()   # need this if 'transparent=True' to reset colors
    844     return res

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/figure.py in savefig(self, fname, transparent, **kwargs)
   2309                 patch.set_edgecolor('none')
   2310
-> 2311         self.canvas.print_figure(fname, **kwargs)
   2312
   2313         if transparent:

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2208
   2209             try:
-> 2210                 result = print_method(
   2211                     filename,
   2212                     dpi=dpi,

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs)
   1637             kwargs.pop(arg)
   1638
-> 1639         return func(*args, **kwargs)
   1640
   1641     return wrapper

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs, *args)
    507             *metadata*, including the default 'Software' key.
    508         """
--> 509         FigureCanvasAgg.draw(self)
    510         mpl.image.imsave(
    511             filename_or_obj, self.buffer_rgba(), format="png", origin="upper",

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in draw(self)
    405              (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
    406               else nullcontext()):
--> 407             self.figure.draw(self.renderer)
    408             # A GUI class may be need to update a window using this draw, so
    409             # don't forget to call the superclass.

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
   1861
   1862             self.patch.draw(renderer)
-> 1863             mimage._draw_list_compositing_images(
   1864                 renderer, self, artists, self.suppressComposite)
   1865

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    409                          else deprecation_addendum,
    410                 **kwargs)
--> 411         return func(*inner_args, **inner_kwargs)
    412
    413     return wrapper

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe)
   2746             renderer.stop_rasterizing()
   2747
-> 2748         mimage._draw_list_compositing_images(renderer, self, artists)
   2749
   2750         renderer.close_group('axes')

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/image.py in draw(self, renderer, *args, **kwargs)
    636                 renderer.draw_image(gc, l, b, im, trans)
    637         else:
--> 638             im, l, b, trans = self.make_image(
    639                 renderer, renderer.get_image_magnification())
    640             if im is not None:

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/image.py in make_image(self, renderer, magnification, unsampled)
    921         clip = ((self.get_clip_box() or self.axes.bbox) if self.get_clip_on()
    922                 else self.figure.bbox)
--> 923         return self._make_image(self._A, bbox, transformed_bbox, clip,
    924                                 magnification, unsampled=unsampled)
    925

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/matplotlib/image.py in _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification, unsampled, round_to_pixel_border)
    446                 self.norm.autoscale_None(A)
    447                 dv = np.float64(self.norm.vmax) - np.float64(self.norm.vmin)
--> 448                 vmid = self.norm.vmin + dv / 2
    449                 fact = 1e7 if scaled_dtype == np.float64 else 1e4
    450                 newmin = vmid - dv * fact

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/numpy/ma/core.py in __add__(self, other)
   4136         if self._delegate_binop(other):
   4137             return NotImplemented
-> 4138         return add(self, other)
   4139
   4140     def __radd__(self, other):

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/numpy/ma/core.py in __call__(self, a, b, *args, **kwargs)
   1019         with np.errstate():
   1020             np.seterr(divide='ignore', invalid='ignore')
-> 1021             result = self.f(da, db, *args, **kwargs)
   1022         # Get the mask for the result
   1023         (ma, mb) = (getmask(a), getmask(b))

~/miniconda3/envs/mpl_latest/lib/python3.8/site-packages/unyt/array.py in __array_ufunc__(self, ufunc, method, *inputs, **kwargs)
   1737                                     raise UnitOperationError(ufunc, u0, u1)
   1738                         else:
-> 1739                             raise UnitOperationError(ufunc, u0, u1)
   1740                     conv, offset = u1.get_conversion_factor(u0, inp1.dtype)
   1741                     new_dtype = np.dtype("f" + str(inp1.dtype.itemsize))

UnitOperationError: The <ufunc 'add'> operator for unyt_arrays with units "g/cm**3" (dimensions "(mass)/(length)**3") and "dimensionless" (dimensions "1") is not well defined

Expected outcome
A usable figure :)

Matplotlib version

  • Operating system: MacOS
  • Matplotlib version: 3.3.0
  • Matplotlib backend (print(matplotlib.get_backend())): MacOSX
  • Python version: 3.8.3
  • Jupyter version (if applicable):
  • Other libraries: IPython 7.16.1

mpl installed via cona (Conda-forge channel)

@jklymak
Copy link
Member

jklymak commented Jul 26, 2020

Operations on the x and y axis are supported as units. Zdata is not, and never has been. It's not clear what the goal of doing so would be.

I think it's possible we should be trying to downcast to an np array here, which I assume is all you would like.

@jklymak
Copy link
Member

jklymak commented Jul 26, 2020

What happens w/ pcolormesh?

@neutrinoceros
Copy link
Contributor Author

import unyt
import numpy as np
from matplotlib import pyplot as plt

arr = unyt.unyt_array(np.random.normal(0, 1, 16**2).reshape(16, 16), "g/cm**3")
fig, ax = plt.subplots()
im = ax.pcolormesh(arr)
fig.colorbar(im)
fig.savefig("/tmp/pcolor_unyt.png")

gives
pcolor_unyt

So yeah, it doesn't make a difference with respect to simply using a raw numpy array directly, but what I want to point out is that imshow in particular is inconsistent with the other 2d functions I've tried here.

Zdata is not, and never has been. It's not clear what the goal of doing so would be.

I for one think this would be quite useful, but that's going well beyond this bug report.

@jklymak
Copy link
Member

jklymak commented Jul 26, 2020

So imshow just needs to precondition the array similarly to pcolormesh. Seems doable.

@neutrinoceros
Copy link
Contributor Author

Yes I figure the machinery is obviously there already :)
Thank you for your quick reply, I appreciate it !

@QuLogic
Copy link
Member

QuLogic commented Jul 29, 2020

imshow calls AxesImage.set_image which calls cbook.safe_masked_invalid(A, copy=True). This does subok=True, but maybe we can have it do subok=False, at least for images.

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

Successfully merging a pull request may close this issue.

3 participants