Ustaw rozmiar paska kolorów Matplotlib, aby dopasować wykres

161

Nie mogę sprawić, aby pasek kolorów na wykresach imshow, takich jak ten, miał taką samą wysokość jak wykres, z wyjątkiem użycia Photoshopa po fakcie. Jak dopasować wysokości?Przykład niezgodności rozmiaru paska kolorów

Elliot
źródło
Czy wypróbowałeś sugestie ze stackoverflow.com/questions/16702479/ ...
lmjohns3
@ imjohns3 Wydaje się, że nic w tym poście nie wpływa na pasek kolorów. Pozostaje tej samej wielkości bez względu na to, co ustawię. Jeśli jednak ustawię ułamek i zmniejszę, rozmiar wykresu zmieni się, podczas gdy pasek kolorów pozostanie taki sam, dopóki nie wrócimy do tego, co już mam, wtedy przestają robić cokolwiek.
Elliot
Sprawdź dokumentację - matplotlib.org/api/colorbar_api.html - i użyj fractionlub shrinkargs.
BoltzmannBrain

Odpowiedzi:

194

Możesz to łatwo zrobić za pomocą AxisDivider matplotlib .

Przykład ze strony, do której prowadzi łącze, działa również bez korzystania z wykresów podrzędnych:

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)

wprowadź opis obrazu tutaj

bogatron
źródło
1
Nie pracuję w podplocie, więc to nie ma zastosowania.
Elliot
Po wielu majsterkowaniu, udało się. Ma wiele problemów z interakcją z subplot_adjust, musisz odbierać wywołania we właściwych miejscach względem siebie.
Elliot
11
To nieznacznie zmienia rozmiar wykresów. Mam 4 w siatce 2x2, ale chcę, aby tylko dwa po prawej miały paski (skala dotyczy wierszy). Jednak to sprawia, że ​​nie są tego samego rozmiaru. Próbowałem po prostu nie mieć wywołania Colorbar (z połączeniem rozdzielającym), ale oczywiście pozostawia to puste białe pole i cyfry z boku. Jak sprawić, by miały stały rozmiar bez nakładania na nich wszystkich pasków?
Elliot
@bogatron Niestety to nie działa z rzutowanymi osiami
Bogdan
Jeśli chcesz dodać tytuł za pomocą plt.title, zostanie on zamieniony na prawo. Czy istnieje sposób, aby to przezwyciężyć?
user2820579
224

Ta kombinacja (i zbliżone do nich wartości) wydaje mi się „magicznie” działać, utrzymując pasek kolorów skalowany do wykresu, niezależnie od rozmiaru wyświetlacza.

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

Nie wymaga również współdzielenia osi, która może wyprowadzić wykres z kwadratu.

skytaker
źródło
4
To może działać w niektórych przypadkach, ale generalnie nie. Spróbuj np. Wykreślić coś podobnego do pierwotnego pytania, które ma szerokość dwukrotnie większą od wysokości.
Matthias
Opcja ułamka nadal wydaje się działać w przypadku, o którym wspominasz, jeśli jest skalowana w celu dopasowania do wysokości wykresu. (mpl v1.4.3)
skytaker
6
To jedyny uniwersalny sposób na zrobienie tego. Rozwiązania z axex_grid1 nie będą działać dla rzutowanych osi, takich jak GeoAxes.
Bogdan
OMG, to jest niesamowite. Bardzo dziękuję za rozwiązanie
2D_
3
W odpowiedzi na komentarz @Matthias. Możesz poprawić sytuację, w której obraz jest zbyt szeroki, używając tej sztuczki: im_ratio = data.shape[0]/data.shape[1] plt.colorbar(im,fraction=0.046*im_ratio, pad=0.04)gdzie datajest twój obraz.
eindzl
30

@bogatron udzielił już odpowiedzi sugerowanej przez dokumentację matplotlib , która daje odpowiednią wysokość, ale wprowadza inny problem. Teraz szerokość paska kolorów (jak również przestrzeń między paskiem kolorów a wykresem) zmienia się wraz z szerokością wykresu. Innymi słowy, współczynnik kształtu paska kolorów nie jest już ustalony.

Aby uzyskać zarówno odpowiednią wysokość, jak i dane proporcje, musisz zagłębić się nieco w tajemniczy axes_grid1moduł.

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)

