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

Skip to content

Datashader artist and colorbars regression #1069

@maximlt

Description

@maximlt

Bug found in the interactivity page of the getting started guide, of the dev site.

To reproduce:

import pandas as pd
import numpy as np
import datashader as ds
import datashader.transfer_functions as tf
from collections import OrderedDict as odict
import matplotlib.pyplot as plt
from datashader.mpl_ext import dsshow, alpha_colormap
from mpl_toolkits.axes_grid1 import ImageGrid


num=100000
np.random.seed(1)

dists = {cat: pd.DataFrame(odict([('x',np.random.normal(x,s,num)), 
                                  ('y',np.random.normal(y,s,num)), 
                                  ('val',val), 
                                  ('cat',cat)]))      
         for x,  y,  s,  val, cat in 
         [(  2,  2, 0.03, 10, "d1"), 
          (  2, -2, 0.10, 20, "d2"), 
          ( -2, -2, 0.50, 30, "d3"), 
          ( -2,  2, 1.00, 40, "d4"), 
          (  0,  0, 3.00, 50, "d5")] }

df = pd.concat(dists,ignore_index=True)
df["cat"]=df["cat"].astype("category")

%matplotlib inline

fig = plt.figure(figsize=(9, 9))

# Here, we create a grid of axes using ImageGrid
# https://matplotlib.org/3.1.0/gallery/axes_grid1/demo_axes_grid.html 
grid = ImageGrid(fig, 111, nrows_ncols=(2, 2), axes_pad=0.5, share_all=True,
                 cbar_location="right", cbar_mode="each", cbar_size="5%", cbar_pad="2%")

artist0 = dsshow(df, ds.Point('x', 'y'), ds.count(), vmax=1000, aspect='equal', ax=grid[0])

plt.colorbar(artist0, cax=grid.cbar_axes[0]);
grid[0].set_title('Point density (linear scale, clipped)');

artist1 = dsshow(df, ds.Point('x', 'y'), ds.mean('val'), cmap='inferno', aspect='equal', ax=grid[1])
plt.colorbar(artist1, cax=grid.cbar_axes[1]);
grid[1].set_title('Mean point value (linear scale)');

dsblue=['lightblue', 'darkblue']
artist2 = dsshow(df, ds.Point('x', 'y'), ds.count(), norm='log', cmap=dsblue, aspect='equal', ax=grid[2])
plt.colorbar(artist2, cax=grid.cbar_axes[2]);
grid[2].set_title('Point density (log scale)');

artist3 = dsshow(df, ds.Point('x', 'y'), norm='log', cmap=alpha_colormap('#ffffff', 40, 255), ax=grid[3])
grid[3].set_facecolor('#000000');
grid.cbar_axes[3].set_visible(False);
grid[3].set_title('Point density (log, alpha-based colormap)');
Traceback
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/IPython/core/formatters.py:339, in BaseFormatter.__call__(self, obj)
    337     pass
    338 else:
--> 339     return printer(obj)
    340 # Finally look for special method names
    341 method = get_real_method(obj, self.print_method)

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/IPython/core/pylabtools.py:151, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
    148     from matplotlib.backend_bases import FigureCanvasBase
    149     FigureCanvasBase(fig)
--> 151 fig.canvas.print_figure(bytes_io, **kw)
    152 data = bytes_io.getvalue()
    153 if fmt == 'svg':

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/backend_bases.py:2295, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2289     renderer = _get_renderer(
   2290         self.figure,
   2291         functools.partial(
   2292             print_method, orientation=orientation)
   2293     )
   2294     with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2295         self.figure.draw(renderer)
   2297 if bbox_inches:
   2298     if bbox_inches == "tight":

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/artist.py:73, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     71 @wraps(draw)
     72 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 73     result = draw(artist, renderer, *args, **kwargs)
     74     if renderer._rasterizing:
     75         renderer.stop_rasterizing()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/figure.py:2810, in Figure.draw(self, renderer)
   2807         # ValueError can occur when resizing a window.
   2809 self.patch.draw(renderer)
-> 2810 mimage._draw_list_compositing_images(
   2811     renderer, self, artists, self.suppressComposite)
   2813 for sfig in self.subfigs:
   2814     sfig.draw(renderer)

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/image.py:132, 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
    135     image_group = []

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/axes/_base.py:3082, in _AxesBase.draw(self, renderer)
   3079         a.draw(renderer)
   3080     renderer.stop_rasterizing()
