Plotagem de maneira não obstrutiva com o Matplotlib


138

Eu tenho jogado com Numpy e matplotlib nos últimos dias. Estou tendo problemas ao tentar fazer do matplotlib plot uma função sem bloquear a execução. Eu sei que já existem muitos tópicos aqui no SO, fazendo perguntas semelhantes, e eu pesquisei bastante no Google, mas não consegui fazer isso funcionar.

Eu tentei usar show (block = False) como algumas pessoas sugerem, mas tudo o que recebo é uma janela congelada. Se eu simplesmente chamar show (), o resultado será plotado corretamente, mas a execução será bloqueada até que a janela seja fechada. De outros tópicos que li, suspeito que o show (block = False) funcione ou não, dependendo do back-end. Isso está correto? Meu back-end é Qt4Agg. Você poderia dar uma olhada no meu código e me dizer se vê algo errado? Aqui está o meu código. Obrigado por qualquer ajuda.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

PS. Esqueci de dizer que gostaria de atualizar a janela existente toda vez que plotar algo, em vez de criar uma nova.


1
tem de tentar modo matplotlib interativa com plt.ion()antes plt.show()? Em seguida, deve ser sem bloqueio, pois cada plot é gerado em um thread filho.
Anzel

@Anzel Acabei de experimentar, mas parece não fazer diferença.
Opetroch

3
Como você está executando seu script? Se eu executar o seu código de exemplo no terminal / prompt de comando, ele parece funcionar bem, mas acho que tive problemas no passado ao tentar fazer coisas como essa no IPython QtConsole ou IDEs.
Marius

1
@Marius Aha !! Você está certo. Na verdade, estou executando-o no console do meu IDE (PyCharm). Ao executá-lo no prompt do cmd, plt.show (block = False) funciona bem! Estarei perguntando demais se eu perguntar se você encontrou alguma idéia / solução para isso? Muito obrigado!
opetroch 02/02

Eu realmente não sinto muito. Eu realmente não entendo os detalhes de como o matplotlib interage com o console; portanto, geralmente apenas mudo para a execução no prompt de comando se precisar fazer isso matplotlib.
Marius

Respostas:


167

Passei muito tempo procurando soluções e encontrei esta resposta .

Parece que, a fim de obter o que você (e eu) quer, você precisa a combinação de plt.ion(), plt.show()(não com block=False) e, mais importante, plt.pause(.001)(ou o tempo que você quiser). A pausa é necessária porque os eventos da GUI acontecem enquanto o código principal está em suspensão, incluindo o desenho. É possível que isso seja implementado com a obtenção de tempo em um encadeamento adormecido, então talvez os IDEs mexam com isso - eu não sei.

Aqui está uma implementação que funciona para mim no python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()

3
Sua resposta me ajudou muito a resolver um problema semelhante que eu estava tendo. Anteriormente, eu plt.drawsegui, plt.show(block = False)mas ele parou de funcionar: a figura não respondeu, fechando-o travou o iPython. Minha solução foi remover todas as instâncias plt.draw()e substituí-las por plt.pause(0.001). Em vez de tê-lo seguido de plt.show(block = False)como plt.drawera antes, foi precedido por plt.ion()e plt.show(). Agora tenho um, MatplotlibDeprecationWarningmas deixe-me traçar meus números, por isso estou feliz com esta solução.
blue_chip 8/03/16

3
Note-se que em Python 2.7, você precisa usar raw_inputnão input. Veja aqui
Chris

Solução realmente útil quando a abordagem "animada" reativa não é possível! Alguém sabe como se livrar do aviso de reprovação?
Frederic Fortier

Alguém pode me dizer por que recebo um prompt de comando freezen quando tento adicionar o plt.ion antes do plt.show?
Gabriel Augusto

@ GabrielAugusto Não tenho certeza do que poderia causar isso, e não tenho certeza do que você quer dizer. Acabei de testar este exemplo no Python 3.6 e ele ainda funciona. Se você usou o mesmo padrão e ele congela, pode haver algo errado com sua instalação. Você deve verificar se a plotagem normal funciona primeiro. Se você tentou algo diferente, não há muito o que fazer nos comentários. Em qualquer um dos casos, considere fazer uma pergunta separada.
krs013

23

Um truque simples que funciona para mim é o seguinte:

  1. Use o argumento block = False dentro de show: plt.show (block = False)
  2. Use outro plt.show () no final do script .py.

Exemplo :

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Nota : plt.show()é a última linha do meu script.