Zwróć uwagę, że określa to szerokość paska kolorów w stosunku do wysokości wykresu (w przeciwieństwie do szerokości figury, tak jak było wcześniej).

Odstęp między paskiem kolorów a wykresem można teraz określić jako ułamek szerokości paska kolorów, co jest liczbą IMHO o wiele bardziej znaczącą niż ułamek szerokości figury.

wykres obrazu z paskiem kolorów

AKTUALIZACJA:

Stworzyłem notatnik IPython na ten temat , w którym spakowałem powyższy kod do łatwej do ponownego wykorzystania funkcji:

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)

Można go używać w następujący sposób:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)
Matthias
źródło
1
To bardzo pomocna mała funkcja! Jedno słowo ostrzeżenia jest takie, że nie działa, gdy chcesz dodać wiele pasków kolorów, ponieważ pojawiają się one jeden na drugim.
David Hall
22

Doceniam wszystkie powyższe odpowiedzi. Jednak, podobnie jak niektóre odpowiedzi i komentarze zauważono, axes_grid1moduł może nie GeoAxes adres, a regulacja fraction, pad, shrink, i inne podobne parametry mogą niekoniecznie dają bardzo dokładny porządek, który naprawdę mnie martwi. Uważam, że dając colorbarjej własny axesmoże być lepszym rozwiązaniem, aby rozwiązać wszystkie problemy, które zostały wymienione.

Kod

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)

Wynik

wprowadź opis obrazu tutaj

Później znajduję również matplotlib.pyplot.colorbaroficjalną dokumentacjęax opcję, czyli istniejące osie, które zapewnią miejsce na pasek kolorów. Dlatego jest przydatna w przypadku wielu wykresów pomocniczych, patrz poniżej.

Kod

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)

Wynik

wprowadź opis obrazu tutaj

Ponownie, możesz również osiągnąć podobne efekty, określając cax, dokładniej z mojej perspektywy.

Kod

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)

Wynik

wprowadź opis obrazu tutaj

Fei Yao
źródło
@ Mgławica HS Dziękuję za uwagi i cieszę się, że Ci się podobało. :)
Fei Yao
Naprawdę niesamowita odpowiedź. Dziękuję Ci!!
episodeyang
11

Podczas tworzenia colorbar próby użyj parametrów fraction i / lub shrink.

Z dokumentów:

frakcja 0,15; ułamek oryginalnych osi do użycia w pasku kolorów

zmniejszyć 1,0; ułamek, o który należy zmniejszyć pasek kolorów

Steve Barnes
źródło
1
Jeśli ustawię shrink 1.0 i fraction na cokolwiek, zmniejsza to wykres, nie wpływając w ogóle na rozmiar paska kolorów, dopóki zmiana frakcji nie spowoduje, że będzie to dokładnie to, co już mam, w którym momencie ich zmiana przestaje robić cokolwiek.
Elliot
Gdzie dokładnie je określasz, muszą to być parametry colorbar()funkcji lub metody.
Steve Barnes
Dzięki. wystarczy podać parametr shrink i działa jak magia!
CodingNow
11

Wszystkie powyższe rozwiązania są dobre, ale najbardziej lubię @ Steve i @ bejota, ponieważ nie wymagają wyszukanych połączeń i są uniwersalne.

Przez uniwersalny rozumiem, że działa z każdym typem osi, w tym GeoAxes. Na przykład, jeśli masz rzutowane osie do mapowania:

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

wezwanie do

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

zawiedzie z: KeyException: map_projection

Dlatego jedynym uniwersalnym sposobem radzenia sobie z rozmiarem paska kolorów dla wszystkich typów osi jest:

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

Pracuj z ułamkiem od 0,035 do 0,046, aby uzyskać najlepszy rozmiar. Jednak wartości frakcji i paddig będą musiały zostać dostosowane, aby uzyskać najlepsze dopasowanie do wykresu i będą się różnić w zależności od tego, czy orientacja paska kolorów jest w pozycji pionowej, czy poziomej.

Bogdana
źródło
1
Możemy również dodać shrinkparametr, gdy fractionrazem z padnie dają one pożądanych rezultatów.
Fei Yao,