Установите размер цветовой шкалы Matplotlib в соответствии с графиком

161

Я не могу сделать так, чтобы цветовая шкала на графиках imshow была такой же высоты, как и график, за исключением фактов использования Photoshop. Как мне получить высоту, чтобы соответствовать?Пример несоответствия размера цветовой шкалы

Эллиот
источник
Вы пробовали предложения от stackoverflow.com/questions/16702479/...
lmjohns3
@ imjohns3 Ничто в этом посте, похоже, никак не влияет на цветную полосу. Он остается одинаковым, независимо от того, что я установил. Если я установлю дробь и сжатие, размер графика изменится, а цветовая полоса останется прежней, пока мы не вернемся к тому, что у меня уже есть, тогда они перестанут что-либо делать.
Эллиот
Проверьте документы - matplotlib.org/api/colorbar_api.html - и используйте fractionили shrinkаргументы.
BoltzmannBrain

Ответы:

194

Вы можете сделать это легко с помощью matplotlib AxisDivider .

Пример со связанной страницы также работает без использования вспомогательных участков:

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

plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))

# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

plt.colorbar(im, cax=cax)

введите описание изображения здесь

bogatron
источник
1
Я не работаю на участке, так что это не применимо.
Эллиот
После долгих переделок получил его на работу. У вас много проблем с взаимодействием с subplot_adjust, вы должны получать вызовы только в правильных местах относительно друг друга.
Эллиот
11
Это немного меняет размеры графиков. У меня есть 4 в сетке 2x2, но я хочу, чтобы только два справа имели столбцы (масштаб применяется по строкам). Однако это делает их не одинакового размера. Я попытался просто не иметь вызова цветовой панели (с вызовом делителя), но, конечно, это оставляет пустую белую коробку и цифры на стороне. Как я могу сделать так, чтобы они имели одинаковый размер, не ставя столбики на всех из них?
Эллиот
@bogatron К сожалению, это не работает с проецируемыми осями
Богдан
Если вы хотите добавить заголовок с помощью plt.title, он будет отображаться поменять местами справа. Есть ли способ преодолеть это?
user2820579
225

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

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

Это также не требует разделения оси, которая может вывести график из квадрата.

skytaker
источник
4
Это может работать в некоторых случаях, но в целом это не так. Попробуйте, например, построить что-то похожее на исходный вопрос, ширина которого в два раза больше высоты.
Матиас
Опция дроби все еще работает в том случае, если вы упомянули, если масштабируется в соответствии с высотой графика. (mpl v1.4.3)
скайтакер
6
Это единственный универсальный способ сделать это. Решения с axex_grid1 не будут работать для проецируемых осей, таких как GeoAxes.
Богдан
О боже, это круто. Большое спасибо за решение
2D_
3
В ответ на комментарий @Matthias. Вы можете исправить ситуацию, когда изображение слишком широкое, используя этот трюк: im_ratio = data.shape[0]/data.shape[1] plt.colorbar(im,fraction=0.046*im_ratio, pad=0.04)где dataваше изображение.
Эйндзл
30

@bogatron уже дал ответ, предложенный документами matplotlib , который выдает правильную высоту, но создает другую проблему. Теперь ширина цветовой шкалы (а также расстояние между цветовой шкалой и графиком) изменяется в зависимости от ширины графика. Другими словами, соотношение сторон цветовой шкалы больше не фиксируется.

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

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np

aspect = 20
pad_fraction = 0.5

ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)

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

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

графическое изображение с цветовой шкалой

ОБНОВИТЬ:

Я создал блокнот IPython по этой теме , где я упаковал приведенный выше код в легко используемую функцию:

import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1

def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
    """Add a vertical color bar to an image plot."""
    divider = axes_grid1.make_axes_locatable(im.axes)
    width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
    pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
    current_ax = plt.gca()
    cax = divider.append_axes("right", size=width, pad=pad)
    plt.sca(current_ax)
    return im.axes.figure.colorbar(im, cax=cax, **kwargs)

Это можно использовать так:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)
Матиас
источник
1
Это действительно полезная маленькая функция! Одним из предупреждений является то, что он не работает, когда вы хотите добавить несколько цветовых полос, потому что они появляются друг над другом.
Дэвид Холл
22

Я ценю все ответы выше. Однако, как и некоторые ответы и комментарии указали, axes_grid1модуль может не адрес GeoAxes, тогда как регулировка fraction, pad, shrinkи другие подобные параметры могут не обязательно дать очень точный порядок, который действительно беспокоит меня. Я считаю, что предоставление colorbarсвоего axesможет быть лучшим решением для решения всех вопросов, которые были упомянуты.

Код

import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))

# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.

cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)

результат

введите описание изображения здесь

Позже я нахожу matplotlib.pyplot.colorbarофициальную документацию также даетax опцию, которая представляет собой существующие оси, которые предоставят место для цветовой шкалы. Следовательно, это полезно для нескольких подзаговоров, см. Следующее.

Код

fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)

результат

введите описание изображения здесь

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

Код

fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)

результат

введите описание изображения здесь

Фэй Яо
источник
@ HS-nebula Спасибо за ваши комментарии, и я рад, что вам понравилось. :)
Фэй Яо
Действительно удивительный ответ. Спасибо!!
эпизодян
11

Когда вы создаете colorbar попробуйте использовать параметры дроби и / или сжатия.

Из документов:

фракция 0,15; доля исходных осей для использования для цветовой шкалы

уменьшить 1,0; доля, на которую сжимается цветовая полоса

Стив Барнс
источник
1
Если я установлю shrink 1.0 и fraction на что-либо, он сжимает график, не влияя на размер цветовой шкалы, пока изменение доли не приведет к тому, что оно уже будет, и в этот момент изменение их перестанет делать что-либо.
Эллиот
Где именно вы указываете их, они должны быть параметрами для colorbar()функции или метода.
Стив Барнс
Спасибо. просто нужно указать параметр сжатия, и он работает как по волшебству!
CodingNow
11

Все вышеперечисленные решения хороши, но мне нравятся @ Steve's и @ bejota's лучше всего, поскольку они не включают в себя модные звонки и универсальны.

Под универсальным я подразумеваю, что работает с любым типом осей, включая GeoAxes. Например, если вы спроецировали оси для отображения:

projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))

вызов

cax = divider.append_axes("right", size=width, pad=pad)

потерпит неудачу с: KeyException: map_projection

Поэтому единственным универсальным способом определения размера цветовой шкалы для всех типов осей является:

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

Работайте с дробью от 0,035 до 0,046, чтобы получить лучший размер. Однако значения фракции и паддига необходимо будет отрегулировать так, чтобы они наилучшим образом подходили для вашего графика и будут различаться в зависимости от ориентации цветовой шкалы по вертикали или горизонтали.

Богдан
источник
1
Мы также можем добавить shrinkпараметр, когда fractionвместе с padне дают желаемых результатов достаточно.
Фэй Яо