Home > Software design >  How to split 1 colorbar in 8 colorbars in matplotlib?
How to split 1 colorbar in 8 colorbars in matplotlib?

Time:04-20

I'm trying to plot 8 colorbars of a colormesh plot in matplotlib using the same colormap with 8 colors. Each one of the colorbars containing 1 color of the colormap. The original code I was using:

import numpy as np
import matplotlib.pyplot as plt    
from matplotlib.colors import ListedColormap
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import FormatStrFormatter

plt.rcParams.update({
    'font.size': 8
})

def make_plot(ax, data, colormap, ticks, custom_cbar):
    
    ax.set_aspect('equal')
    divider = make_axes_locatable(ax)
    
    if custom_cbar == True:
        plot = ax.pcolormesh(data, cmap = colormap, vmin=np.min(data) - 0.5, vmax=np.max(data)   0.5)
        cax = divider.append_axes("top", size = '15%', pad = 0.05)
        cbar = plt.colorbar(plot, ax=ax, cax = cax, orientation='horizontal', ticks = ticks)
        cbar.ax.set_xticklabels(['A', 'B', 'C', 'D', 'E', 'F', 'Gh', 'KGh' ])
        cbar.ax.tick_params(size=0, labelsize=6)
        cbar.outline.set_visible(False)
    else:
        plot = ax.pcolormesh(data, cmap = colormap)
        cax = divider.append_axes("top", size = '15%', pad = 0.05)
        cbar = plt.colorbar(plot, ax=ax, cax = cax, orientation='horizontal', format = '%.3f', ticks = ticks)
        cbar.ax.tick_params(direction = 'in', labelsize = 6)
    
    cbar.ax.xaxis.set_ticks_position('top')
    ax.yaxis.set_major_formatter(FormatStrFormatter('%g'))
    ax.xaxis.set_major_formatter(FormatStrFormatter('%g'))

# ===================== Data =================== #
np.random.seed(17172721)
data_int = np.random.randint(8, size=(10,30))
ticks_int = [0,1,2,3,4,5,6,7]

data = np.random.random((10,30))
delta_data = data.max() - data.min()
ticks = [data.min(), 0.5*delta_data, data.max()]

# ===================== Figure =================== #
cm = 1/2.54
fig = plt.figure(1, figsize = (15*cm, 8*cm), dpi = 227, constrained_layout = True)
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
ax4 = fig.add_subplot(2,2,4)

# ===================== Plot =================== #
colormap = ListedColormap(['#404040', '#FFEE00', '#00DC00', '#FF8000', '#9900FF', '#007BFF', '#FF0000','#700000'])
make_plot(ax1, data_int, colormap, ticks_int, True)
make_plot(ax2, data, 'rainbow', ticks, False)
make_plot(ax3, data, 'rainbow', ticks, False)
make_plot(ax4, data, 'rainbow', ticks, False)

plt.show()

The original code produces this Figure:

enter image description here

Does anyone know how to do this and keep the alignment with all other subplots?

EDIT:

Based on JohanC comments, this implemented version of the code produces a good solution for some cases:

import numpy as np
import matplotlib.pyplot as plt    
from matplotlib.colors import ListedColormap
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import FormatStrFormatter

plt.rcParams.update({
    'font.size': 8
})

