Search code examples
pythonmatplotlibcolorbar

Unevenly (irregularly) spaced data for colorbar with evenly spaced colors


I'm trying to create a color bar with 13 levels and use a diverging RdBu_r colormap with the central value spaced at 0. Currently, I have an evenly spaced colorbar with values descending by 8 ranging from -48 to +48

What I would like to make is a colorbar with even colored spacing, but unevenly spaced values, like this colorbar which I modified in photoshop, such that the values go from [-96.0, -72, -48, -24, -12, -6, 0, 6, 12, 24, 48, 72, 96]

My current attempt looks like this:

from cartopy import crs as ccrs;  import matplotlib.pyplot as plt

crs_new = ccrs.PlateCarree()

fig, axs = plt.subplots(subplot_kw={'projection': crs_new},figsize=(8, 6))

cmap = 'RdBu_r'  

uneven_levels = [-96.0, -72, -48, -24, -12, -6, 0, 6, 12, 24, 48, 72, 96]
vmin,vmax = -48,48
cs=plt.pcolormesh(lon,lat, data,cmap=cmap, transform=crs_new,vmin=vmin,vmax=vmax)
cbar=plt.colorbar(cs,boundaries= uneven_levels)

Which results in a colorbar with really dark ends and clearly uses linearly spaced coloring.

Using countourf and "spacing = uniform" did not work.

Using "colors.DivergingNorm(vmin=vmin, vcenter=0, vmax=vmax)" did not work.

I've tried to define my own color bar with

cmap = plt.get_cmap('RdBu_r')
colors = cmap(np.linspace(0, 1, len(uneven_levels)))

but have no idea to get the data to match up with these levels, results in the same thing as figure 3, so this also did not work.

Any help is greatly appreciated! :)


Solution

  • The reason there are a lot of dark colors is vmin, vmax = -48, 48 which forces all values below -48 to the darkest blue, and all values above 48 to the darkest red.

    To obtain a similar effect as contourf, matplotlib's from_levels_and_colors could be helpful. This generates a colormap with 12 colors, and a norm. The norm is a function that converts values between -96 and 96 to values for the colormap, as defined by the levels.

    Here is some example code:

    import numpy as np
    import seaborn as sns
    from matplotlib import pyplot as plt
    import matplotlib.colors as mcolors
    
    x = np.linspace(0, 3 * np.pi, 500)
    data = np.sin(x - x[:, np.newaxis] * 2) * 95
    uneven_levels = [-96, -72, -48, -24, -12, -6, 0, 6, 12, 24, 48, 72, 96]
    cmap_rb = plt.get_cmap('RdBu_r')
    colors = cmap_rb(np.linspace(0, 1, len(uneven_levels) - 1))
    cmap, norm = mcolors.from_levels_and_colors(uneven_levels, colors)
    
    fig, axs = plt.subplots(ncols=2, figsize=(8, 6))
    
    v = np.linspace(-96, 96, 1000)
    axs[0].plot(v, norm(v))
    
    cs = axs[1].pcolormesh(data, cmap=cmap, norm=norm)
    cbar = fig.colorbar(cs, ticks=uneven_levels)
    
    plt.show()
    

    The image on the left shows how the boundary norm maps values between -96 and 96 to their respective color in a nonlinear fashion. The image on the right show the a colorbar with the boundaries.

    example plot