Home > database >  PyPlot: Rescale Gridspec To Include Colorbar
PyPlot: Rescale Gridspec To Include Colorbar

Time:06-22

I am trying to add a GridSpec subplot to a 2D histogram in PyPlot. I want the histogram to have a colorbar. However, when I add the colorbar, the x-axes no longer match up.

How can I make the axes match?

An example of the problem I am having is replicated below. Because of the colorbar, the x-axes no longer match:

import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from scipy.stats import binned_statistic, sem
import numpy as np 

mean = [0, 0]
cov = [[1, 0.5], [0.5, 1]]  

x, y = np.random.multivariate_normal(mean, cov, 5000).T


fig=plt.figure(figsize=(10,10))
gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
ax0 = plt.subplot(gs[0])  
h = ax.hist2d(x, 
          y, 
          density=True, 
          cmap="plasma",
          norm=LogNorm(), 
          bins=100)

x_min = -4
x_max = 4
x_rng = np.linspace(x_min, x_max, 100)



h = ax0.hist2d(x, 
          y, 
          density=True,
          cmap="plasma", 
          norm=LogNorm(), 
          bins=50)

ax0.plot(x_rng, x_rng, color="purple", linestyle="--", lw=3, label="1:1")
ax0.set_ylabel("y", fontsize=20)
ax0.set_ylim([x_min-0.5, x_max 0.5])
ax0.set_xlim([x_min-0.5, x_max 0.5])
cb = plt.colorbar(h[3],ax=ax0, pad = .015, aspect=50)
cb.set_label('Probability Density [unit$^{-2}$]',size=20)
cb.ax.tick_params(labelsize=20)

ax0.tick_params(axis="y", labelsize=20)
ax0.legend(fontsize=15)

ax1 = plt.subplot(gs[1], sharex = ax, aspect="auto")



y_mean, bins, _ = binned_statistic(x,
                             y,
                            statistic=np.mean,
                            bins = x_rng)


y_sem, bins, _ = binned_statistic(x,
                            y,
                            statistic=sem,
                            bins = x_rng)


x_rng_bcs =  (bins[:-1]   bins[1:])/2. 

ax1.errorbar(x_rng_bcs, y_mean - x_rng_bcs, yerr = y_sem, color="purple", fmt="o")
ax1.errorbar(x_rng_bcs, np.zeros_like(x_rng_bcs), color="purple", linestyle="--")
ax1.tick_params(axis="x", labelsize=20, rotation=45)
ax1.tick_params(axis="y", labelsize=20, rotation=45)
ax1.set_ylabel("$\Delta y$", fontsize=20)
ax1.set_xlabel("x", fontsize=20)
fig.tight_layout()
fig.subplots_adjust(hspace=.0)
fig.show()

Which yields:

enter image description here

CodePudding user response:

One option is to make a separate set of axes for you colorbar and use width_ratios to make it the right size.

E.g. replace

gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])

with

gs = gridspec.GridSpec(2, 2, height_ratios=[2, 1], width_ratios=[9, 1])

and then when you plot the colorbar pass the required axes as the cax keyword

ax_cb = plt.subplot(gs[0, 1])
cb = plt.colorbar(h[3], cax=ax_cb, pad = .015, aspect=50)

Doing this will give the following plot, and you can adjust the width_ratios to get the desired colorbar width.

enter image description here

Full code below

import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from scipy.stats import binned_statistic, sem
import numpy as np

from matplotlib import gridspec

mean = [0, 0]
cov = [[1, 0.5], [0.5, 1]]

x, y = np.random.multivariate_normal(mean, cov, 5000).T

fig = plt.figure(figsize=(10, 10))
gs = gridspec.GridSpec(2, 2, height_ratios=[2, 1], width_ratios=[9, 1])
ax0 = plt.subplot(gs[0, 0])
h = ax0.hist2d(x, y, density=True, cmap="plasma", norm=LogNorm(), bins=100)

x_min = -4
x_max = 4
x_rng = np.linspace(x_min, x_max, 100)


h = ax0.hist2d(x, y, density=True, cmap="plasma", norm=LogNorm(), bins=50)

ax0.plot(x_rng, x_rng, color="purple", linestyle="--", lw=3, label="1:1")
ax0.set_ylabel("y", fontsize=20)
ax0.set_ylim([x_min - 0.5, x_max   0.5])
ax0.set_xlim([x_min - 0.5, x_max   0.5])

ax_cb = plt.subplot(gs[0, 1])
cb = plt.colorbar(h[3], cax=ax_cb, pad=0.015, aspect=50)
cb.set_label("Probability Density [unit$^{-2}$]", size=20)
cb.ax.tick_params(labelsize=20)

ax0.tick_params(axis="y", labelsize=20)
ax0.legend(fontsize=15)

ax1 = plt.subplot(gs[1, 0], sharex=ax0, aspect="auto")


y_mean, bins, _ = binned_statistic(x, y, statistic=np.mean, bins=x_rng)


y_sem, bins, _ = binned_statistic(x, y, statistic=sem, bins=x_rng)


x_rng_bcs = (bins[:-1]   bins[1:]) / 2.0

ax1.errorbar(x_rng_bcs, y_mean - x_rng_bcs, yerr=y_sem, color="purple", fmt="o")
ax1.errorbar(x_rng_bcs, np.zeros_like(x_rng_bcs), color="purple", linestyle="--")
ax1.tick_params(axis="x", labelsize=20, rotation=45)
ax1.tick_params(axis="y", labelsize=20, rotation=45)
ax1.set_ylabel("$\Delta y$", fontsize=20)
ax1.set_xlabel("x", fontsize=20)
fig.tight_layout()
fig.subplots_adjust(hspace=0.0)
plt.show()

CodePudding user response:

This is what constrained_layout is supposed to do:

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

mean = [0, 0]
cov = [[1, 0.5], [0.5, 1]]
x, y = np.random.multivariate_normal(mean, cov, 5000).T

fig, axs =plt.subplots(2, 1, gridspec_kw={'height_ratios':[2, 1]},
                       sharex=True, constrained_layout=True)
ax = axs[0]
h = ax.hist2d(x, y, density=True, norm=LogNorm(), bins=100)
fig.colorbar(h[3], ax=ax)

x_rng = np.linspace(-4, 4, 100)

ax = axs[1]
ax.plot(x_rng, np.random.randn(len(x_rng)))

plt.show()

enter image description here

  • Related