Matplotlib 2 Alt grafik, 1 Renk Çubuğu


235

Matplotlib'de ikisi arasında paylaşılan tek bir colorbar ile aynı y eksenini paylaşmak için iki alt grafiğin nasıl alınacağını araştırmak için tamamen çok uzun zaman harcadım.

Olan şey colorbar(), ya fonksiyonlardan birini çağırdığımda ya subplot1da subplot2renk çubuğunu artı grafiğin 'alt alan' sınırlama kutusunun içine sığacağı ve iki yan yana çizimin iki çok farklı olmasına neden olacak şekilde grafiği otomatik olarak ölçeklendirmesiydi. boyutları.

Bunu aşmak için, daha sonra sadece bir colorbar mevcut hiçbir arsa oluşturmak için hack üçüncü bir alt grafik oluşturmaya çalıştım. Tek sorun şu ki, iki parselin yüksekliği ve genişliği eşit değil ve nasıl iyi görüneceğini anlayamıyorum.

İşte benim kod:

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

Yanıtlar:


319

Sadece renk çubuğunu kendi eksenine yerleştirin ve kullanın subplots_adjust ona yer açmak için .

Kısa bir örnek olarak:

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

resim açıklamasını buraya girin

Renk aralığı son görüntü tarafından belirleneceğini söyledi Not (edilmesine sebep olduğunu çizilen imdeğer aralığı tarafından ayarlanmış olsa bile) vminve vmax. Örneğin, başka bir grafik daha yüksek bir maksimum değere sahipse, maks. Değerden daha yüksek değerlere sahip noktalar imdüzgün renkte gösterilir.


4
ImageGrid bu amaç için de çok kullanışlıdır.
Phillip Cloud

5
tight_layout () kullanmanız gerekiyorsa, tight_layout'tan sonra subplots_adjust'tan sonra her şeyi yapmak ve daha sonra subplots_adjust ve add_axes koordinatlarını manuel olarak değiştirmek istersiniz.
user1748155

2
Zaten sahip olduğum iki farklı dağılım grafiği için nasıl tek bir renk çubuğuna sahip olabilirim? Yukarıda denedim ama nasıl "im" yerine uygun değişkenler bilmiyorum. Diyelim ki dağılım grafiklerim plot1 = pylib.scatter (x, y, z) ve plot2 = pylib.scatter (a, b, c)
Rotail

46
Bu diğerleri için açık olabilir, ancak renk çubuğunun tüm parsellerdeki rengi doğru bir şekilde temsil etmesi için vminve vmaxargümanlarının kritik olduğunu belirtmek istedim . Her alt grafiğin renk aralığını kontrol ederler. Gerçek verileriniz varsa, önce min ve maks değerlerini bulmak için bunu geçmeniz gerekebilir.
James Owers

2
grafiklerin değer aralığı farklıysa, renk çubuğu aralığı yalnızca son grafiğin aralığını gösterir, değil mi? baska öneri?
Lukas

132

Bir eksen listesiyle axparametresini kullanarak Joe Kington'un kodunu basitleştirebilirsiniz figure.colorbar(). Gönderen belgeler :

balta

Yok | yeni bir renk çubuğu ekseni için alanın çalınacağı üst eksen nesneleri. Bir eksen listesi verilirse, tümü renk çubuğu eksenlerine yer açmak için yeniden boyutlandırılır.

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
Bu çözüm burada çok iyi çalıştı ve en kolay çözüm gibi görünüyor.
KKND

8
Boşlukları 1 olarak değiştirirseniz, her iki grafik de renk çubuğundan daha kısadır. Peki, bu sorunu nasıl çözebilirim?
Jin

6
Ne yazık ki tight_layout ile çalışmıyor, ama yine de iyi bir çözüm.
Mark

1
Sadece hatırlamak için ... Bu çözümü seviyorum! Tinha que ser cearense!
iury simoes-sousa

1
Bu cevabın önemli kısmı fig.colorbar(im, ax=axes.ravel().tolist()). Eğer atlarsanız ax=axes.ravel().tolist(), Colorbar bir subplot içinde yer alacaktır.
nyanpasu64

55

Bu çözüm, eksen konumlarının veya renk çubuğu boyutunun manuel olarak değiştirilmesini gerektirmez, çok satırlı ve tek sıralı düzenlerle çalışır ve ile çalışır tight_layout(). Bu bir den uyarlanmıştır galeri örneğin kullanarak, ImageGridMatplotlib en dan AxesGrid Toolbox .

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

görüntü ızgarası


Çift +1, bu harika bir yaklaşım
Brett

Gerçekten tight_layout ile çalışır, ancak nasıl o renk çubuğuna bir etiket eklemek için hiçbir fikrim yok. Kws etiketini, başlığını, metnini ... hiçbir şey kabul etmiyor! Ve dokümanlar pek yardımcı olmuyor.
TomCho

3
@TomCho için siz bunu örneğini zaman Colorbar'ın kolu tutmam yeterlidir, bir etiket ayarlayın: thecb = ax.cax.colorbar(im). Sonra yapabilirsinthecb.set_label_text("foo")
spinup

1
Renk eşlemi nasıl değiştirilir?
Sigur

1
@Sigur Şimdiye kadar çözdüğünüze eminim, ancak diğerleri için im: im = ax.imshow (data, vmin = 0, vmax = 1, cmap = 'your_cmap_here') bildirirken cmap'i değiştirebilirsiniz.
Shaun Lowis

38

