¿Hay alguna manera de separar los gráficos de matplotlib para que el cálculo pueda continuar?

StackOverflow https://stackoverflow.com/questions/458209

  •  19-08-2019
  •  | 
  •  

Pregunta

Después de estas instrucciones en el intérprete de Python, se obtiene una ventana con una trama:

from matplotlib.pyplot import *
plot([1,2,3])
show()
# other code

Desafortunadamente, no sé cómo continuar explorando interactivamente la figura creada por show () mientras el programa realiza más cálculos.

¿Es posible en absoluto? A veces, los cálculos son largos y ayudaría si procedieran durante el examen de los resultados intermedios.

¿Fue útil?

Solución

Use las llamadas de matplotlib que no bloquearán:

Uso de draw () :

from matplotlib.pyplot import plot, draw, show
plot([1,2,3])
draw()
print 'continue computation'

# at the end call show to ensure window won't close.
show()

Uso del modo interactivo:

from matplotlib.pyplot import plot, ion, show
ion() # enables interactive mode
plot([1,2,3]) # result shows immediatelly (implicit draw())

print 'continue computation'

# at the end call show to ensure window won't close.
show()

Otros consejos

Use la palabra clave 'bloquear' para anular el comportamiento de bloqueo, p.

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

para continuar su código.

Es mejor consultar siempre con la biblioteca que está utilizando si admite el uso de una manera sin bloqueo .

Pero si desea una solución más genérica, o si no hay otra forma, puede ejecutar cualquier cosa que bloquee en un proceso separado utilizando módulo multiprocesamiento incluido en python. La computación continuará:

from multiprocessing import Process
from matplotlib.pyplot import plot, show

def plot_graph(*args):
    for data in args:
        plot(data)
    show()

p = Process(target=plot_graph, args=([1, 2, 3],))
p.start()

print 'yay'
print 'computation continues...'
print 'that rocks.'

print 'Now lets wait for the graph be closed to continue...:'
p.join()

