Подтвердить что ты не робот

Matplotlib 2 Подзаголовки, 1 Colorbar

Я потратил слишком много времени на изучение того, как получить две подзаголовки, чтобы разделить одну и ту же ось Y с одной цветной панелью, разделяемой между ними в Matplotlib.

Что было, так это то, что когда я вызывал функцию colorbar() в subplot1 или subplot2, она автоматически вычерчивала график таким образом, чтобы цветная панель плюс график помещалась в ограничительную рамку "subplot", две бок о бок графики должны быть двух очень разных размеров.

Чтобы обойти это, я попытался создать третий подзаговор, который я тогда взломал, чтобы не отображать ни одного сюжета с присутствующим цветом. Единственная проблема заключается в том, что теперь высоты и ширины двух участков неравномерны, и я не могу понять, как сделать это хорошо.

Вот мой код:

from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches
from matplotlib.ticker import NullFormatter

# SIS Functions
TE = 1 # Einstein radius
g1 = lambda x,y: (TE/2) * (y**2-x**2)/((x**2+y**2)**(3/2)) 
g2 = lambda x,y: -1*TE*x*y / ((x**2+y**2)**(3/2))
kappa = lambda x,y: TE / (2*np.sqrt(x**2+y**2))

coords = np.linspace(-2,2,400)
X,Y = np.meshgrid(coords,coords)
g1out = g1(X,Y)
g2out = g2(X,Y)
kappaout = kappa(X,Y)
for i in range(len(coords)):
    for j in range(len(coords)):
        if np.sqrt(coords[i]**2+coords[j]**2) <= TE:
            g1out[i][j]=0
            g2out[i][j]=0

fig = plt.figure()
fig.subplots_adjust(wspace=0,hspace=0)

# subplot number 1
ax1 = fig.add_subplot(1,2,1,aspect='equal',xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{1}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
plt.ylabel(r"y ($\theta_{E}$)",rotation='horizontal',fontsize="15")
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.imshow(g1out,extent=(-2,2,-2,2))
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
e1 = patches.Ellipse((0,0),2,2,color='white')
ax1.add_patch(e1)

# subplot number 2
ax2 = fig.add_subplot(1,2,2,sharey=ax1,xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{2}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
ax2.yaxis.set_major_formatter( NullFormatter() )
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
plt.imshow(g2out,extent=(-2,2,-2,2))
e2 = patches.Ellipse((0,0),2,2,color='white')
ax2.add_patch(e2)

# subplot for colorbar
ax3 = fig.add_subplot(1,1,1)
ax3.axis('off')
cbar = plt.colorbar(ax=ax2)

plt.show()
4b9b3361

Ответ 1

Просто поместите цветную полосу на свою ось и используйте subplots_adjust чтобы освободить место для нее.

В качестве быстрого примера:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)

plt.show()

enter image description here

Обратите внимание, что диапазон цветов будет установлен последним изображенным графиком (который дал начало im), даже если диапазон значений установлен с помощью vmin и vmax. Если другой график имеет, например, более высокое максимальное значение, точки с более высокими значениями, чем максимальное значение im будут отображаться одинаковым цветом.

Ответ 2

Вы можете упростить код Джо Кингтона, используя параметр ax в figure.colorbar() со списком осей. Из документации:

топор

Нет | объект родительских осей, из которых будет украдено пространство для новых осей цветовой шкалы. Если указан список осей, все они будут изменены, чтобы освободить место для осей цветовой шкалы.

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

fig.colorbar(im, ax=axes.ravel().tolist())

plt.show()

1

Ответ 3

Это решение не требует ручной настройки мест расположения осей или размера цветной панели, работает с многострочными и однострочными макетами и работает с tight_layout(). Он адаптирован из примера галереи , используя ImageGrid из matplotlib AxesGrid Toolbox.

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

# Set up figure and image grid
fig = plt.figure(figsize=(9.75, 3))

grid = ImageGrid(fig, 111,          # as in plt.subplot(111)
                 nrows_ncols=(1,3),
                 axes_pad=0.15,
                 share_all=True,
                 cbar_location="right",
                 cbar_mode="single",
                 cbar_size="7%",
                 cbar_pad=0.15,
                 )

# Add data to image grid
for ax in grid:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

# Colorbar
ax.cax.colorbar(im)
ax.cax.toggle_label(True)

#plt.tight_layout()    # Works, but may still require rect paramater to keep colorbar labels visible
plt.show()

сетка изображений

Ответ 4

Использование make_axes еще проще и дает лучший результат. Он также предоставляет возможности для настройки позиционирования цветной панели. Также обратите внимание на возможность subplots делиться осями x и y.

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

fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

cax,kw = mpl.colorbar.make_axes([ax for ax in axes.flat])
plt.colorbar(im, cax=cax, **kw)

plt.show()

Ответ 5

Как указывалось в других ответах, идея, как правило, состоит в том, чтобы определить оси для цветовой панели, в которой они находятся. Существуют различные способы сделать это; тот, который еще не был упомянут, - это непосредственное указание осей цветовых полос при создании подзаговора с помощью plt.subplots(). Преимущество состоит в том, что положение осей не нужно устанавливать вручную, и во всех случаях с автоматическим аспектом цветовая полоса будет точно такой же высоты, что и вспомогательные участки. Даже во многих случаях, когда используются изображения, результат будет удовлетворительным, как показано ниже.

При использовании plt.subplots() использование аргумента gridspec_kw позволяет сделать оси цветовой шкалы намного меньшими, чем другие оси.

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})