Kullanımı make_axesdaha da kolaydır ve daha iyi bir sonuç verir. Ayrıca renk çubuğunun konumunu özelleştirmek için olanaklar sunar. Ayrıca subplotsx ve y eksenlerini paylaşma seçeneğini de not edin .

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
Alt grafik kare olmadığında bu yöntem çalışmaz. Eğer değiştirirsennrows=1 , renk çubuğu tekrar alt grafiklerden daha büyük olur.
Wesley Tansey

Matplotlib varsayılanlarınız nedir? harika görünüyor!
rafaelvalle

18

Bu iş parçacığını tökezleyen bir acemi olarak, abevieiramota'nın çok düzgün cevabına bir python- dummies uyarlaması eklemek istiyorum (çünkü ne olduğunu çözmek için 'ravel' aramak zorunda olduğum seviyedeyim kodları yapıyordu):

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

Çok daha az pitonik, benim gibi yeni başlayanların burada gerçekte ne olduğunu görmesi çok daha kolay.


17

Diğer cevaplarda belirtildiği gibi, fikir genellikle colorbar'ın ikamet etmesi için bir eksen tanımlamaktır. Bunu yapmanın çeşitli yolları vardır; henüz bahsedilmemiş olanı, alt grafik oluşturmada renk çubuğu eksenlerini doğrudanplt.subplots() . Avantaj, eksen konumunun manuel olarak ayarlanması gerekmemesi ve her durumda otomatik yönü olan renk çubuğunun alt grafiklerle tam olarak aynı yükseklikte olmasıdır. Görüntülerin kullanıldığı birçok durumda bile sonuç aşağıda gösterildiği gibi tatmin edici olacaktır.

Kullanırken plt.subplots(), gridspec_kwbağımsız değişken kullanımı renk çubuğu eksenlerini diğer eksenlerden çok daha küçük hale getirmeye izin verir.

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})

Misal:

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

resim açıklamasını buraya girin

Grafiklerin görünüşü otomatik ölçeklendirilirse veya genişlik yönündeki görünümleri nedeniyle görüntüler küçülürse (yukarıdaki gibi) bu işe yarar. Bununla birlikte, görüntüler daha yüksekse, sonuç aşağıdaki gibi görünecektir ve bu istenmeyen bir durumdur.

resim açıklamasını buraya girin

Renk çubuğu yüksekliğini alt alan yüksekliğine sabitlemek için bir çözüm , renkmpl_toolkits.axes_grid1.inset_locator.InsetPosition çubuğu eksenlerini görüntü alt grafiği eksenlerine göre ayarlamak için kullanmak olacaktır .

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

resim açıklamasını buraya girin


Bunu burada sorma iznim olup olmadığından emin değilim, ancak ax = fig.add_subplot()bunun yerine bu çözümü uygulamanın bir yolu var mı? Soruyorum çünkü taban haritasıyla nasıl kullanacağımı anlayamıyorum.
lanadaquenada

1
@lanadaquenada Evet bu mümkün, ancak bu durumda bir a GridSpecsağlamanız gerekir add_subplot().
ImportanceOfBeingErnest

10

Abevieiramota tarafından bir eksen listesi kullanmanın çözümü , yorumlarda belirtildiği gibi yalnızca bir satır görüntü kullanana kadar çok iyi çalışır. Yardım için makul bir en-boy oranı kullanmak figsize, ama yine de mükemmel olmaktan uzak. Örneğin:

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 resim dizisi

Colorbar fonksiyonu sağlar shrinkColorbar eksenlerinin boyutu için bir ölçek faktörü olup parametre. Bazı manuel deneme ve hata gerektirir. Örneğin:

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

Küçültülmüş renk çubuklu 1 x 3 görüntü dizisi


4

@ Abevieiramota'nın mükemmel cevabına eklemek için, kısıtlanmış_dizgisi ile tight_layout'un euqivalent'ini alabilirsiniz. Tarafından uygulanan 1: 1 en boy oranı imshowyerine kullanırsanız, büyük yatay boşluklar elde edersiniz . pcolormeshimshow

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

resim açıklamasını buraya girin


1

Yayınlanan hemen hemen her çözümün ax.imshow(im, ...), çoklu alt şekiller için renk çubuğunda görüntülenen renkleri normalleştirmediğini fark ettim . imHaritalanabilir son örneği alınır, ancak birden değerleri ne olur im-s farklı? (Bu eşleştirilebilirlerin kontur kümeleri ve yüzey kümeleriyle aynı şekilde ele alındığını varsayıyorum.) Aşağıda 2x2 alt grafiği için iki renk çubuğu oluşturan bir 3d yüzey çizimi kullanan bir örneğim var (her satırda bir renk çubuğu) ). Her ne kadar soru açıkça farklı bir düzenleme istese de, örnek bazı şeyleri açıklığa kavuşturmaya yardımcı olduğunu düşünüyorum. Ne plt.subplots(...)yazık ki 3D eksenleri nedeniyle bunu kullanarak bir yol bulamadım .

Örnek Grafik

Renk çubuklarını daha iyi bir şekilde yerleştirebilseydim ... (Bunu yapmanın muhtemelen çok daha iyi bir yolu var, ama en azından takip etmek çok zor olmamalı.)

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

Birden gelen değerler ise ims farklıdır, bunlar gerektiği değil orijinal soru gerçekten geçerli değildir diye, aynı Colorbar kullanmak
spinup

0

Bu konu iyi işlenmiş ancak yine de biraz farklı bir felsefede başka bir yaklaşım önermek istiyorum .

Kurulumu biraz daha karmaşıktır, ancak (bence) biraz daha esneklik sağlar. Örneğin, her alt grafik / renk çubuğunun ilgili oranları ile oynatılabilir:

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

resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.