-> 3082 mimage._draw_list_compositing_images(
   3083     renderer, self, artists, self.figure.suppressComposite)
   3085 renderer.close_group('axes')
   3086 self.stale = False

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/image.py:132, 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
    135     image_group = []

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/image.py:646, in _ImageBase.draw(self, renderer, *args, **kwargs)
    644         renderer.draw_image(gc, l, b, im, trans)
    645 else:
--> 646     im, l, b, trans = self.make_image(
    647         renderer, renderer.get_image_magnification())
    648     if im is not None:
    649         renderer.draw_image(gc, l, b, im)

File ~/work/dev/datashader/datashader/mpl_ext.py:288, in DSArtist.make_image(self, renderer, magnification, unsampled)
    285 print('3', self.norm, self.norm.vmin, self.norm.vmax)
    287 # Normalize and color to make an RGBA array
--> 288 rgba = self.shade(binned)
    289 if self.shade_hook is not None:
    290     img = to_ds_image(binned, rgba)

File ~/work/dev/datashader/datashader/mpl_ext.py:412, in ScalarDSArtist.shade(self, binned)
    410 print('_vmin,_vmax', self._vmin, self._vmax)
    411 # if self._vmin is not None:
--> 412 self.norm.vmin = self._vmin
    413 # if self._vmax is not None:
    414 self.norm.vmax = self._vmax

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colors.py:1150, in Normalize.vmin(self, value)
   1148 if value != self._vmin:
   1149     self._vmin = value
-> 1150     self._changed()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colors.py:1178, in Normalize._changed(self)
   1173 def _changed(self):
   1174     """
   1175     Call this whenever the norm is changed to notify all the
   1176     callback listeners to the 'changed' signal.
   1177     """
-> 1178     self.callbacks.process('changed')

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:292, in CallbackRegistry.process(self, s, *args, **kwargs)
    290 except Exception as exc:
    291     if self.exception_handler is not None:
--> 292         self.exception_handler(exc)
    293     else:
    294         raise

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:96, in _exception_printer(exc)
     94 def _exception_printer(exc):
     95     if _get_running_interactive_framework() in ["headless", None]:
---> 96         raise exc
     97     else:
     98         traceback.print_exc()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:287, in CallbackRegistry.process(self, s, *args, **kwargs)
    285 if func is not None:
    286     try:
--> 287         func(*args, **kwargs)
    288     # this does not capture KeyboardInterrupt, SystemExit,
    289     # and GeneratorExit
    290     except Exception as exc:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/image.py:308, in _ImageBase.changed(self)
    306 self._imcache = None
    307 self._rgbacache = None
--> 308 cm.ScalarMappable.changed(self)

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cm.py:618, in ScalarMappable.changed(self)
    613 def changed(self):
    614     """
    615     Call this whenever the mappable is changed to notify all the
    616     callbackSM listeners to the 'changed' signal.
    617     """
--> 618     self.callbacks.process('changed', self)
    619     self.stale = True

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:292, in CallbackRegistry.process(self, s, *args, **kwargs)
    290 except Exception as exc:
    291     if self.exception_handler is not None:
--> 292         self.exception_handler(exc)
    293     else:
    294         raise

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:96, in _exception_printer(exc)
     94 def _exception_printer(exc):
     95     if _get_running_interactive_framework() in ["headless", None]:
---> 96         raise exc
     97     else:
     98         traceback.print_exc()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:287, in CallbackRegistry.process(self, s, *args, **kwargs)
    285 if func is not None:
    286     try:
--> 287         func(*args, **kwargs)
    288     # this does not capture KeyboardInterrupt, SystemExit,
    289     # and GeneratorExit
    290     except Exception as exc:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:547, in Colorbar.update_normal(self, mappable)
    544     self.norm = mappable.norm
    545     self._reset_locator_formatter_scale()
--> 547 self.draw_all()
    548 if isinstance(self.mappable, contour.ContourSet):
    549     CS = self.mappable

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:574, in Colorbar.draw_all(self)
    568 self._short_axis().set_ticks([], minor=True)
    570 # Set self._boundaries and self._values, including extensions.
    571 # self._boundaries are the edges of each square of color, and
    572 # self._values are the value to map into the norm to get the
    573 # color:
--> 574 self._process_values()
    575 # Set self.vmin and self.vmax to first and last boundary, excluding
    576 # extensions:
    577 self.vmin, self.vmax = self._boundaries[self._inside][[0, -1]]

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:1111, in Colorbar._process_values(self)
   1109 # transform from 0-1 to vmin-vmax:
   1110 if not self.norm.scaled():
