Skip to content

Colorbar with SymmetricalLogLocator : issue when handling only negative values #7202

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
egaudry opened this issue Sep 30, 2016 · 8 comments
Closed
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Milestone

Comments

@egaudry
Copy link

egaudry commented Sep 30, 2016

Tested with matplotlib 1.4.3 and 1.5.3 built from sources - python 2.7.11 - Windows and Linux (64-bit)

To reproduce this issue, choose a set of values X so that -100<X<-10, then plot a NonUniformImage and attach a colorbar with a SymmetricalLogLocator : no tick or ticklabel will be displayed on the colorbar.

@egaudry
Copy link
Author

egaudry commented Sep 30, 2016

The attached patch allows to draw ticks/ticklabels in case no decade (or only one) was selected based on vmin/vmax detected values.
There is however still an issue in the colorbar height.

Index: lib/matplotlib/colorbar.py
===================================================================
--- lib/matplotlib/colorbar.py  (revision 97805)
+++ lib/matplotlib/colorbar.py  (working copy)
@@ -592,12 +592,14 @@
         formatter.set_data_interval(*intv)

         b = np.array(locator())
-        if isinstance(locator, ticker.LogLocator):
+        if isinstance(locator, ticker.LogLocator) or isinstance(locator, ticker.SymmetricalLogLocator):
             eps = 1e-10
             b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))]
         else:
             eps = (intv[1] - intv[0]) * 1e-10
             b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)]
+        if len(b)<2:
+            b = np.array(locator())
         ticks = self._locate(b)
         formatter.set_locs(b)
         ticklabels = [formatter(t, i) for i, t in enumerate(b)]
Index: lib/matplotlib/ticker.py
===================================================================
--- lib/matplotlib/ticker.py    (revision 97805)
+++ lib/matplotlib/ticker.py    (working copy)
@@ -1722,6 +1722,8 @@
                 a_range = get_log_range(t, -vmin + 1)
             else:
                 a_range = get_log_range(-vmax, -vmin + 1)
+                if vmin<0 and vmax<0:
+                  a_range = ( a_range[0], a_range[1]+1 )
         else:
             a_range = (0, 0)

@egaudry
Copy link
Author

egaudry commented Sep 30, 2016

might be linked to #7146

@tacaswell
Copy link
Member

@egaudry Can you open a pull request with that patch?

attn @efiring

@NelleV
Copy link
Member

NelleV commented Oct 24, 2016

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
from matplotlib import ticker

X = -1 * np.random.randint(10, 90, size=(100, 100))

fig, ax = plt.subplots()
m = ax.imshow(X, norm=colors.SymLogNorm(1))
formatter = ticker.LogFormatter(10)
cb = fig.colorbar(m, format=formatter)
ax.set_title("label only base")

@NelleV NelleV added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Oct 25, 2016
@NelleV NelleV added this to the 2.0 (style change major release) milestone Oct 25, 2016
@tacaswell
Copy link
Member

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
from matplotlib import ticker

plt.ion()

X = np.random.randint(15, 90, size=(100, 100))

fig, ax = plt.subplots()
m = ax.imshow(X, norm=colors.LogNorm())
formatter = ticker.LogFormatter(10)
cb = fig.colorbar(m, format=formatter)
ax.set_title("label only base")

so

@NelleV
Copy link
Member

NelleV commented Oct 31, 2016

So… The behaviour is not the same for log and symlog Norm… minorticks appear with symlog but not log.
I'll do more tests.

@NelleV
Copy link
Member

NelleV commented Nov 5, 2016

@efiring @tacaswell @dopplershift

Following monday's discussion, I've ran some tests. By default, Log formatter behave extremely poorly with colorbars. The current behaviour is as follows

log_colorbar_default

Disabling labelOnlyBase does not change anything:
log_colorbar_disable_labelonlybase

Disabling the sublabels only feature (introduced in august) does not change anything:

log_colorbar_disable_sublabels_only

To have a behaviour that seems reasonable for colorbars we need to modify both the sublabels filtering and the labelOnlyBase:

log_colorbar_proposed_new_default

Now going to using the LogFormatter on a log scaled plot while disabling the sublabels filtering does not change anything on this example: http://matplotlib.org/devdocs/examples/scales/scales.html

With sublabels:
old_formatter

Without sublabels:
disabling_sublabels

From our discussion, I undersand that the labelOnlyBase=True is mostly use for minor ticks. I propose to change the default to have labelOnlyBase = False to remove or disable the sublabel filtering by default. I have yet to find a case where this new filtering improved the readibility of the figure, and I believe this may be due to improvements in the defaults of max number of ticks and placements of those ticks.

I have to tweak matplotlib's code to add an option to disable the sublabels, that you can find in https://github.com/NelleV/matplotlib/tree/test_7202

But here are some tests scripts I wrote:

1. Colorbars

import numpy as np

import matplotlib.pyplot as plt
from matplotlib import ticker, colors

disable_sublabel = True


X_1 = np.random.randint(10000, 90000, size=(100, 100))
X_2 = np.random.randint(10000, 1000000, size=(100, 100))

