Search code examples
pythonmatplotlibheatmapcolorbar

How to add positive and negative logarithmic scaled colorbars


I'm trying to create a 2D heatmap in Python using matplotlib, but I'm running into difficulty when it comes to handling both positive and negative values. My dataset contains values that range from very high to very low positive and negative values, which will be perfect to represent with a log scale, but the negative values would be a problem. So my idea was to represent them with different color scales.

Specifically, I would like to create two colorbars, one using a log scale for the positive values and another using a log scale for the absolute values of the negative. The positive values should be represented with a red pattern, while the negative values should be represented with a blue pattern.

Here's a simplified version of the code I have so far:

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

# Generate some sample data
data = np.random.randn(10, 10)
datap = np.where(data>0, data, 0)
datan = np.abs(np.where(data<0, data, 0))

# Colorbar limits
vmin = np.abs(data).min()
vmax = np.abs(data).max()

# Create the figure and axis objects
fig, ax1 = plt.subplots()

# Create the heatmap with positive values
heatmap1 = ax1.imshow(datap, cmap='Reds', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))

# Create the second axis object for the negative values
ax2 = ax1.twinx()

# Create the heatmap for the negative values
heatmap2 = ax2.imshow(datan, cmap='Blues', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))

# Add colorbars for both heatmaps
cbar1 = fig.colorbar(heatmap1, ax=ax1, pad=0.3, label='Positive Values')
cbar2 = fig.colorbar(heatmap2, ax=ax2, pad=0.1, label='Absolute Values of Negative Values')

# Show the plot
plt.show()

But the problem is that I'm not understanding how to separate the colorbars, as you can see in the result below: enter image description here

I'm not sure how to properly add the second colorbar, or maybe a better way to show this dataset.


Solution

  • You can move the red color bar to the right side of the blue bar. Just change the position of the red bar. Note that you also have to adjust the margin of the plot so both color bars can appear in the figure. Here's an example:

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.colors
    
    # Generate some sample data
    data = np.random.randn(10, 10)
    datap = np.where(data>0, data, 0)
    datan = np.abs(np.where(data<0, data, 0))
    
    # Colorbar limits
    vmin = np.abs(data).min()
    vmax = np.abs(data).max()
    
    # Create the figure and axis objects
    fig, ax1 = plt.subplots()
    
    # Create the heatmap with positive values
    heatmap1 = ax1.imshow(datap, cmap='Reds', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))
    
    # Create the second axis object for the negative values
    ax2 = ax1.twinx()
    
    # Create the heatmap for the negative values
    heatmap2 = ax2.imshow(datan, cmap='Blues', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))
    
    # Change red color bar position
    cbaxes1 = fig.add_axes([0.85, 0.1, 0.03, 0.7]) 
    
    # Add colorbars for both heatmaps
    cbar1 = fig.colorbar(heatmap1, ax=ax1, pad=0.3, label='Positive Values', cax = cbaxes1)
    cbar2 = fig.colorbar(heatmap2, ax=ax2, pad=0.1, label='Absolute Values of Negative Values')
    
    # Adjust the plot margin
    plt.subplots_adjust(left=0.1, bottom=0.1, right=0.8, top=0.8)
    # Show the plot
    plt.show()
    

    enter image description here