diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 8732297c3c03..444dcb1c9bbb 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -451,6 +451,12 @@ Deprecations `.Legend.draggable()` is drepecated in favor of `.Legend.set_draggable()`. ``Legend.draggable`` may be reintroduced as a property in future releases. +Colorbar for log-scaled hexbin +------------------------------ + +When using `hexbin` and plotting with a logarithmic color scale, the colorbar +ticks are now correctly log scaled. Previously the tick values were linear +scaled log(number of counts). API Changes in 2.2.0 ==================== diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 9fa6e6650b00..77cb9f0e5bdb 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4326,9 +4326,23 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, offset_position="data" ) + # Check for valid norm + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) + + # Set normalizer if bins is 'log' + if bins == 'log': + if norm is not None: + warnings.warn("Only one of 'bins' and 'norm' arguments can be " + "supplied, ignoring bins={}".format(bins)) + else: + norm = mcolors.LogNorm() + bins = None + if isinstance(norm, mcolors.LogNorm): if (accum == 0).any(): - # make sure we have not zeros + # make sure we have no zeros accum += 1 # autoscale the norm with curren accum values if it hasn't @@ -4337,10 +4351,7 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, if norm.vmin is None and norm.vmax is None: norm.autoscale(accum) - # Transform accum if needed - if bins == 'log': - accum = np.log10(accum + 1) - elif bins is not None: + if bins is not None: if not iterable(bins): minimum, maximum = min(accum), max(accum) bins -= 1 # one less edge than bins @@ -4348,9 +4359,6 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, bins = np.sort(bins) accum = bins.searchsorted(accum) - if norm is not None and not isinstance(norm, mcolors.Normalize): - raise ValueError( - "'norm' must be an instance of 'mcolors.Normalize'") collection.set_array(accum) collection.set_cmap(cmap) collection.set_norm(norm) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png b/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png index 7b3ddeed9d18..febefb870918 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png and b/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 12197bd8e55f..cea6c273a076 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -858,18 +858,18 @@ def __init__(self, x, y): @image_comparison(baseline_images=['hexbin_log'], - remove_text=True, - extensions=['png']) + extensions=['png'], style='mpl20') def test_hexbin_log(): - # Issue #1636 - np.random.seed(0) + # Issue #1636 (and also test log scaled colorbar) + np.random.seed(19680801) n = 100000 x = np.random.standard_normal(n) y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n) y = np.power(2, y * 0.5) fig, ax = plt.subplots() - ax.hexbin(x, y, yscale='log') + h = ax.hexbin(x, y, yscale='log', bins='log') + plt.colorbar(h) def test_inverted_limits():