将 Matplotlib 颜色条大小设置为匹配图形

2024-12-27 08:47:00
admin
原创
118
摘要:问题描述:我无法让像这样的colorbar图表的imshow高度与图表的高度相同,除非事后使用 Photoshop。我该如何让高度匹配?解决方案 1:这种组合(以及接近这些组合的值)似乎“神奇地”起作用了,无论显示屏的尺寸如何,都能保持颜色条与图表的比例一致。plt.colorbar(im,fraction=...

问题描述:

我无法让像这样的colorbar图表的imshow高度与图表的高度相同,除非事后使用 Photoshop。我该如何让高度匹配?
颜色条大小不匹配的示例


解决方案 1:

这种组合(以及接近这些组合的值)似乎“神奇地”起作用了,无论显示屏的尺寸如何,都能保持颜色条与图表的比例一致。

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

它也不需要共享轴,因为共享轴可能会使图变得不方形。

解决方案 2:

您可以使用 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)

在此处输入图片描述

解决方案 3:

我很感谢上述所有答案。但是,正如一些答案和评论指出的那样,该axes_grid1模块无法处理 GeoAxes,而调整fractionpadshrink和其他类似参数不一定能给出非常精确的顺序,这真的让我很困扰。我相信赋予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)

结果

在此处输入图片描述

解决方案 4:

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

解决方案 5:

当您创建colorbar尝试使用分数和/或收缩参数时。

摘自文件:

分数 0.15;用于颜色条的原始轴的分数

收缩 1.0;收缩颜色条的分数

解决方案 6:

以上所有解决方案都很好,但我最喜欢@Steve 和@bejota 的解决方案,因为它们不需要花哨的调用并且具有通用性。

我说的通用是指它适用于任何类型的轴,包括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 之间的分数来获得最佳尺寸。但是,分数和 paddig 的值需要进行调整才能最适合您的图,并且会根据颜色条的方向是垂直还是水平而有所不同。

解决方案 7:

另一种方法是

shrink=0.7, aspect=20*0.7

shrink缩放高度和宽度,但aspect参数恢复原始宽度。默认纵横比为 20。0.7是根据经验确定的。

解决方案 8:

axes_grid1.axes_divider是此任务的规定方法(matplotlib 甚至有一个演示),但通过添加颜色条,它会使图像变小。如果您想保留原始图像大小,则以下提供了一种方法(基于Fei Yao 的回答)。

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)

结果

解决方案 9:

我最近遇到了这个问题,我曾经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()

我发现这在创建以 matplotlib 对象作为参数、在其上绘制并返回对象的函数时特别有用Axes,因为我不需要传递必须从对象生成的单独轴figure,或者传递figure对象本身。

解决方案 10:

到目前为止提供的答案非常有用,但根据情况,它们可能存在缺点。例如,它们:

  1. 会占用轴空间(1、2、3),这在使用多个子图时会变得明显,

  2. 调整以找到最适合每个图的特定值(2 , 3)或

  3. fig.tight_layout()调用( 4 )后可能会导致难以预测的结果。

避免上述缺点的替代方法是通过以下方式添加插入/子轴ax.inset_axes

import matplotlib.pyplot as plt
import numpy as np


x = np.linspace(0, 1, 70)
y = np.linspace(0, 1, 100)
data = (np.stack(np.meshgrid(x, y)) ** 2).sum(0)


fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)

# specify lower-left corner, width and height of the colorbar, 
# in coordinates relative to its parent axis
cax = axs[1].inset_axes((1.05, 0, 0.08, 1.0))
fig.colorbar(hm, cax=cax)
fig.suptitle(r"$f{Add~axis~with~relative~(axis)~coordinates}$", y=0.87)
fig.tight_layout()

通过这种替代方案,颜色条的坐标和大小是相对于其父轴的,从而确保无论父轴的形状或其他子图的排列如何,结果都是可预测的。结果:

使用轴坐标

这些是迄今为止获得最多赞同的答案所获得的结果(重现代码在最后)。

  1. 使用轴分隔符:

分隔符

  1. 使用fraction+pad参数

分数+垫

  1. 在图形坐标中添加新轴

图形坐标中的轴

  1. 使用shrink+aspect参数

缩小+方面

重现代码:

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

plt.close("all")

x = np.linspace(0, 1, 70)
y = np.linspace(0, 1, 100)
data = (np.stack(np.meshgrid(x, y)) ** 2).sum(0)

# using Axes divider
fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)
divider = make_axes_locatable(axs[1])
cax = divider.append_axes("right", size="5%", pad=0.05)
fig.colorbar(hm, cax=cax)
fig.suptitle(r"$f{Axes~divider}$" + "
steals axis space", y=0.92)
fig.tight_layout()

# using fraction + pad
fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)
fig.colorbar(hm, ax=axs[1], fraction=0.046, pad=0.04)
fig.suptitle(
    r"$f{fraction+pad}$" + "
steals axis space + trial and error positioning", y=0.92
)
fig.tight_layout()

# adding axis in absolute (figure) coordinates
fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)
cax = fig.add_axes(
    (
        axs[1].get_position().x1 + 0.01,
        axs[1].get_position().y0,
        0.02,
        axs[1].get_position().y1 - axs[1].get_position().y0,
    ),
)
fig.colorbar(hm, cax=cax)
fig.suptitle(
    r"$f{Add~axis~with~Figure~coordinates}$"
    + "
Trial and error + hard to predict after tight_layout",
    y=1.02,
)
fig.tight_layout()

# using shrink=0.7, aspect=20*0.7
fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)
fig.colorbar(hm, ax=axs[1], shrink=0.7, aspect=20 * 0.7)
fig.suptitle(
    r"$f{shrink=0.7,~aspect=20*0.7}$" + "
steals axis space + trial and error",
    y=0.92,
)
fig.tight_layout()

# adding axis in relative (axis) coordinates
fig, axs = plt.subplots(1, 3, figsize=(6, 3))
for ax in axs:
    hm = ax.imshow(data)
cax = axs[1].inset_axes((1.05, 0, 0.08, 1.0))
fig.colorbar(hm, cax=cax)
fig.suptitle(r"$f{Add~axis~with~relative~(axis)~coordinates}$", y=0.87)
fig.tight_layout()

解决方案 11:

对于这些类型的图,我喜欢 的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])

mpl_toolkits.axes_grid1.ImageGrid 固定纵横比图像,匹配颜色条

解决方案 12:

如果您不想声明另一组轴,我发现的最简单的解决方案是使用 figsize 调用来更改图形大小。

在上面的例子中,我将首先

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

然后以不同的比例重新渲染,直到色条不再遮挡主线情节。

解决方案 13:

我发现的针对此问题的最佳解决方案已在本页中详细解释。

基本上,一旦你开始想象:

fig, myplot = plt.subplots((1,1), figsize = (12,10), layout = 'constrained')

提供论点:

layout = 'constrained'

对我有用。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用