Пример:

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im  = ax.imshow(np.random.rand(11,8), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,8), vmin=0, vmax=1)
ax.set_ylabel("y label")

fig.colorbar(im, cax=cax)

plt.show()

enter image description here

Это хорошо работает, если аспект графиков автоматически масштабируется или изображения сжимаются из-за их аспекта в направлении ширины (как показано выше). Однако, если изображения шире, чем высоко, результат будет выглядеть следующим образом, что может быть нежелательным.

enter image description here

Решением для фиксации высоты цветовой шкалы к высоте подсплота было бы использование mpl_toolkits.axes_grid1.inset_locator.InsetPosition для установки осей цветовой полосы относительно осей подплота изображения.

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(7,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im  = ax.imshow(np.random.rand(11,16), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,16), vmin=0, vmax=1)
ax.set_ylabel("y label")

ip = InsetPosition(ax2, [1.05,0,0.05,1]) 
cax.set_axes_locator(ip)

fig.colorbar(im, cax=cax, ax=[ax,ax2])

plt.show()

enter image description here

Ответ 6

Как новичок, который наткнулся на этот поток, я бы хотел добавить адаптацию python-for-dummies abevieiramota​​strong > очень аккуратного ответа (потому что я на том уровне, что мне пришлось смотреть "ravel", чтобы определить, что делает их код):

import numpy as np
import matplotlib.pyplot as plt

fig, ((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(2,3)

axlist = [ax1,ax2,ax3,ax4,ax5,ax6]

first = ax1.imshow(np.random.random((10,10)), vmin=0, vmax=1)
third = ax3.imshow(np.random.random((12,12)), vmin=0, vmax=1)

fig.colorbar(first, ax=axlist)

plt.show()

Гораздо меньше питонов, гораздо легче для noobs, подобных мне, чтобы увидеть, что на самом деле происходит здесь.

Ответ 7

Решение использования списка осей с помощью abevieiramota​​strong > работает очень хорошо, пока вы не используете только одну строку изображений, как указано в комментариях. Использование разумного соотношения сторон для figsize помогает, но все еще далек от совершенства. Например:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(9.75, 3))
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

fig.colorbar(im, ax=axes.ravel().tolist())

plt.show()

1 x 3 массив изображений

Функция colorbar предоставляет параметр shrink, который является коэффициентом масштабирования для размера осей цветной панели. Это требует некоторых ручных проб и ошибок. Например:

fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.75)

1 x 3 массив изображений с уменьшенной цветной панелью

Ответ 8

Чтобы добавить к @abevieiramota превосходный ответ, вы можете получить euqivalent of sharp_layout с constrained_layout. Вы по-прежнему будете получать большие горизонтальные промежутки, если будете использовать imshow вместо pcolormesh из-за соотношения сторон 1:1, наложенного imshow.

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2, ncols=2, constrained_layout=True)
for ax in axes.flat:
    im = ax.pcolormesh(np.random.random((10,10)), vmin=0, vmax=1)

