Matplotlib - добавить цветную полосу в последовательность линейных графиков - PullRequest
56 голосов
/ 01 декабря 2011

У меня есть последовательность линейных графиков для двух переменных (x, y) для ряда различных значений переменной z. Я обычно добавляю линейные сюжеты с такими легендами:

import matplotlib.pyplot as plt

fig = plt.figure()
ax  = fig.add_subplot(111)
# suppose mydata is a list of tuples containing (xs, ys, z) 
# where xs and ys are lists of x's and y's and z is a number. 
legns = []
for(xs,ys,z) in mydata:
   pl = ax.plot(xs,ys,color = (z,0,0))
   legns.append("z = %f"%(z))
ax.legends(legns) 
plt.show()

Но у меня слишком много графиков, и легенды покроют график. Я предпочел бы иметь цветную полосу, указывающую значение z, соответствующее цвету. Я не могу найти ничего подобного в галерее, и все мои попытки справиться с цветовой шкалой провалились. По-видимому, я должен создать коллекцию графиков, прежде чем пытаться добавить цветовую панель.

Есть ли простой способ сделать это? Спасибо.

РЕДАКТИРОВАТЬ (уточнение):

Я хотел сделать что-то вроде этого:

import matplotlib.pyplot as plt
import matplotlib.cm     as cm

fig = plt.figure()
ax  = fig.add_subplot(111)
mycmap = cm.hot
# suppose mydata is a list of tuples containing (xs, ys, z) 
# where xs and ys are lists of x's and y's and z is a number between 0 and 1
plots = []
for(xs,ys,z) in mydata:
   pl = ax.plot(xs,ys,color = mycmap(z))
   plots.append(pl)
fig.colorbar(plots)
plt.show()

Но это не будет работать в соответствии со ссылкой на Matplotlib, потому что список графиков не является "отображаемым", что бы это ни значило.

Я создал альтернативную функцию построения, используя LineCollection:

def myplot(ax,xs,ys,zs, cmap):
    plot = lc([zip(x,y) for (x,y) in zip(xs,ys)], cmap = cmap)
    plot.set_array(array(zs))
    x0,x1 = amin(xs),amax(xs)
    y0,y1 = amin(ys),amax(ys)
    ax.add_collection(plot)
    ax.set_xlim(x0,x1)
    ax.set_ylim(y0,y1)
    return plot

xs и ys - списки списков координат x и y, а zs - список различных условий для раскрашивания каждой строки. Хотя это немного похоже на комок ... Я думал, что есть более изящный способ сделать это. Мне нравится гибкость функции plt.plot().

Ответы [ 4 ]

104 голосов
/ 19 июля 2012

(я знаю, что это старый вопрос, но ...) Для цветных полос требуется matplotlib.cm.ScalarMappable, plt.plot создает линии, которые не отображаются скалярно, поэтому для создания цветовой полосы нам понадобится скалярное отображение.

Ok. Таким образом, конструктор ScalarMappable принимает экземпляры cmap и norm. (Нормы масштабируют данные до 0-1, cmaps, с которыми вы уже работали, принимают число от 0 до 1 и возвращают цвет). Итак, в вашем случае:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(min=0, max=1))
plt.colorbar(sm)

Поскольку ваши данные уже находятся в диапазоне 0-1, вы можете упростить создание sm до:

sm = plt.cm.ScalarMappable(cmap=my_cmap)

Надеюсь, это кому-нибудь поможет.

РЕДАКТИРОВАТЬ : Для matplotlib v1.2 или выше код становится:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)

РЕДАКТИРОВАТЬ : Для matplotlib v1.3 или выше код становится:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)

РЕДАКТИРОВАТЬ : Для matplotlib v3.1 или выше упрощается до:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
plt.colorbar(sm)
34 голосов
/ 03 декабря 2011

Вот один из способов сделать это, все еще используя plt.plot ().По сути, вы делаете одноразовый график и получаете оттуда цветную полосу.

import matplotlib as mpl
import matplotlib.pyplot as plt

min, max = (-40, 30)
step = 10

# Setting up a colormap that's a simple transtion
mymap = mpl.colors.LinearSegmentedColormap.from_list('mycolors',['blue','red'])

# Using contourf to provide my colorbar info, then clearing the figure
Z = [[0,0],[0,0]]
levels = range(min,max+step,step)
CS3 = plt.contourf(Z, levels, cmap=mymap)
plt.clf()

# Plotting what I actually want
X=[[1,2],[1,2],[1,2],[1,2]]
Y=[[1,2],[1,3],[1,4],[1,5]]
Z=[-40,-20,0,30]
for x,y,z in zip(X,Y,Z):
    # setting rgb color based on z normalized to my range
    r = (float(z)-min)/(max-min)
    g = 0
    b = 1-r
    plt.plot(x,y,color=(r,g,b))
plt.colorbar(CS3) # using the colorbar info I got from contourf
plt.show()

Это немного расточительно, но удобно.Это также не очень расточительно, если вы создаете несколько графиков, так как вы можете вызвать plt.colorbar () без регенерации информации для него.

enter image description here

13 голосов
/ 09 марта 2018

Вот несколько упрощенный пример, вдохновленный главным ответом, данным Борисом и Крюком (Спасибо за отличную идею!):

1 Дискретная цветовая полоса

Дискретная цветовая полоса более сложна, потому что цветовая карта, генерируемая mpl.cm.get_cmap(), не является отображаемым изображением, необходимым в качестве аргумента colorbar().Необходимо создать мапсибель, как показано ниже:

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

n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)

cmap = mpl.cm.get_cmap('jet', n_lines)

fig, ax = plt.subplots(dpi=100)
# Make dummie mappable
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
# Clear axis
ax.cla()
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
fig.colorbar(dummie_cax, ticks=c)
plt.show();

Это создаст график с дискретной цветовой шкалой: enter image description here


2. Непрерывная цветовая полоса

Непрерывная цветовая полоса менее задействована, так как mpl.cm.ScalarMappable() позволяет нам получить «изображение» для colorbar().

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


n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)

norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
cmap.set_array([])

fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show();

В результате будет получен график с непрерывной цветной полосой: enter image description here

[Примечание] В этом примере я личноне знаю, почему cmap.set_array([]) необходимо (иначе мы получили бы сообщения об ошибках).Если кто-то понимает принципы под капотом, пожалуйста, прокомментируйте:)

5 голосов
/ 20 марта 2018

Как и другие ответы здесь, попробуйте использовать фиктивные графики, что не очень хороший стиль, вот общий код для

Дискретная цветовая полоса

Дискретная цветовая полоса создается в той жеспособ создания непрерывной цветовой панели, просто с другой нормализацией.В этом случае следует использовать BoundaryNorm.

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

n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1., n_lines + 1)

cmap = plt.get_cmap("jet", len(c))
norm = matplotlib.colors.BoundaryNorm(np.arange(len(c)+1)+0.5,len(c))
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])  # this line may be ommitted for matplotlib >= 3.1

fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
fig.colorbar(sm, ticks=c)
plt.show()

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...