Одна цветовая полоса для нескольких контурных графиков

Я создал три графика, используя:

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, но вы также можете просто передать свой массив осей из subplots в вызов colorbar: matplotlib.org/stable/gallery/images_contours_and_fields/…

RuthC 31.08.2024 18:06
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
58
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Возможно, вы захотите взглянуть на 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 01.09.2024 14:45

@RuthC Я упоминал, что contourf возвращает Scalar Mappable, который обычно вы используете для создания цветовой панели. По вашему комментарию я попробовал эквивалент plt.colorbar(ax[2].contourf(...)), но цветовая полоса менялась от 0 до 6,4, что является максимальным значением z2, поэтому мое интуитивное ощущение, что необходим новый SM, подтвердилось.

gboffi 01.09.2024 14:58

@RuthC Я отредактировал свой ответ, чтобы ответить на ваш комментарий, спасибо.

gboffi 01.09.2024 15:09

Ах да, вы правы. Я предполагал, что диапазон colorbar будет определяться нормой. Я обычно прохожу явные уровни, а не норму, и в этом случае все работает так, как я ожидал. Я опубликую дополнительный ответ с этим.

RuthC 01.09.2024 15:15

Адаптируя ответ @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()

Это правильный ответ. Принятый ответ слегка обескураживает.

Jody Klymak 01.09.2024 16:45

Я поддерживаю комментарий Джоди.

gboffi 01.09.2024 17:25

Другие вопросы по теме