4

I'm struggling to keep the same color bar range through different plots.

For example, I have these visualizations:

enter image description here

enter image description here

Which are produced with this code:

def plot_contour(x_dim, y_dim, x_steps, y_steps, scalar_field, file_path):
    plt.figure()

    x, y = numpy.mgrid[-x_dim:x_dim/:x_steps*1j, -y_dim:y_dim:y_steps*1j] 
    cs = plt.contourf(x, y, scalar_field, zorder=1, extent=[-x_dim, x_dim, -y_dim, y_dim])
    plt.colorbar(cs)

    plt.savefig(file_path + '.png', dpi=Vc.dpi)
    plt.close()

I want to be able to compare both fields, so, I would like to use the same color mapping for both of them.

My first approach was to use the parameters v_min and v_max, using the min/max values of the data.

cs = plt.contourf(x, y, scalar_field, zorder=1, extent=[-x_dim, x_dim, -y_dim, y_dim], vmin=-1.00, vmax=1.05) # Manual setting to test

Then I got the same color mapping:

enter image description here enter image description here

But I also would like to have the same color bar range displayed in the plot. I tried to use

cb = plt.colorbar(cs)
cb.set_clim(vmin=-1.00, vmax=1.05)

With no success.

This complete example produces the same behavior:

import matplotlib
import numpy as numpy
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'

delta = 0.025
x = numpy.arange(-3.0, 3.0, delta)
y = numpy.arange(-2.0, 2.0, delta)
X, Y = numpy.meshgrid(x, y)

Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians

Za = 10.0 * (Z2 - Z1)
Zb = 5.0 * (Z2 - Z1)

def bounds(scalar_fields):
    """
    Get the bounds of a set of scalar_fields
    :param scalar_fields : the scalar field set
    :return: a set of normalized vector field components
    """
    max_bound = -numpy.inf
    min_bound = numpy.inf

    for scalar_field in scalar_fields:
        max_lim = numpy.max(scalar_field)
        min_lim = numpy.min(scalar_field)
        if max_lim > max_bound:
            max_bound = max_lim
        if min_lim < min_bound:
            min_bound = min_lim

    return min_bound, max_bound

def plot_contour(x_dim, y_dim, x_steps, y_steps, scalar_field, v_min, v_max, file_path):
    plt.figure()

    x, y = numpy.mgrid[-x_dim/2:x_dim/2:x_steps*1j, -y_dim/2:y_dim/2:y_steps*1j]

    cs = plt.contourf(x, y, scalar_field, zorder=1, extent=[-x_dim/2.0, x_dim/2.0, -y_dim/2.0, y_dim/2.0],
                      vmin=v_min, vmax=v_max)
    cb = plt.colorbar(cs)

    plt.savefig(file_path + '.png')
    plt.close()

v_min, v_max = bounds([Za, Zb])
x_dim = y_dim = 6

y_steps = x.shape[0]
x_steps = y.shape[0]    

plot_contour(x_dim, y_dim, x_steps, y_steps, Za, v_min, v_max, 'Za')
plot_contour(x_dim, y_dim, x_steps, y_steps, Zb, v_min, v_max, 'Zb') 

How could I do that?

Thank you in advance.

pceccon
  • 9,379
  • 26
  • 82
  • 158
  • Put the `vmin` and `vmax` on both calls? What you are doing should work. Can you reproduce this in a complete callable example (including synthetic data)? – tacaswell Sep 26 '14 at 18:46
  • Yeah, I'm putting this parameter in both calls, the function are the same. I will do that. – pceccon Sep 26 '14 at 18:49
  • and I think that the `set_clim` should go on the `cs` object, not the colorbar. – tacaswell Sep 26 '14 at 18:50

1 Answers1

7

If you want the colors in the colorbars to correspond to the same values within two contour plots, then you need to not only control the colorbar, but also control the levels in the contour plot. That is, to compare the same levels between the plots, the plots should have the same contour levels. This is easy to do. Here's an example of that plot:

enter image description here

There are two ways: 1) calculate the levels ahead of time; 2) use the levels from one plot to set the levels in the other. I'll do the second, since from this it should be clear how to do the first (using, for example, levels = numpy.linspace(v_min, vmax, 10), though, to be clear, I'm not using this here, but am letting mpl calculate the levels).

First, here I'm also using:

Za = 10.0 * (Z2 - Z1)
Zb = 6.0 * (Z2 - Z1)   # 6, rather than 5

Then, to plot:

def plot_contour(x_dim, y_dim, x_steps, y_steps, scalar_field, file_path, v_min, v_max, levels=None):
    x, y = numpy.mgrid[-x_dim/2:x_dim/2:x_steps*1j, -y_dim/2:y_dim/2:y_steps*1j]
    cs = plt.contourf(x, y, scalar_field, zorder=1, cmap=cm.jet, extent=[-x_dim/2.0, x_dim/2.0, -y_dim/2.0, y_dim/2.0], vmin=v_min, vmax=v_max, levels=levels)
    plt.colorbar(cs)
    return cs.levels

v_min, v_max = bounds([Za, Zb])

plt.figure()
plt.subplot(121)
levels = plot_contour(x_dim, y_dim, x_steps, y_steps, Za, 'Za', v_min, v_max)
plt.subplot(122)
plot_contour(x_dim, y_dim, x_steps, y_steps, Zb, 'Zb', v_min, v_max, levels=levels) 
plt.show()
tom10
  • 67,082
  • 10
  • 127
  • 137
  • 1
    So it couldn't be 5 in this example? The min, max levels of one plot should comprehend the limits of the other? I don't have such a control of the data, they are came from forecasts and, if the condition that I mention is necessary, this will not be true through my ensemble. :/ – pceccon Oct 03 '14 at 14:44
  • 1
    No, that was just an example of using one set of levels for both. You can specify the levels however you want, and you could just, say, pass in `levels=np.arange(-5,6)` to both. The main point is that if you want to have two discretized colorbars that match levels (and, of course, these levels also match the contours), then the levels themselves must match. – tom10 Oct 03 '14 at 14:54
  • Ok. I'll try it here and accept you answer. Thank you, @tom10. – pceccon Oct 03 '14 at 14:56
  • No problem, and there's no need to accept it if it doesn't basically do what you want. – tom10 Oct 03 '14 at 15:01
  • 1
    Thanks! You made my day! The most important part of the answer is that it is not enough to set `vmin`, `vmax` or the paramaeters of the colorbar, but just set the levels! – Dr_Zaszuś Dec 23 '19 at 13:29