Eso tiene la sobrecarga de lanzar un nuevo proceso, y a veces es más difícil de depurar en escenarios complejos, por lo que preferiría la otra solución (usando matplotlib 's no bloquear llamadas de API )

Probar

from matplotlib.pyplot import *
plot([1,2,3])
show(block=False)
# other code
# [...]

# Put
show()
# at the very end of your script
# to make sure Python doesn't bail out
# before you finished examining.

La show () documentación dice:

  

En modo no interactivo, muestre todas las figuras y bloquee hasta que se hayan cerrado las figuras; en modo interactivo no tiene efecto a menos que las figuras se hayan creado antes de un cambio de modo no interactivo a modo interactivo (no recomendado). En ese caso, muestra las figuras pero no bloquea.

     

Un único argumento experimental de palabra clave, block , puede establecerse en True o False para anular el comportamiento de bloqueo descrito anteriormente.

Es posible que desee leer este documento en la documentación de matplotlib , titulada:

Usando matplotlib en un shell de python

IMPORTANTE : solo para aclarar algo. Supongo que los comandos están dentro de un script .py y se llama al script usando p. python script.py desde la consola.

Una forma simple que funciona para mí es:

  1. Use el bloque = False dentro del espectáculo: plt.show (block = False)
  2. Use otro show () al final del script .py.

Ejemplo de script.py archivo:

plt.imshow(*something*)                                                               
plt.colorbar()                                                                             
plt.xlabel("true ")                                                                   
plt.ylabel("predicted ")                                                              
plt.title(" the matrix")  

# Add block = False                                           
plt.show(block = False)

################################
# OTHER CALCULATIONS AND CODE HERE ! ! !
################################

# the next command is the last line of my script
plt.show()

En mi caso, quería que aparecieran varias ventanas mientras se están calculando. Como referencia, esta es la forma:

from matplotlib.pyplot import draw, figure, show
f1, f2 = figure(), figure()
af1 = f1.add_subplot(111)
af2 = f2.add_subplot(111)
af1.plot([1,2,3])
af2.plot([6,5,4])
draw() 
print 'continuing computation'
show()

PS. Una guía de la interfaz OO de matplotlib bastante útil .

Bueno, tuve grandes problemas para descifrar los comandos que no bloquean ... Pero finalmente, logré reelaborar el " Libro de cocina / Matplotlib / Animaciones - Animación de elementos seleccionados de la trama " ejemplo, por lo que funciona con subprocesos ( y pasa datos entre subprocesos a través de variables globales o mediante un Pipe multiproceso en Python 2.6.5 en Ubuntu 10.04.

El script se puede encontrar aquí: Animating_selected_plot_elements-thread.py - de lo contrario se pega a continuación ( con menos comentarios ) para referencia:

import sys
import gtk, gobject
import matplotlib
matplotlib.use('GTKAgg')
import pylab as p
import numpy as nx 
import time

import threading 



ax = p.subplot(111)
canvas = ax.figure.canvas

# for profiling
tstart = time.time()

# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = ax.plot(x, nx.sin(x), animated=True)

# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)


# just a plain global var to pass data (from main, to plot update thread)
global mypass

# http://docs.python.org/library/multiprocessing.html#pipes-and-queues
from multiprocessing import Pipe
global pipe1main, pipe1upd
pipe1main, pipe1upd = Pipe()


# the kind of processing we might want to do in a main() function,
# will now be done in a "main thread" - so it can run in
# parallel with gobject.idle_add(update_line)
def threadMainTest():
    global mypass
    global runthread
    global pipe1main

    print "tt"

    interncount = 1

    while runthread: 
        mypass += 1
        if mypass > 100: # start "speeding up" animation, only after 100 counts have passed
            interncount *= 1.03
        pipe1main.send(interncount)
        time.sleep(0.01)
    return


# main plot / GUI update
def update_line(*args):
    global mypass
    global t0
    global runthread
    global pipe1upd

    if not runthread:
        return False 

    if pipe1upd.poll(): # check first if there is anything to receive
        myinterncount = pipe1upd.recv()

    update_line.cnt = mypass

    # restore the clean slate background
    canvas.restore_region(background)
    # update the data
    line.set_ydata(nx.sin(x+(update_line.cnt+myinterncount)/10.0))
    # just draw the animated artist
    ax.draw_artist(line)
    # just redraw the axes rectangle
    canvas.blit(ax.bbox)

    if update_line.cnt>=500:
        # print the timing info and quit
        print 'FPS:' , update_line.cnt/(time.time()-tstart)

        runthread=0
        t0.join(1)   
        print "exiting"
        sys.exit(0)

    return True



global runthread

update_line.cnt = 0
mypass = 0

runthread=1

gobject.idle_add(update_line)

global t0
t0 = threading.Thread(target=threadMainTest)
t0.start() 

# start the graphics update thread
p.show()

print "out" # will never print - show() blocks indefinitely! 

Espero que esto ayude a alguien,
¡Saludos!

En muchos casos, es más conveniente guardar la imagen como un archivo .png en el disco duro. Aquí es por qué:

Ventajas :

  • Puede abrirlo, echarle un vistazo y cerrarlo en cualquier momento del proceso. Esto es particularmente conveniente cuando su aplicación se ejecuta durante mucho tiempo tiempo.
  • No aparece nada y no está obligado a abrir las ventanas. Esto es particularmente conveniente cuando se trata de muchas figuras.
  • Se puede acceder a su imagen para referencia posterior y no se pierde al cerrar la ventana de la figura.

Drawback :

  • Lo único que se me ocurre es que tendrá que ir a buscar la carpeta y abrir la imagen usted mismo.

Si está trabajando en la consola, es decir, IPython puede usar plt.show (block = False) como se indica en las otras respuestas. Pero si eres flojo, puedes escribir:

plt.show(0)

Que será lo mismo.

También tuve que agregar plt.pause (0.001) a mi código para que realmente funcione dentro de un bucle for (de lo contrario, solo mostraría el primer y el último diagrama):

import matplotlib.pyplot as plt

plt.scatter([0], [1])
plt.draw()
plt.show(block=False)

for i in range(10):
    plt.scatter([i], [i+1])
    plt.draw()
    plt.pause(0.001)

También quería que se mostraran mis gráficos para ejecutar el resto del código (y luego seguir mostrando) incluso si hay un error (a veces uso gráficos para la depuración). Codifiqué este pequeño truco para que cualquier diagrama dentro de esta instrucción con se comporte como tal.

Esto es probablemente un poco no estándar y no es aconsejable para el código de producción. Probablemente hay muchas "trampas" ocultas en este código.

from contextlib import contextmanager

@contextmanager
def keep_plots_open(keep_show_open_on_exit=True, even_when_error=True):
    '''
    To continue excecuting code when plt.show() is called
    and keep the plot on displaying before this contex manager exits
    (even if an error caused the exit).
    '''
    import matplotlib.pyplot
    show_original = matplotlib.pyplot.show
    def show_replacement(*args, **kwargs):
        kwargs['block'] = False
        show_original(*args, **kwargs)
    matplotlib.pyplot.show = show_replacement

    pylab_exists = True
    try:
        import pylab
    except ImportError: 
        pylab_exists = False
    if pylab_exists:
        pylab.show = show_replacement

    try:
        yield
    except Exception, err:
        if keep_show_open_on_exit and even_when_error:
            print "*********************************************"
            print "Error early edition while waiting for show():" 
            print "*********************************************"
            import traceback
            print traceback.format_exc()
            show_original()
            print "*********************************************"
            raise
    finally:
        matplotlib.pyplot.show = show_original
        if pylab_exists:
            pylab.show = show_original
    if keep_show_open_on_exit:
        show_original()

# ***********************
# Running example
# ***********************
import pylab as pl
import time
if __name__ == '__main__':
    with keep_plots_open():
        pl.figure('a')
        pl.plot([1,2,3], [4,5,6])     
        pl.plot([3,2,1], [4,5,6])
        pl.show()

        pl.figure('b')
        pl.plot([1,2,3], [4,5,6])
        pl.show()

        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        this_will_surely_cause_an_error

Si / cuando implemente un correcto "mantenga los gráficos abiertos (incluso si se produce un error) y permita que se muestren nuevos gráficos", me gustaría que el script salga correctamente si ninguna interferencia del usuario lo indica de otra manera (para el lote fines de ejecución).

¡Puedo usar algo como una pregunta de tiempo de espera " Fin del guión! \ nPresione p si desea pausar la salida de trazado (tiene 5 segundos): " de https://stackoverflow.com/questions/26704840 / corner-cases-for-my-wait-for-user-input-interruption-implementación .

En mi sistema, show () no se bloquea, aunque quería que el script espere a que el usuario interactúe con el gráfico (y recopile datos utilizando las devoluciones de llamada 'pick_event') antes de continuar.

Para bloquear la ejecución hasta que se cierre la ventana de trazado, utilicé lo siguiente:

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)

# set processing to continue when window closed
def onclose(event):
    fig.canvas.stop_event_loop()
fig.canvas.mpl_connect('close_event', onclose)

fig.show() # this call does not block on my system
fig.canvas.start_event_loop_default() # block here until window closed

# continue with further processing, perhaps using result from callbacks

Tenga en cuenta, sin embargo, que canvas.start_event_loop_default () produjo la siguiente advertencia:

C:\Python26\lib\site-packages\matplotlib\backend_bases.py:2051: DeprecationWarning: Using default event loop until function specific to this GUI is implemented
  warnings.warn(str,DeprecationWarning)

aunque el script aún se ejecutaba.

plt.figure(1)
plt.imshow(your_first_image)

plt.figure(2)
plt.imshow(your_second_image)

plt.show(block=False) # That's important 

raw_input("Press ENTER to exist") # Useful when you run your Python script from the terminal and you want to hold the running to see your figures until you press Enter

En mi opinión, las respuestas en este hilo proporcionan métodos que no funcionan para todos los sistemas y en situaciones más complejas como las animaciones. Sugiero echar un vistazo a la respuesta de MiKTeX en el siguiente hilo, donde se ha encontrado un método robusto: ¿Cómo esperar hasta que termine la animación de matplotlib?

Si desea abrir varias figuras, mientras las mantiene abiertas, este código funcionó para mí:

show(block=False)
draw()

El OP pregunta sobre la separación de los gráficos matplotlib . La mayoría de las respuestas asumen la ejecución del comando desde un intérprete de Python. El caso de uso presentado aquí es mi preferencia para probar el código en un terminal (por ejemplo, bash) donde se ejecuta un file.py y desea que aparezcan los plots pero el script de Python se complete y volver a un símbolo del sistema.

Este archivo independiente utiliza multiprocesamiento para iniciar un proceso separado para trazar datos con matplotlib . El subproceso principal sale utilizando el os._exit (1) mencionado en esta publicación. El os._exit () obliga a main a salir pero deja el proceso hijo matplotlib vivo y receptivo hasta que se cierra la ventana de trazado. Es un proceso completamente separado.

Este enfoque es un poco como una sesión de desarrollo de Matlab con ventanas de figura que aparecen con un símbolo del sistema receptivo. Con este enfoque, ha perdido todo contacto con el proceso de ventana de figura, pero está bien para el desarrollo y la depuración. Simplemente cierre la ventana y siga probando.

El

multiprocesamiento está diseñado para la ejecución de código solo en python, lo que lo hace quizás más adecuado que el subproceso . multiprocessing es multiplataforma, por lo que debería funcionar bien en Windows o Mac con poco o ningún ajuste. No es necesario verificar el sistema operativo subyacente. Esto fue probado en Linux, Ubuntu 18.04LTS.

#!/usr/bin/python3

import time
import multiprocessing
import os

def plot_graph(data):
    from matplotlib.pyplot import plot, draw, show
    print("entered plot_graph()")
    plot(data)
    show() # this will block and remain a viable process as long as the figure window is open
    print("exiting plot_graph() process")

if __name__ == "__main__":
    print("starting __main__")
    multiprocessing.Process(target=plot_graph, args=([1, 2, 3],)).start()
    time.sleep(5)
    print("exiting main")
    os._exit(0) # this exits immediately with no cleanup or buffer flushing

Ejecutar file.py muestra una ventana de figura, luego __main__ sale pero la figura multiprocesamiento + matplotlib La ventana permanece receptiva con zoom, panorámica y otros botones porque es un proceso independiente.

Verifique los procesos en el símbolo del sistema bash con:

ps ax | grep -v grep | grep file.py

Use plt.show (block = False) , y al final de su script llame a plt.show () .

Esto garantizará que la ventana no se cierre cuando finalice el script.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top