fig.colorbar(im, ax=axes.flat)
plt.show()

enter image description here

Ответ 9

Я заметил, что почти каждое опубликованное решение включало ax.imshow(im,...) и не нормализовало цвета, отображаемые в палитре цветов для нескольких подфигур. im картографический берутся из последней инстанции, но что, если значения множественного im разные? (Я предполагаю, что эти сопоставляемые объекты обрабатываются так же, как обрабатываются наборы контуров и наборы поверхностей.) У меня есть пример с использованием трехмерного графика поверхности ниже, который создает две цветовые полосы для субплота 2x2 (по одной цветовой полосе на одну строку).). Хотя вопрос явно требует другой договоренности, я думаю, что пример помогает прояснить некоторые вещи. Я еще не нашел способ сделать это с помощью plt.subplots(...) к сожалению, из-за трехмерных осей.

Example Plot

Если бы я только мог лучше расположить цветовые полосы... (Возможно, есть гораздо лучший способ сделать это, но, по крайней мере, это не должно быть слишком сложным).

import matplotlib
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

cmap = 'plasma'
ncontours = 5

def get_data(row, col):
    """ get X, Y, Z, and plot number of subplot
        Z > 0 for top row, Z < 0 for bottom row """
    if row == 0:
        x = np.linspace(1, 10, 10, dtype=int)
        X, Y = np.meshgrid(x, x)
        Z = np.sqrt(X**2 + Y**2)
        if col == 0:
            pnum = 1
        else:
            pnum = 2
    elif row == 1:
        x = np.linspace(1, 10, 10, dtype=int)
        X, Y = np.meshgrid(x, x)
        Z = -np.sqrt(X**2 + Y**2)
        if col == 0:
            pnum = 3
        else:
            pnum = 4
    print("\nPNUM: {}, Zmin = {}, Zmax = {}\n".format(pnum, np.min(Z), np.max(Z)))
    return X, Y, Z, pnum

fig = plt.figure()
nrows, ncols = 2, 2
zz = []
axes = []
for row in range(nrows):
    for col in range(ncols):
        X, Y, Z, pnum = get_data(row, col)
        ax = fig.add_subplot(nrows, ncols, pnum, projection='3d')
        ax.set_title('row = {}, col = {}'.format(row, col))
        fhandle = ax.plot_surface(X, Y, Z, cmap=cmap)
        zz.append(Z)
        axes.append(ax)

## get full range of Z data as flat list for top and bottom rows
zz_top = zz[0].reshape(-1).tolist() + zz[1].reshape(-1).tolist()
zz_btm = zz[2].reshape(-1).tolist() + zz[3].reshape(-1).tolist()
## get top and bottom axes
ax_top = [axes[0], axes[1]]
ax_btm = [axes[2], axes[3]]
## normalize colors to minimum and maximum values of dataset
norm_top = matplotlib.colors.Normalize(vmin=min(zz_top), vmax=max(zz_top))
norm_btm = matplotlib.colors.Normalize(vmin=min(zz_btm), vmax=max(zz_btm))
cmap = cm.get_cmap(cmap, ncontours) # number of colors on colorbar
mtop = cm.ScalarMappable(cmap=cmap, norm=norm_top)
mbtm = cm.ScalarMappable(cmap=cmap, norm=norm_btm)
for m in (mtop, mbtm):
    m.set_array([])

# ## create cax to draw colorbar in
# cax_top = fig.add_axes([0.9, 0.55, 0.05, 0.4])
# cax_btm = fig.add_axes([0.9, 0.05, 0.05, 0.4])
cbar_top = fig.colorbar(mtop, ax=ax_top, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_top)
cbar_top.set_ticks(np.linspace(min(zz_top), max(zz_top), ncontours))
cbar_btm = fig.colorbar(mbtm, ax=ax_btm, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_btm)
cbar_btm.set_ticks(np.linspace(min(zz_btm), max(zz_btm), ncontours))

plt.show()
plt.close(fig)
## orientation of colorbar = 'horizontal' if done by column