-> 1111     self.norm.vmin = 0
   1112     self.norm.vmax = 1
   1113 self.norm.vmin, self.norm.vmax = mtransforms.nonsingular(
   1114     self.norm.vmin, self.norm.vmax, expander=0.1)

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colors.py:1150, in Normalize.vmin(self, value)
   1148 if value != self._vmin:
   1149     self._vmin = value
-> 1150     self._changed()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colors.py:1178, in Normalize._changed(self)
   1173 def _changed(self):
   1174     """
   1175     Call this whenever the norm is changed to notify all the
   1176     callback listeners to the 'changed' signal.
   1177     """
-> 1178     self.callbacks.process('changed')

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:292, in CallbackRegistry.process(self, s, *args, **kwargs)
    290 except Exception as exc:
    291     if self.exception_handler is not None:
--> 292         self.exception_handler(exc)
    293     else:
    294         raise

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:96, in _exception_printer(exc)
     94 def _exception_printer(exc):
     95     if _get_running_interactive_framework() in ["headless", None]:
---> 96         raise exc
     97     else:
     98         traceback.print_exc()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:287, in CallbackRegistry.process(self, s, *args, **kwargs)
    285 if func is not None:
    286     try:
--> 287         func(*args, **kwargs)
    288     # this does not capture KeyboardInterrupt, SystemExit,
    289     # and GeneratorExit
    290     except Exception as exc:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/image.py:308, in _ImageBase.changed(self)
    306 self._imcache = None
    307 self._rgbacache = None
--> 308 cm.ScalarMappable.changed(self)

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cm.py:618, in ScalarMappable.changed(self)
    613 def changed(self):
    614     """
    615     Call this whenever the mappable is changed to notify all the
    616     callbackSM listeners to the 'changed' signal.
    617     """
--> 618     self.callbacks.process('changed', self)
    619     self.stale = True

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:292, in CallbackRegistry.process(self, s, *args, **kwargs)
    290 except Exception as exc:
    291     if self.exception_handler is not None:
--> 292         self.exception_handler(exc)
    293     else:
    294         raise

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:96, in _exception_printer(exc)
     94 def _exception_printer(exc):
     95     if _get_running_interactive_framework() in ["headless", None]:
---> 96         raise exc
     97     else:
     98         traceback.print_exc()

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:287, in CallbackRegistry.process(self, s, *args, **kwargs)
    285 if func is not None:
    286     try:
--> 287         func(*args, **kwargs)
    288     # this does not capture KeyboardInterrupt, SystemExit,
    289     # and GeneratorExit
    290     except Exception as exc:

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:547, in Colorbar.update_normal(self, mappable)
    544     self.norm = mappable.norm
    545     self._reset_locator_formatter_scale()
--> 547 self.draw_all()
    548 if isinstance(self.mappable, contour.ContourSet):
    549     CS = self.mappable

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:574, in Colorbar.draw_all(self)
    568 self._short_axis().set_ticks([], minor=True)
    570 # Set self._boundaries and self._values, including extensions.
    571 # self._boundaries are the edges of each square of color, and
    572 # self._values are the value to map into the norm to get the
    573 # color:
--> 574 self._process_values()
    575 # Set self.vmin and self.vmax to first and last boundary, excluding
    576 # extensions:
    577 self.vmin, self.vmax = self._boundaries[self._inside][[0, -1]]

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colorbar.py:1117, in Colorbar._process_values(self)
   1113 self.norm.vmin, self.norm.vmax = mtransforms.nonsingular(
   1114     self.norm.vmin, self.norm.vmax, expander=0.1)
   1115 if (not isinstance(self.norm, colors.BoundaryNorm) and
   1116         (self.boundaries is None)):
-> 1117     b = self.norm.inverse(b)
   1119 self._boundaries = np.asarray(b, dtype=float)
   1120 self._values = 0.5 * (self._boundaries[:-1] + self._boundaries[1:])

File ~/miniconda3/envs/datashader-docs38/lib/python3.8/site-packages/matplotlib/colors.py:1554, in make_norm_from_scale.<locals>.Norm.inverse(self, value)
   1552 t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax])
   1553 if not np.isfinite([t_vmin, t_vmax]).all():
-> 1554     raise ValueError("Invalid vmin or vmax")
   1555 value, is_scalar = self.process_value(value)
   1556 rescaled = value * (t_vmax - t_vmin)

ValueError: Invalid vmin or vmax

<Figure size 648x648 with 8 Axes>

Commenting plt.colorbar(artist2, cax=grid.cbar_axes[2]); the code actually runs without eror, but the output images look different from the ones that are on the current site:

  • Currently on the 0.13.0 site:
    image

  • After running this code and commenting the line:
    image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions