Matplotlib 2 서브 플로트, 1 컬러 바


235

Matplotlib에서 두 개의 서브 플로트를 사용하여 동일한 y 축을 하나의 컬러 막대로 공유하는 방법을 연구하는 데 너무 오랜 시간을 보냈습니다.

무슨 일이 있었 는지 또는 에서 colorbar()함수를 호출하면 플롯을 자동 스케일링하여 컬러 바와 플롯이 '서브 플롯'경계 상자 안에 들어가서 두 개의 나란히 플롯이 두 가지 매우 다르게 나타납니다. 크기.subplot1subplot2

이 문제를 해결하기 위해 세 번째 서브 플롯을 만들려고 시도했는데 컬러 바가있는 플롯을 렌더링하지 않기 위해 해킹했습니다. 유일한 문제는, 이제 두 플롯의 높이와 너비가 고르지 않으며, 어떻게 보이게 만드는지 알 수 없습니다.

내 코드는 다음과 같습니다.

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

답변:


319

컬러 바를 자체 축 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()

여기에 이미지 설명을 입력하십시오

색상 범위가 마지막 이미지로 설정한다는 주 (초래했다 그 플롯 im값의 범위로 설정되어있는 경우에도) vminvmax. 예를 들어 다른 플롯에 최대 값이 높으면 최대 값보다 높은 값을 가진 점이 im균일 한 색상으로 표시됩니다.


4
ImageGrid는이 정확한 목적에도 매우 유용합니다.
Phillip Cloud

5
tight_layout ()을 사용해야하는 경우 tight_layout 후 subplots_adjust 이후에 모든 작업을 수행 한 다음 subplots_adjust 및 add_axes의 좌표를 수동으로 조정해야합니다.
user1748155

2
이미 가지고있는 두 개의 서로 다른 산점도에 대해 단일 색상 막대를 가질 수있는 방법 위에서 시도했지만 "im"을 적절한 변수로 대체하는 방법을 모르겠습니다. 내 산점도는 plot1 = pylib.scatter (x, y, z)이고 plot2 = pylib.scatter (a, b, c)
Rotail

46
이것은 다른 사람들에게 분명했을 수도 있지만, 컬러 바가 모든 플롯의 색상을 정확하게 나타내려면 vminvmax인수가 중요 하다는 점을 지적하고 싶습니다 . 각 서브 플롯의 색상 범위를 제어합니다. 실제 데이터가있는 경우 최소값과 최대 값을 먼저 찾으려면이를 통과해야 할 수 있습니다.
James Owers

2
플롯의 값 범위가 다른 경우 컬러 바 범위는 마지막 플롯의 범위 만 표시합니다. 어떤 제안?
Lukas

132

축 목록과 함께 ax매개 변수를 사용하여 Joe Kington의 코드를 단순화 할 수 있습니다 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


4
이 솔루션은 여기에서 매우 효과적이며 가장 쉬운 것으로 보입니다.
Kknd

8
nrows를 1로 변경하면 두 플롯 모두 컬러 바보다 숏입니다. 그렇다면이 문제를 어떻게 해결할 수 있습니까?
Jin

6
동정은 tight_layout과 함께 작동하지 않지만 그럼에도 불구하고 좋은 해결책입니다.
Mark

1
기억하기 만하면 ...이 솔루션을 좋아합니다! Tinha que ser cearense!
iury simoes-sousa

1
이 답변의 중요한 부분은입니다 fig.colorbar(im, ax=axes.ravel().tolist()). 를 생략 ax=axes.ravel().tolist()하면 컬러 바가 하나의 서브 플롯 내에 배치됩니다.
nyanpasu64

55

이 솔루션은 축 위치 또는 색상 막대 크기를 수동으로 조정할 필요가 없으며 여러 행 단일 행 레이아웃과 함께 작동합니다 tight_layout(). matplotlib의 AxesGrid Toolbox를 사용하여 갤러리 예제 에서 수정되었습니다 .ImageGrid

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

이미지 그리드


더블 +1, 이것은 훌륭한 접근법입니다
Brett

실제로 tight_layout과 함께 작동하지만 해당 색상 막대에 레이블을 추가하는 방법을 모릅니다. kws 레이블, 제목, 텍스트 등은 허용되지 않습니다. 그리고 문서는별로 도움이되지 않습니다.
TomCho

3
@TomCho 레이블을 설정하려면 인스턴스화 할 때 다음과 같이 색상 막대의 핸들을 잡을 수 있습니다 thecb = ax.cax.colorbar(im). 그럼 당신은 할 수 있습니다thecb.set_label_text("foo")
spinup

1
컬러 맵을 변경하는 방법?
Sigur

1
@Sigur 나는 당신이 지금까지 그것을 알아 냈을 것이라고 확신하지만, 다른 사람들을 위해 im을 선언 할 때 cmap을 변경할 수 있습니다 : im = ax.imshow (data, vmin = 0, vmax = 1, cmap = 'your_cmap_here')
Shaun

38

사용 make_axes이 훨씬 쉽고 더 나은 결과를 제공합니다. 또한 컬러 바의 위치를 ​​사용자 지정할 수 있습니다. 또한 subplotsx 및 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()


7
서브 플롯이 제곱이 아닌 경우이 방법이 작동하지 않습니다. 을 변경 nrows=1하면 색상 막대가 하위 그림보다 다시 커집니다.
웨슬리 탄지

matplotlib의 기본값은 무엇입니까? 좋아 보인다!
rafaelvalle

18

이 스레드를 우연히 발견 한 초보자로서 abevieiramota 의 매우 깔끔한 답변에 대한 Python-for-dummies 적응을 추가하고 싶습니다. 그들의 코드는하고 있었다) :

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

pythonic이 훨씬 적고, 나와 같은 멍청한 놈들이 실제로 무슨 일이 일어나고 있는지 쉽게 알 수 있습니다.


17

다른 답변에서 지적했듯이 아이디어는 일반적으로 컬러 바가 상주 할 축을 정의하는 것입니다. 다양한 방법이 있습니다. 아직 언급되지 않은 것은 서브 플롯 생성시 컬러 바 축을 직접 지정하는 것입니다 plt.subplots(). 이점은 축 위치를 수동으로 설정할 필요가 없으며 자동 종횡비를 사용하는 경우 모든 색상 막대가 서브 플로트와 정확히 동일한 높이가됩니다. 이미지가 많이 사용되는 경우에도 결과는 다음과 같이 만족 스럽습니다.

사용하는 경우 plt.subplots(), 사용 gridspec_kw인수는 다른 축보다 년 Colorbar 축이 훨씬 작게 만들 수 있습니다.

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

여기에 이미지 설명을 입력하십시오

플롯의 가로 세로 비율이 자동으로 조정되거나 가로 방향으로 가로 세로 비율로 인해 이미지가 축소되는 경우 (위와 같이) 잘 작동합니다. 그러나 이미지가 더 넓고 높으면 결과는 다음과 같으며 바람직하지 않을 수 있습니다.

여기에 이미지 설명을 입력하십시오

하기 용액 부가 적 줄거리 높이로 년 Colorbar 높이를 수정 사용하는 것 mpl_toolkits.axes_grid1.inset_locator.InsetPosition, 화상 부가 적 줄거리 축에 상대적인 년 Colorbar 축을 설정.

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

여기에 이미지 설명을 입력하십시오


여기에 물어볼 수 있는지 확실하지 않지만 ax = fig.add_subplot()대신 이 솔루션을 구현하는 방법이 있습니까? 베이스 맵과 함께 사용하는 방법을 알 수 없기 때문에 묻습니다.
lanadaquenada

1
@lanadaquenada 예 가능하지만, 당신은을 제공 할 필요가 그 GridSpec에게 add_subplot()그 경우에.
ImportanceOfBeingErnest

10

abevieiramota 의 축 목록을 사용하는 솔루션은 주석에서 지적한 것처럼 한 행의 이미지 만 사용할 때까지 매우 잘 작동합니다. 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년 Colorbar 축의 크기 스케일링 인자이다 파라미터. 수동 시험 및 오류가 필요합니다. 예를 들면 다음과 같습니다.

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

축소 된 컬러 바가있는 1 x 3 이미지 배열


4

@abevieiramota의 훌륭한 답변에 추가하기 위해 constrained_layout을 사용하여 tight_layout의 euqivalent를 얻을 수 있습니다. 에 의해 부과되는 1 : 1 종횡비 때문에 imshow대신 사용하면 여전히 큰 수평 간격이 pcolormesh생깁니다 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()

여기에 이미지 설명을 입력하십시오


1

나는 게시 된 거의 모든 솔루션이 관련된 것으로 ax.imshow(im, ...)나타 났으며 여러 하위 구성 요소의 색상 막대에 표시되는 색상을 표준화하지 않았습니다. im맵핑이 마지막 인스턴스에서 촬영되어 있지만 여러 값 어떤 경우 im-s는 다르다? (이 매핑 가능 항목은 등고선 세트 및 표면 세트 처리와 같은 방식으로 처리된다고 가정합니다.) 아래 3d 표면 플롯을 사용하여 2x2 서브 플롯에 대해 두 개의 컬러 막대를 만드는 예제가 있습니다 (한 행당 하나의 컬러 막대) ). 이 질문은 명시 적으로 다른 배열을 요구하지만, 그 예는 몇 가지를 분명히하는 데 도움이된다고 생각합니다. plt.subplots(...)불행히도 3D 축으로 인해 아직 이 방법을 찾지 못했습니다.

플롯 예

컬러 바를 더 나은 방법으로 배치 할 수 있다면 ... (아마도 훨씬 더 나은 방법이 있지만, 그렇게하기가 쉽지 않습니다.)

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

다수의 값 경우 im의 서로 다른, 그들은해야 하지 원래의 질문이 정말 적용되지 않는다, 그래서 같은 년 Colorbar를 사용
스핀 업

0

이 주제는 잘 다루어졌지만 여전히 약간 다른 철학으로 다른 접근법을 제안하고 싶습니다 .

설정하는 것이 조금 더 복잡하지만 (제 의견으로는) 좀 더 융통성을 허용합니다. 예를 들어, 각 서브 플롯 / 컬러 바의 각 비율로 재생할 수 있습니다.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec

# Define number of rows and columns you want in your figure
nrow = 2
ncol = 3

# Make a new figure
fig = plt.figure(constrained_layout=True)

# Design your figure properties
widths = [3,4,5,1]
gs = GridSpec(nrow, ncol + 1, figure=fig, width_ratios=widths)

# Fill your figure with desired plots
axes = []
for i in range(nrow):
    for j in range(ncol):
        axes.append(fig.add_subplot(gs[i, j]))
        im = axes[-1].pcolormesh(np.random.random((10,10)))

# Shared colorbar    
axes.append(fig.add_subplot(gs[:, ncol]))
fig.colorbar(im, cax=axes[-1])

plt.show()

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.