7
Isso produz (para mim, no Linux, Anaconda, Python 2.7, back-end padrão) uma janela em branco que permanece em branco até o final da execução, quando finalmente é preenchida. Não é útil para atualizar um gráfico no meio da execução. :-(
sh37211 4/17

@ sh37211 Não tenho certeza qual é seu objetivo. Em alguns casos, você tenta plotar algo, mas após o comando plotar você tem outros comandos, isso é útil, pois permite plotar e executar os outros comandos. Consulte esta publicação para obter mais informações sobre isso: stackoverflow.com/questions/458209/… . Se você deseja atualizar um gráfico, deve ser outra maneira.
Seralouk 4/17

17

Você pode evitar o bloqueio da execução gravando o gráfico em uma matriz e exibindo a matriz em um encadeamento diferente. Aqui está um exemplo de geração e exibição de gráficos simultaneamente usando pf.screen a partir de pyformulas 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Resultado:

Animação senoidal

Disclaimer: Eu sou o mantenedor de pyformulas.

Referência: Matplotlib: salvar plot para numpy array


9

Muitas dessas respostas são superinfladas e, pelo que pude encontrar, a resposta não é tão difícil de entender.

Você pode usar plt.ion()se quiser, mas eu achei o uso plt.draw()tão eficaz

Para o meu projeto específico, estou plotando imagens, mas você pode usar plot()ou o que scatter()quer que seja figimage(), não importa.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Ou

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Se você estiver usando uma figura real.
Eu usei o @ krs013 e as respostas do @Default Picture para descobrir isso.
Espero que isso evite que alguém lance cada figura em um tópico separado ou que precise ler esses romances apenas para descobrir isso.


3

Plotagem ao vivo

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

se a quantidade de dados for demais, você pode diminuir a taxa de atualização com um contador simples

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Retendo a plotagem após a saída do programa

Esse era o meu problema real que não conseguia encontrar uma resposta satisfatória. Queria traçar que não fechasse após a conclusão do script (como MATLAB),

Se você pensar bem, depois que o script terminar, o programa será encerrado e não haverá uma maneira lógica de manter o gráfico dessa maneira, portanto, existem duas opções

  1. bloquear o script de sair (isso é plt.show () e não é o que eu quero)
  2. executar o gráfico em um thread separado (muito complicado)

isso não foi satisfatório para mim, então encontrei outra solução fora da caixa

SaveToFile e View no visualizador externo

Para isso, o salvamento e a visualização devem ser rápidos e o visualizador não deve bloquear o arquivo e atualizar o conteúdo automaticamente

Selecionando o formato para salvar

formatos baseados em vetor são pequenos e rápidos

  • O SVG é bom, mas não pode encontrar um bom visualizador, exceto o navegador da Web que, por padrão, precisa de atualização manual
  • O PDF pode suportar formatos vetoriais e existem visualizadores leves que suportam atualizações ao vivo

Visualizador leve e rápido com atualização ao vivo

Para PDF, existem várias boas opções

  • No Windows, uso o SumatraPDF , gratuito, rápido e leve (usa apenas 1,8 MB de RAM no meu caso)

  • No Linux, existem várias opções, como Evince (GNOME) e Ocular (KDE)

Código de exemplo e resultados

Código de amostra para saída da plotagem em um arquivo

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

após a primeira execução, abra o arquivo de saída em um dos visualizadores mencionados acima e aproveite.

Aqui está uma captura de tela do VSCode ao lado do SumatraPDF, também o processo é rápido o suficiente para obter uma taxa de atualização semi-ao vivo (posso chegar perto de 10Hz na minha configuração, basta usar time.sleep()entre intervalos) pyPlot, sem bloqueio


2

A resposta de Iggy foi a mais fácil para eu seguir, mas recebi o seguinte erro ao executar um subplotcomando subseqüente que não estava lá quando eu estava apenas fazendo show:

MatplotlibDeprecationWarning: a adição de eixos usando os mesmos argumentos dos eixos anteriores atualmente reutiliza a instância anterior. Em uma versão futura, uma nova instância sempre será criada e retornada. Enquanto isso, esse aviso pode ser suprimido e o comportamento futuro garantido, passando um rótulo exclusivo para cada instância de eixos.

Para evitar esse erro, ajuda a fechar (ou limpar ) a plotagem depois que o usuário pressiona enter.

Aqui está o código que funcionou para mim:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()

0

O pacote Python drawow permite atualizar um gráfico em tempo real de maneira não-bloqueadora.
Também funciona com uma webcam e OpenCV, por exemplo, para traçar medidas para cada quadro.
Veja a postagem original .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.