Как проверить, существует ли colorbar на рисунке


Вопрос: Есть ли способ проверить, существует ли уже цветовая полоса?

Я делаю много сюжетов с петлей. Проблема в том, что цветовая полоса рисуется каждую итерацию!

Введите описание изображения здесь

Если бы я мог определить, существует ли цветовая полоса, то я мог бы поместить функцию цветовой полосы в Оператор if.

if cb_exists:
    # do nothing
else:
    plt.colorbar() #draw the colorbar

Если я использую мультипроцессорную обработку для создания фигур, можно ли предотвратить добавление нескольких цветных полос?

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

def plot(number):
    a = np.random.random([5,5])*number
    plt.pcolormesh(a)
    plt.colorbar()
    plt.savefig('this_'+str(number))

# I want to make a 50 plots
some_list = range(0,50)
num_proc = 5
p = multiprocessing.Pool(num_proc)
temps = p.map(plot, some_list)

Введите описание изображения здесь

Я понимаю, что могу очистить фигуру с помощью plt.clf () и plt.cla () перед построением следующей итерации. Но у меня есть данные на слое базовой карты, которые я не хочу перерисовывать (это увеличивает время, необходимое для создания графика). Так что, если бы я мог удалить цветовую панель и добавить новую, я бы сэкономил немного времени.

3 5

3 ответа:

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

Здесь мы также учитываем желание пользователя не ссылаться на какие-либо именованные объекты извне. (Что не имеет особого смысла, поскольку нам все равно нужно использовать plt, но эй.. таким был и вопрос)

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
im = ax.pcolormesh(np.array(np.random.rand(2,2) ))
ax.plot(np.cos(np.linspace(0.2,1.8))+0.9, np.sin(np.linspace(0.2,1.8))+0.9, c="k", lw=6)
ax.set_title("Title")
cbar = plt.colorbar(im)
cbar.ax.set_ylabel("Label")


for i in range(10):
    # inside this loop we should not access any variables defined outside
    #   why? no real reason, but questioner asked for it.
    #draw new colormesh
    im = plt.gcf().gca().pcolormesh(np.random.rand(2,2))
    #check if there is more than one axes
    if len(plt.gcf().axes) > 1: 
        # if so, then the last axes must be the colorbar.
        # we get its extent
        pts = plt.gcf().axes[-1].get_position().get_points()
        # and its label
        label = plt.gcf().axes[-1].get_ylabel()
        # and then remove the axes
        plt.gcf().axes[-1].remove()
        # then we draw a new axes a the extents of the old one
        cax= plt.gcf().add_axes([pts[0][0],pts[0][1],pts[1][0]-pts[0][0],pts[1][1]-pts[0][1]  ])
        # and add a colorbar to it
        cbar = plt.colorbar(im, cax=cax)
        cbar.ax.set_ylabel(label)
        # unfortunately the aspect is different between the initial call to colorbar 
        #   without cax argument. Try to reset it (but still it's somehow different)
        cbar.ax.set_aspect(20)
    else:
        plt.colorbar(im)

plt.show()

В общем случае гораздо лучшим решением было бы оперировать объектами, уже присутствующими на графике, и только обновлять их новыми данными. Таким образом, мы подавляем необходимость удалять и добавлять оси и находим гораздо более чистое и быстрое решение.
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
im = ax.pcolormesh(np.array(np.random.rand(2,2) ))
ax.plot(np.cos(np.linspace(0.2,1.8))+0.9, np.sin(np.linspace(0.2,1.8))+0.9, c="k", lw=6)
ax.set_title("Title")
cbar = plt.colorbar(im)
cbar.ax.set_ylabel("Label")


for i in range(10):
    data = np.array(np.random.rand(2,2) )
    im.set_array(data.flatten())
    cbar.set_clim(vmin=data.min(),vmax=data.max()) 
    cbar.draw_all() 
    plt.draw()

plt.show()


Обновление:

На самом деле, последний подход привязки объектов извне даже работает вместе с подходом multiprocess, желаемым спрашивающим.

Итак, вот код, который обновляет рисунок, без необходимости удалять цветовую панель.

import matplotlib.pyplot as plt
import numpy as np
import multiprocessing
import time

fig, ax = plt.subplots()
im = ax.pcolormesh(np.array(np.random.rand(2,2) ))
ax.plot(np.cos(np.linspace(0.2,1.8))+0.9, np.sin(np.linspace(0.2,1.8))+0.9, c="w", lw=6)
ax.set_title("Title")
cbar = plt.colorbar(im)
cbar.ax.set_ylabel("Label")
tx = ax.text(0.2,0.8, "", fontsize=30, color="w")
tx2 = ax.text(0.2,0.2, "", fontsize=30, color="w")

def do(number):
    start = time.time()
    tx.set_text(str(number))
    data = np.array(np.random.rand(2,2)*(number+1) )
    im.set_array(data.flatten())
    cbar.set_clim(vmin=data.min(),vmax=data.max()) 
    tx2.set_text("{m:.2f} < {ma:.2f}".format(m=data.min(), ma= data.max() )) 
    cbar.draw_all() 
    plt.draw()
    plt.savefig("multiproc/{n}.png".format(n=number))
    stop = time.time()

    return np.array([number, start, stop])


if __name__ == "__main__":
    multiprocessing.freeze_support()

    some_list = range(0,50)
    num_proc = 5
    p = multiprocessing.Pool(num_proc)
    nu = p.map(do, some_list)
    nu = np.array(nu)

    plt.close("all")
    fig, ax = plt.subplots(figsize=(16,9))
    ax.barh(nu[:,0], nu[:,2]-nu[:,1], height=np.ones(len(some_list)), left=nu[:,1],  align="center")
    plt.show()

(код в конце показывает расписание, которое позволяет увидеть, что многопроцессорная обработка действительно имела место)

Один из подходов таков:

  1. Первоначально (до того, как будет нарисована любая цветовая полоса) задайте переменную

    colorBarPresent = False
    
  2. В методе рисования цветовой полосы проверьте, не нарисована ли она уже. Если нет, нарисуйте его и установите переменную colorBarPresent True:

    def drawColorBar():
        if colorBarPresent:
            # leave the function and don't draw the bar again
        else:
            # draw  the color bar
            colorBarPresent = True
    

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

После предыдущего ответа (Как получить экземпляр colorbar из figure в matplotlib ), примером может быть:

ax=plt.gca()        #plt.gca() for current axis, otherwise set appropriately.
im=ax.images        #this is a list of all images that have been plotted
if im[-1].colorbar is None:   #in this case I assume to be interested to the last one plotted, otherwise use the appropriate index or loop over
    plt.colorbar() #plot a new colorbar

Обратите внимание, что изображение без colorbar возвращает None в im[-1].colorbar