Я создал три графика, используя:
fig, ax = plt.subplots(1,3,figsize=(12,4))
X,Y=np.meshgrid(lon,lat)
P1=ax[0].contourf(X,Y,A)
ax[0].set_title('Plot A')
P2=ax[1].contourf(X,Y,B)
ax[1].set_title('Plot B')
P3=ax[2].contourf(X,Y,C)
ax[2].set_title('Plot C')
Как добавить одну цветовую полосу для всех подграфиков?
Возможно, вы захотите взглянуть на ImageGrid Matplotlib:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable
x, y = np.mgrid[-2:2:100j, -2:2:100j]
z1 = np.sin(x**2 + y**2)
z2 = np.cos(x**2 + y**2)
cmap = "inferno"
fig = plt.figure()
grid = ImageGrid(
fig, 111, # similar to subplot(111)
nrows_ncols=(1, 2), # creates 1x2 grid of Axes
axes_pad=0.2, # pad between Axes in inch.
cbar_mode = "single"
)
grid[0].contourf(x, y, z1, cmap=cmap)
grid[1].contourf(x, y, z2, cmap=cmap)
norm = Normalize(vmin=min(z1.min(), z2.min()), vmax=max(z1.max(), z2.max()))
cb2 = grid[1].cax.colorbar(
ScalarMappable(norm=norm, cmap=cmap),
label = "z value"
)
plt.show()
Сначала вам нужно использовать одну и ту же цветовую шкалу для всех заполненных контурных графиков, и это можно решить с помощью plt.Normalize(vmin, vmax, ...)
, который возвращает линейное преобразование из интервала (vmin, vmax) в (0, 1), и используя одно и то же преобразование для всех графиков.
Вы можете указать, что plt.colorbar крадет пространство из осей или из итерации осей, синтаксис — colorbar(..., ax=ax)
или colorbar(..., ax=axs)
, и это замечание касается того, где размещается цветная полоса и как сжимаются оси фигуры.
Последняя проблема заключается в том, что вы хотите поместить внутри цветовой панели. Обычно вы используете Scalar Mappable, который возвращается из вашего contourf
, но здесь у вас есть три разных графика... поэтому вы можете построить специальный SM, используя matplotlib.cm .ScalarMappable, определяющий функцию нормализации (ту, которую вы использовали во всех contourf
) и, возможно, цветовую карту, если вы не использовали на графиках функцию по умолчанию.
Вы можете увидеть все вышеперечисленное в коде ниже.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import numpy as np
x, y = np.meshgrid(range(21), range(16))
z0 = x/10+y/5 # max z0 = 5
z1 = z0 + x/4 # max z1 = 10
z2 = x/10*y/5 # max z2 = 6
zmin = min(z.min() for z in (z0, z1, z2))
zmax = max(z.max() for z in (z0, z1, z2))
norm = plt.Normalize(zmin, zmax)
fig, axs = plt.subplots(1,3,figsize=(10,3), layout='constrained')
axs[0].contourf(x, y, z0, norm=norm, cmap='plasma')
axs[1].contourf(x, y, z1, norm=norm, cmap='plasma')
axs[2].contourf(x, y, z2, norm=norm, cmap='plasma')
plt.colorbar(ScalarMappable(norm=norm, cmap='plasma'), ax=axs)
Каждый contourf
возвращает ScalarMappable, поэтому возникает соблазн использовать один из них для создания экземпляра цветовой панели, но в этом случае диапазон цветовой панели изменяется от min(z_i)
до max(z_i)
, а не то, что нам нужно. Например, используя ax[2].contourf(x, y, z2, ...)
в качестве нашего ScalarMappable, мы имеем
Также обратите внимание, что возвращаемое значение contourf
является масштабируемым, поэтому вы можете просто выбрать один из этих трех вместо создания дополнительного для цветовой панели.
@RuthC Я упоминал, что contourf
возвращает Scalar Mappable, который обычно вы используете для создания цветовой панели. По вашему комментарию я попробовал эквивалент plt.colorbar(ax[2].contourf(...))
, но цветовая полоса менялась от 0 до 6,4, что является максимальным значением z2
, поэтому мое интуитивное ощущение, что необходим новый SM, подтвердилось.
@RuthC Я отредактировал свой ответ, чтобы ответить на ваш комментарий, спасибо.
Ах да, вы правы. Я предполагал, что диапазон colorbar
будет определяться нормой. Я обычно прохожу явные уровни, а не норму, и в этом случае все работает так, как я ожидал. Я опубликую дополнительный ответ с этим.
Адаптируя ответ @gboffi, мы также можем передавать явные уровни, чтобы цвета выравнивались между тремя графиками и их цветовой панелью. Таким образом, мы можем просто использовать скалярапапабель, возвращаемый из contourf
. Также обратите внимание, что мы видим границы уровней на цветовой панели вместо плавного градиента, показанного на первой цветовой панели @gboffi.
import matplotlib.pyplot as plt
import numpy as np
x, y = np.meshgrid(range(21), range(16))
z0 = x/10+y/5 # max z0 = 5
z1 = z0 + x/4 # max z1 = 10
z2 = x/10*y/5 # max z2 = 6
zmin = min(z.min() for z in (z0, z1, z2))
zmax = max(z.max() for z in (z0, z1, z2))
levels = np.linspace(zmin,zmax,11)
fig, axs = plt.subplots(1, 3, figsize=(10,3), layout='constrained')
axs[0].contourf(x, y, z0, levels=levels, cmap='plasma')
axs[1].contourf(x, y, z1, levels=levels, cmap='plasma')
cf = axs[2].contourf(x, y, z2, levels=levels, cmap='plasma')
plt.colorbar(cf, ax=axs)
plt.show()
Это правильный ответ. Принятый ответ слегка обескураживает.
Я поддерживаю комментарий Джоди.
Я вижу, что вы уже приняли ответ, используя
ImageGrid
, но вы также можете просто передать свой массив осей изsubplots
в вызовcolorbar
: matplotlib.org/stable/gallery/images_contours_and_fields/…