formatter = ticker.LogFormatter(
    10, disable_sublabel=disable_sublabel,
    labelOnlyBase=False)

X = [X_1, X_2]

fig, axes = plt.subplots(ncols=2, figsize=(10, 4))
for i, x in enumerate(X):
    ax = axes[i]
    m = ax.imshow(x, norm=colors.SymLogNorm(1))
    ax.set_title("Disabling sublabels & labelOnlyBase")
    cb = fig.colorbar(m, format=formatter, ax=ax, shrink=0.9, )
fig.savefig("images/log_colorbar_proposed_new_default.png")

formatter = ticker.LogFormatter(
    10, disable_sublabel=disable_sublabel,
    labelOnlyBase=True)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4))
for i, x in enumerate(X):
    ax = axes[i]
    m = ax.imshow(x, norm=colors.SymLogNorm(1))
    ax.set_title("Disable sublabel only")
    cb = fig.colorbar(m, format=formatter, ax=ax, shrink=0.9)
fig.savefig("images/log_colorbar_disable_sublabels_only.png")

formatter = ticker.LogFormatter(10, labelOnlyBase=False)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4))
for i, x in enumerate(X):
    ax = axes[i]
    m = ax.imshow(x, norm=colors.SymLogNorm(1))
    ax.set_title("Log formatter labelOnlyBase=False")
    cb = fig.colorbar(m, ax=ax, format=formatter, shrink=0.9)
fig.savefig("images/log_colorbar_disable_labelonlybase.png")

formatter = ticker.LogFormatter(10)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4))
for i, x in enumerate(X):
    ax = axes[i]
    m = ax.imshow(x, norm=colors.SymLogNorm(1))
    ax.set_title("Current Default")
    cb = fig.colorbar(m, ax=ax, format=formatter, shrink=0.9)
fig.savefig("images/log_colorbar_default.png")

2. the scales example

import numpy as np
from matplotlib import ticker
import matplotlib.pyplot as plt

disable_sublabel = True

old_formatter = ticker.LogFormatterSciNotation(
    10, labelOnlyBase=False)
formatter = ticker.LogFormatterSciNotation(
    10, disable_sublabel=True,
    labelOnlyBase=False)

np.random.seed(19680801)
# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))

for i, fmt in enumerate([old_formatter, formatter]):
    # plot with various axes scales
    fig, axs = plt.subplots(2, 2)

    # linear
    ax = axs[0, 0]
    ax.plot(x, y)
    ax.set_yscale('linear')
    ax.set_title('linear')
    ax.grid(True)

    # log
    ax = axs[0, 1]
    ax.plot(x, y)
    ax.set_yscale('log')
    ax.set_title('log')
    ax.yaxis.set_major_formatter(fmt)

    ax.grid(True)

    # symmetric log
    ax = axs[1, 1]
    ax.plot(x, y - y.mean())
    ax.set_yscale('symlog', linthreshy=0.05)
    ax.set_title('symlog')
    ax.yaxis.set_major_formatter(fmt)
    ax.grid(True)

    # logit
    ax = axs[1, 0]
    ax.plot(x, y)
    ax.set_yscale('logit')
    ax.set_title('logit')
    #ax.yaxis.set_major_formatter(formatter)
    ax.yaxis.set_minor_formatter(ticker.NullFormatter())
    ax.grid(True)

    plt.show()
    if i == 0:
        fig.savefig("images/example_old_formatter.png")
    else:
        fig.savefig("images/example_disabling_sublabels.png")

3. some random tests

import numpy as np

import matplotlib.pyplot as plt
from matplotlib import ticker, colors

formatter = ticker.LogFormatterSciNotation(
    10, disable_sublabel=True,
    labelOnlyBase=False)

X_1 = np.random.randint(10000, 90000, size=(1000,))
X_1.sort()
X_2 = np.random.randint(1e5, 1e12, size=(1000,))
X_2.sort()
X_3 = np.random.randint(1e5, 1e16, size=(1000,))
X_3.sort()

X = [X_1, X_2, X_3]

fig, ax = plt.subplots(ncols=len(X), nrows=2, figsize=(12, 6))
for i, x in enumerate(X):
    ax[0, i].plot(x)
    ax[0, i].set_yscale("symlog")

    ax[1, i].plot(x)
    ax[1, i].set_yscale("symlog")
    ax[1, i].yaxis.set_major_formatter(formatter)


ax[0, 1].set_title("Symmetrical log")

fig, ax = plt.subplots(ncols=len(X), nrows=2, figsize=(12, 6))
for i, x in enumerate(X):
    ax[0, i].plot(x)
    ax[0, i].set_yscale("log")
    ax[1, i].plot(x)
    ax[1, i].set_yscale("log")
    ax[1, i].yaxis.set_major_formatter(formatter)

ax[0, 1].set_title("Log")
l_f = ax[0, 1].yaxis.get_major_formatter()

@NelleV
Copy link
Member

NelleV commented Nov 5, 2016

Note that by default, labelOnlyBase is True for LogFormatter but False for LogFormatterSciNotation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Projects
None yet
Development

No branches or pull requests

3 participants