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

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

11 ответов

Решение

Вы можете сделать это легко с помощью 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)

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

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

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

Я ценю все ответы выше. Однако, как указывалось в некоторых ответах и ​​комментариях,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)

Результат

@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)

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

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

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

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

Все вышеперечисленные решения хороши, но мне нравятся @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, чтобы получить лучший размер. Тем не менее, значения для дроби и паддига должны быть скорректированы, чтобы наилучшим образом соответствовать вашему графику, и будут отличаться в зависимости от того, является ли ориентация цветовой шкалы вертикальной или горизонтальной.

Альтернативой является

      shrink=0.7, aspect=20*0.7

shrink масштабирует высоту и ширину, но aspectаргумент восстанавливает исходную ширину. Соотношение сторон по умолчанию - 20. 0.7 определяется эмпирически.

Недавно я столкнулся с этой проблемой, я использовалax.twinx()чтобы решить это. Например:

      from matplotlib import pyplot as plt

# Some other code you've written
...

# Your data generation goes here
xdata = ...
ydata = ...
colordata = function(xdata, ydata)

# Your plotting stuff begins here
fig, ax = plt.subplots(1)
im = ax.scatterplot(xdata, ydata, c=colordata)

# Create a new axis which will be the parent for the colour bar
# Note that this solution is independent of the 'fig' object
ax2 = ax.twinx()
ax2.tick_params(which="both", right=False, labelright=False)

# Add the colour bar itself
plt.colorbar(im, ax=ax2)

# More of your code
...

plt.show()

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

axes_grid1.axes_divider— это предписанный метод для этой задачи (в matplotlib даже есть демо-версия ), но добавление цветовой панели уменьшает изображение. Если вы хотите сохранить исходный размер изображения, то следующий способ предлагает один из способов (на основе ответа Фей Яо ).

      data = [(1,2,3,4,5),(4,5,6,7,8),(7,8,9,10,11)]

im = plt.imshow(data, cmap='RdBu')
l, b, w, h = plt.gca().get_position().bounds
cax = plt.gcf().add_axes([l + w + 0.03, b, 0.03, h])
plt.colorbar(im, cax=cax)

Удобная оболочка функций.

      import matplotlib.pyplot as plt

def add_colorbar(im, width=None, pad=None, **kwargs):

    l, b, w, h = im.axes.get_position().bounds       # get boundaries
    width = width or 0.1 * w                         # get width of the colorbar
    pad = pad or width                               # get pad between im and cbar
    fig = im.axes.figure                             # get figure of image
    cax = fig.add_axes([l + w + pad, b, width, h])   # define cbar Axes
    return fig.colorbar(im, cax=cax, **kwargs)       # draw cbar
    

data = [(1,2,3,4,5),(4,5,6,7,8),(7,8,9,10,11)]

# an example usage
im = plt.imshow(data, cmap='RdBu')
add_colorbar(im)

Для таких типов сюжетов мне нравитсяImageGridAPI отmpl_toolkits.axes_grid1. Он предназначен для управления несколькими графиками с фиксированным соотношением сторон, но отлично работает для одного изображения.

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


fig = plt.figure()
plot = ImageGrid(fig, 111, (1, 1),
                 cbar_mode='single',
                 cbar_location='right',
                 cbar_size='3%',
                 cbar_pad='5%')
im = plot[0].imshow(np.random.randn(2**4, 2**6))
cbar = fig.colorbar(im, cax=plot.cbar_axes[0])

Если вы не хотите объявлять другой набор осей, самым простым решением, которое я нашел, является изменение размера фигуры с помощью вызова figsize.

В приведенном выше примере я бы начал с

      fig = plt.figure(figsize = (12,6))

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

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