Ustaw zakres pasków kolorów w matplotlib

156

Mam następujący kod:

import matplotlib.pyplot as plt

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')

plt.colorbar()
plt.show()

To tworzy wykres wartości „v” na osiach X względem Y, przy użyciu określonej mapy kolorów. Osie X i Y są idealne, ale mapa kolorów rozciąga się między min a max v. Chciałbym, aby mapa kolorów znajdowała się w zakresie od 0 do 1.

Myślałem o użyciu:

plt.axis(...)

Aby ustawić zakresy osi, ale wymaga to tylko argumentów dla min i maks X i Y, a nie mapy kolorów.

Edytować:

Dla jasności powiedzmy, że mam jeden wykres, którego wartości mieszczą się w zakresie (0 ... 0,3), i inny wykres, którego wartości (0,2 ... 0,8).

Na obu wykresach chcę, aby zakres paska kolorów wynosił (0 ... 1). Na obu wykresach chcę, aby ten zakres kolorów był identyczny przy użyciu pełnego zakresu cdict powyżej (więc 0,25 na obu wykresach będzie tego samego koloru). Na pierwszym wykresie wszystkie kolory z przedziału od 0,3 do 1,0 nie pojawią się na wykresie, ale będą w klawiszu paska kolorów z boku. W drugim przypadku wszystkie kolory od 0 do 0,2 oraz od 0,8 do 1 nie pojawią się na wykresie, ale na pasku kolorów z boku.

Paweł
źródło

Odpowiedzi:

177

Używanie vmini vmaxwymusza zakres kolorów. Oto przykład:

wprowadź opis obrazu tutaj

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )

def do_plot(n, f, title):
    #plt.clf()
    plt.subplot(1, 3, n)
    plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
    plt.title(title)
    plt.colorbar()

plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()
tom10
źródło
3
Dlaczego ta odpowiedź jest lepsza niż ta wykorzystująca plt.clim opublikowana przez @Amro?
Alex Lamson
90

Użyj funkcji CLIM (odpowiednik funkcji CAXIS w MATLAB):

plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4)  # identical to caxis([-4,4]) in MATLAB
plt.show()
Amro
źródło
2
Uważam, że clim () skaluje osie kolorów, ale same kolory zmieniają wartości. Punkt na określonej części skali będzie miał ten sam kolor niezależnie od skali, ale wartość, którą reprezentuje, ulegnie zmianie.
Paul
4
Tak. Jest to pożądane zachowanie pytającego, więc rozwiązuje problem: skala kolorów między wykresami powinna być identyczna.
Excalabur
16

Nie jestem pewien, czy jest to najbardziej eleganckie rozwiązanie (właśnie tego użyłem), ale możesz przeskalować dane do zakresu od 0 do 1, a następnie zmodyfikować pasek kolorów:

import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
                       norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)

Dzięki dwóm różnym limitom możesz kontrolować zakres i legendę paska kolorów. W tym przykładzie na pasku wyświetlany jest tylko zakres od -0,5 do 1,5, podczas gdy mapa kolorów obejmuje od -2 do 2 (więc może to być zakres danych, który rejestrujesz przed skalowaniem).

Zamiast więc skalować mapę kolorów, skalujesz dane i dopasowujesz do tego pasek kolorów.

nikow
źródło
1
Myślę, że to robi coś subtelnie innego… przepraszam, prawdopodobnie nie byłem wystarczająco precyzyjny w moim pytaniu. Twoje rozwiązanie przeskaluje kolory tak, aby to, co wcześniej reprezentowało wartość 1.0, teraz reprezentowało maksymalną wartość w moich danych. Pasek kolorów pokaże 0..1 tak, jak tego potrzebuję (z vmin = 0, vmax = 1), ale wszystko powyżej tej maksymalnej wartości będzie tego samego koloru ...
Paul
1
... Zaktualizowałem moje pytanie, aby wyraźniej pokazać, o co mi chodzi. Przepraszam, jeśli byłem zbyt niejasny.
Paul
10

Korzystanie ze środowiska rysunku i .set_clim ()

Ta alternatywa może być łatwiejsza i bezpieczniejsza, jeśli masz wiele działek:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))

fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end

plt.show()

wprowadź opis obrazu tutaj

Pojedynczy pasek kolorów

Najlepszą alternatywą jest wtedy użycie jednego paska koloru dla całej powierzchni. Można to zrobić na różne sposoby. Ten samouczek jest bardzo przydatny do zrozumienia najlepszej opcji. Wolę takie rozwiązanie, które można po prostu skopiować i wkleić zamiast poprzedniej wizualizującej części kodu colorbar .

fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
                    wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)

wprowadź opis obrazu tutaj

PS

Sugerowałbym użycie pcolormeshzamiast, pcolorponieważ jest szybszy (więcej informacji tutaj ).

GM
źródło