Расположение и размер цветной панели matplotlib

Я использую quadmesh для создания простой полярной проекции. Здесь минимальный script, который производит в основном то, что я пытаюсь сделать:

from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt

def make_plot(data,fig,subplot):
    nphi,nt = data.shape
    phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
    theta_coords = np.linspace(0,np.radians(35),nt+1)

    ax = fig.add_subplot(subplot,projection='polar')
    ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
    ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  

    theta,phi = np.meshgrid(phi_coords,theta_coords)
    quadmesh = ax.pcolormesh(theta,phi,data)
    ax.grid(True)
    fig.colorbar(quadmesh,ax=ax)
    return fig,ax


a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)
fig.savefig('test.png')

Вышеупомянутый script создает график, который выглядит следующим образом:

enter image description here

Я хотел бы, чтобы цветные панели:

  • Не перекрывайте этикетку 6.
  • масштабируются так, чтобы они были примерно такой же высоты, как и график.

Есть ли какой-нибудь трюк, чтобы сделать эту работу должным образом? (Обратите внимание, что этот макет не единственный, который я буду использовать, например, я мог бы использовать макет 1x2 или макет 4x4... Кажется, должен быть какой-то способ масштабирования цветной панели до той же высоты, что и связанный сюжет...)

Ответ 1

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

plt.colorbar(im,fraction=0.046, pad=0.04)

Ответ 2

Вы можете сделать это с помощью комбинации pad, shrink и aspect kwargs:

from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt

def make_plot(data,fig,subplot):
    nphi,nt = data.shape
    phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
    theta_coords = np.linspace(0,np.radians(35),nt+1)

    ax = fig.add_subplot(subplot,projection='polar')
    ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
    ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  

    theta,phi = np.meshgrid(phi_coords,theta_coords)
    quadmesh = ax.pcolormesh(theta,phi,data)
    ax.grid(True)
    cb = fig.colorbar(quadmesh,ax=ax, shrink=.5, pad=.2, aspect=10)
    return fig,ax,cb


a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)

figure.colorbar doc

Наилучшее значение для этих параметров будет зависеть от соотношения сторон осей.

Размер осей, по-видимому, не увязывается с термоусадочной пленкой, поэтому в компоновке 1x2 имеется много пространства над и под участком, которые являются частью объекта осей, но пустыми. Размер цветовой полосы зависит от размера прямоугольника, а не от округлого размера, поэтому значения по умолчанию не работают. Вероятно, есть способ сделать термоусадочную пленку, но я не знаю, как это сделать.

Альтернативный метод заключается в том, чтобы заставить фигуру быть правильным соотношением сторон ex:

fig.set_size_inches(10, 4) # for 1x2
fig.set_size_inches(4, 10) # for 2x1

что делает квадраты подстроки квадратными, поэтому значения по умолчанию более или менее работают.

Ответ 3

Автоматическое масштабирование цветовой полосы, как известно, сложно. Попытка применить работу, описанную в конце этой страницы, не работала для меня ни с вашим примером, ни с моими попытками с использованием объекта Axes3D, Взглянув на сигнатуру вызова colorbar, аргумент ключевого слова ax представляет собой "объект, из которого пространство для новых осей цветной панели будут украдены". Это объясняет, почему цветовая полоса перекрывает 6. Итак, мы должны поддерживать двойной Axes. К сожалению, необходимость поддерживать дополнительные оси означает много ручного взлома позиции. Я объединил следующее:

#!/usr/bin/env python
from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()

N, M = 360, 71
phi_coords = np.linspace(0, np.pi*2, M+1) -np.pi/2
theta_coords = np.linspace(0, np.radians(35), N+1)
theta, phi = np.meshgrid(phi_coords, theta_coords)

# Original method
a = np.zeros((N,M)) + np.arange(N)[:,None]
ax1 = fig.add_subplot(1,2,1, projection="polar")
ax1.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
ax1.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  
quadmesh = ax1.pcolormesh(theta, phi, a)
fig.colorbar(quadmesh,ax=ax1)

# Use ``add_axes`` to make a ``cax``
b = np.random.random((N,M))
ax2 = fig.add_subplot(1,2,2, projection="polar")
ax2.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
ax2.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  
quadmesh = ax2.pcolormesh(theta, phi, b)
# <hack>
# Hack to force the drawing of the image.
print(ax2.get_position())
import tempfile
with tempfile.SpooledTemporaryFile() as fid:
    fig.savefig(fid, format="png")

# </hack>
# Now the axes has been scaled to the final size.
bbox = ax2.get_position()
print(bbox)
cax = fig.add_axes(
        [bbox.xmax*1.03, bbox.ymin, bbox.width*0.08, bbox.height]
    )
fig.colorbar(quadmesh,cax=cax)

fig.savefig('test.png')

Левое изображение совпадает с оригиналом, но изображение правой руки имеет смещение цветовой полосы от полярного графика, а высота бара соответствует полярному графику. Как вы можете видеть, мне пришлось вынудить фигуру рисовать, чтобы получить размер оси справа для участка левой руки. Если вы не нажмете перерисовать, размер цветовой полосы будет основываться на размере исходных осей, и вы получите оси того же размера, что и левый. На данный момент я не нашел способ сообщить Axes, чтобы цветная строка автоматически отслеживала основной Axes для изменения размера.