def make_plot(ax, data, colormap, ticks, custom_cbar):
    
    ax.set_aspect('equal')
    divider = make_axes_locatable(ax)
    
    if custom_cbar == True:
        plot = ax.pcolormesh(data, cmap = colormap, vmin=np.min(data) - 0.5, vmax=np.max(data)   0.5)
        cax = divider.append_axes("top", size = '15%', pad = 0.05)
        cbar = plt.colorbar(plot, ax=ax, cax = cax, orientation='horizontal', ticks = ticks, drawedges=True)
        cbar.ax.set_xticklabels(['A', 'B', 'C', 'D', 'E', 'F', 'Gh', 'KGh' ])
        cbar.ax.tick_params(size=0, labelsize=6)
        
        cbar.outline.set_edgecolor('white')
        cbar.outline.set_linewidth(2.5)

        cbar.dividers.set_color('white')
        cbar.dividers.set_linewidth(2.5)
    else:
        plot = ax.pcolormesh(data, cmap = colormap)
        cax = divider.append_axes("top", size = '15%', pad = 0.05)
        cbar = plt.colorbar(plot, ax=ax, cax = cax, orientation='horizontal', format = '%.3f', ticks = ticks)
        cbar.ax.tick_params(direction = 'in', labelsize = 6)
    
    cbar.ax.xaxis.set_ticks_position('top')
    ax.yaxis.set_major_formatter(FormatStrFormatter('%g'))
    ax.xaxis.set_major_formatter(FormatStrFormatter('%g'))

# ===================== Data =================== #
np.random.seed(17172721)
data_int = np.random.randint(8, size=(10,30))
ticks_int = [0,1,2,3,4,5,6,7]

data = np.random.random((10,30))
delta_data = data.max() - data.min()
ticks = [data.min(), 0.5*delta_data, data.max()]

# ===================== Figure =================== #
cm = 1/2.54
fig = plt.figure(1, figsize = (15*cm, 8*cm), dpi = 227, constrained_layout = True)
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
ax4 = fig.add_subplot(2,2,4)

# ===================== Plot =================== #
colormap = ListedColormap(['#404040', '#FFEE00', '#00DC00', '#FF8000', '#9900FF', '#007BFF', '#FF0000','#700000'])
make_plot(ax1, data_int, colormap, ticks_int, True)
make_plot(ax2, data, 'rainbow', ticks, False)
make_plot(ax3, data, 'rainbow', ticks, False)
make_plot(ax4, data, 'rainbow', ticks, False)

plt.show()

This modified version of the code produces this output:

enter image description here

However, in this case, it is still plotting 1 colorbar with the full colormap. The idea is to plot 8 colorbars next to each other and each one with 1 color of the colormap.

Thanks in advance!

CodePudding user response:

One could draw white vertical lines onto the colorbar:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig, ax = plt.subplots(figsize=(12, 5))
ax.set_aspect('equal')
divider = make_axes_locatable(ax)
ticklabels = ['A', 'B', 'C', 'D', 'E', 'F', 'Gh', 'KGh']
nticks = len(ticklabels)
colormap = plt.get_cmap('turbo', nticks)
data = np.random.randint(0, nticks, (8, 12))
cmesh = ax.pcolormesh(data, cmap=colormap, vmin=np.min(data) - 0.5, vmax=np.max(data)   0.5)
cax = divider.append_axes("top", size='15%', pad=0.2)
cbar = plt.colorbar(cmesh, ax=ax, cax=cax, orientation='horizontal', ticks=np.arange(nticks))
cbar.ax.set_xticklabels(['A', 'B', 'C', 'D', 'E', 'F', 'Gh', 'KGh'])
cbar.ax.tick_params(size=0, labelsize=6)
cbar.outline.set_visible(False)
for x in np.arange(nticks   1) - 0.5:
     cbar.ax.axvline(x, color='white', lw=5)
plt.tight_layout()
plt.show()

colorbar with separators

CodePudding user response:

Based on the @JohanC suggestion, I've come with a temporary solution by adding the option drawedges = True when calling the method to plot as:

cbar = plt.colorbar(plot, ax=ax, cax = cax, orientation='horizontal', ticks = ticks, drawedges=True)

This option draw dividers between colorbar levels. Then I used the following methods:

cbar.outline.set_edgecolor('white')
cbar.outline.set_linewidth(2.5)

cbar.dividers.set_color('white')
cbar.dividers.set_linewidth(2.5)

which makes the colorbar outline and dividers white, and define its width.

The result can be seen in this Image: enter image description here

Although it is not the solution I'm seeking, it is a good solution to some cases. Unfortunately my concern is yet on how to divide the colorbar utilizing the same colormap. I'll edit the question posting this solution.

  • Related