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

258

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 hace 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.

meteore
fuente
55
No puedo confirmar que la solución seleccionada de nosklo a las 16:52 esté funcionando. Para mí, draw no abre una ventana para mostrar la trama, solo la demostración de bloqueo al final muestra la solución. Sin embargo, su respuesta a partir de las 17:00 es correcta. Activar el modo interactivo a través de ion()soluciona el problema.
H. Brandsmeier
si eres un programador avanzado, puedes usarlo, os.fork()pero os.fork()ten en cuenta que usarlo puede ser complicado porque estás creando un nuevo proceso copiando el proceso anterior.
Trevor Boyd Smith el

Respuestas:

214

Use matplotliblas llamadas que no bloquearán:

Utilizando 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()

Usando el 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()
nosklo
fuente
28
Con matplotlib 0.98.3, la importación correcta es de matplotlib.pyplot import plot, draw, show
meteore
112
draw()no funciona para mí, no abre ninguna ventana. Sin embargo, usar en show(block=False)lugar de draw()parece hacer el truco en matplotlib 1.1.
Rumpel
44
@nosklo, ¿viste? Lo convertiste en un tutorial de
Jan
44
@noskolo, ¿qué pasa si tengo varias figuras, cómo trazar y mostrar la Fig1 mientras continúo con el fondo para continuar? Me gustaría que esta figura esté abierta hasta que se genere el siguiente higo, así que al final tengo todos los higos abiertos y el código está terminado. Con su solución actual, me hace esperar para cerrar la Fig1 y luego el código continúa. ¡¡Gracias!!
Físico
9
draw()tampoco funcionó para mí, solo lo pause(0.001)hizo: stackoverflow.com/questions/28269157/…
NumesSanguis
133

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

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

para continuar tu código.

ene
fuente
17
pero esto cerrará la ventana de trazado inmediatamente, no mantendrá el trazado abierto.
LWZ
8
Sí, eso es cierto si llamas a tu script desde la línea de comandos. Si está en el shell Ipython, la ventana no se cerrará.
Jan
1
revise la respuesta de @Nico para un truco que dejará la ventana abierta en el caso general.
Jan
2
Para mí, esto no cierra la ventana de inmediato, solo cuando el script está terminado (por lo que puede bloquear manualmente al final del script si desea que permanezca abierto).
luator
Sí, las ventanas no bloqueadas se cerrarán cuando salga el script . Puede (a) permitir el bloqueo en su última trama, o (b) no salir del guión (tal vez solicitar entrada: "presione <Intro> para salir de la trama" o algo así).
Daniel Goldfarb
29

Es mejor consultar siempre con la biblioteca que está utilizando si admite el uso 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 el multprocessingmódulo 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 (usar matplotliblas llamadas API sin bloqueo )

nosklo
fuente
¡Gracias! Como todavía no tengo Python 2.6 en mi sistema, utilicé threading. Thread como sustituto de Process. Observé que las declaraciones de impresión posteriores se vuelven insoportablemente lentas (tercera impresión, emití KeyboardInterrupt después de 1 minuto de espera). ¿Es este un efecto de usar subprocesos en lugar de multiprocesamiento?
meteore
@meteore: Sí, el subproceso apesta. Siempre puede obtener multiprocesamiento para python <2.6 desde aquí: pyprocessing.berlios.de
nosklo
Esto es absolutamente excelente. ¿Tiene alguna idea de por qué las declaraciones de impresión no se ejecutan cuando está en Emacs (modo python) hasta que se cierra la ventana de trazado?
meteore
En Ubuntu 8.10 (Intrepid), el paquete (para python <2.6) se llama procesamiento de python y lo importa con 'procesamiento de importación'
meteore
1
¿No te perdiste el if __name__ == '__main__':?
Wernight
25

Tratar

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

# Put
plt.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 solo argumento de palabra clave experimental, bloque, puede establecerse en Verdadero o Falso para anular el comportamiento de bloqueo descrito anteriormente.

Nico Schlömer
fuente
¿Por qué no usar draw (); [. otro código]; espectáculo() ? Que yo sepa, block = False ha quedado en desuso
Bogdan
¿Por qué crees que está en desuso? Lo veo documentado aquí .
Nico Schlömer
11

IMPORTANTE : Solo para aclarar algo. Supongo que los comandos están dentro de un .pyscript y se llama al script usando, por ejemplo, python script.pydesde la consola.

Una forma simple que funciona para mí es:

  1. Use el bloque = Falso dentro del espectáculo: plt.show (bloque = Falso)
  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()

seralouk
fuente
8

En mi caso, quería que aparecieran varias ventanas emergentes mientras se calculan. 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()

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

meteore
fuente
6

Bueno, tuve grandes problemas para descifrar los comandos que no bloquean ... Pero finalmente, logré reelaborar el ejemplo " Cookbook / Matplotlib / Animations - Animating selected plot elements ", por lo que funciona con hilos ( y pasa datos entre hilos a través de variables globales, o a través de un multiprocesoPipe ) 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,
¡salud!

sdaau
fuente
5

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.
  • 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.

Retirarse:

  • Lo único que se me ocurre es que tendrás que ir a buscar la carpeta y abrir la imagen tú mismo.
elgehelge
fuente
Si está tratando de generar muchas imágenes, estoy totalmente de acuerdo.
fantabolous
66
Los png de retroceso no son interactivos: \
Inverso
5

Si está trabajando en la consola, es decir IPython, podría 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.

Anton Protopopov
fuente
5

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)
Martin Pecka
fuente
Esto funcionó para mí con matplotlib3 en macOS. ¡Excelente!
Jerry Ma
4

En mi sistema, show () no se bloquea, aunque quería que el script esperara 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

Sin embargo, tenga en cuenta 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 guión todavía se ejecutó.

Andrés
fuente
¡Gracias! Spyder importa -pylab al inicio, lo que generalmente es útil, pero significa que show () no se bloqueará cuando ioff (), ¡esto le permite corregir este comportamiento!
perdido el
3

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 trama dentro de esta withdeclaración 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 "mantener las parcelas abiertas (incluso si ocurre un error) y permitir que se muestren nuevas parcelas", me gustaría que el script salga correctamente si ninguna interferencia del usuario lo indica de otra manera (para fines de ejecución por lotes).

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

Simon Streicher
fuente
2
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
Thanhtang
fuente
16
¿Cómo presionaría enter antes de existir?
grovina
2

El OP pregunta por separar las matplotlibparcelas. 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 file.pyse ejecuta a y desea que surjan los gráficos, pero el script de Python se complete y regrese a un símbolo del sistema.

Este archivo independiente se utiliza multiprocessingpara iniciar un proceso separado para trazar datos con matplotlib. El hilo principal sale usando lo os._exit(1)mencionado en esta publicación. Las os._exit()fuerzas principales salen, pero deja el matplotlibproceso hijo vivo y receptivo hasta que se cierra la ventana de la trama. 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. Solo cierra la ventana y sigue probando.

multiprocessingestá diseñado para la ejecución de código solo en python, lo que lo hace quizás más adecuado que subprocess. multiprocessinges 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

Al ejecutar, file.pyaparece una ventana de figura, luego __main__sale, pero la ventana de figura multiprocessing+ matplotlibsigue respondiendo 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

Marc Compere
fuente
Estaba tratando de usar su solución, pero no parece funcionar para mí y estoy tratando de averiguar por qué. No estoy ejecutando el código a través de la terminal, sino desde Pycharm IDE si eso hace alguna diferencia, aunque no debería.
ttsesm
1
ok, lo que finalmente funcionó para mí fue configurar el proceso secundario .daemon=Falsecomo se describe aquí stackoverflow.com/a/49607287/1476932 Sin embargo, sys.exit()no terminé el proceso primario como se describe allí hasta que cerré la ventana secundaria. Por otro lado, el uso os._exit(0)del ejemplo anterior funcionó.
ttsesm
1

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 matplotlib?

MikeTeX
fuente
0

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

show(block=False)
draw()
DomDev
fuente
show (block = False) ha quedado en desuso y ahora ya no funciona
Bogdan
0

Si bien no respondo directamente a la solicitud de OP, estoy publicando esta solución, ya que puede ayudar a alguien en esta situación:

  • Estoy creando un .exe con pyinstaller ya que no puedo instalar python donde necesito generar los gráficos, por lo que necesito el script de python para generar el gráfico, guardarlo como .png, cerrarlo y continuar con el siguiente, implementado como varios gráficos en un bucle o usando una función.

para esto estoy usando:

import matplotlib.pyplot as plt
#code generating the plot in a loop or function
#saving the plot
plt.savefig(var+'_plot.png',bbox_inches='tight', dpi=250) 
#you can allways reopen the plot using
os.system(var+'_plot.png') # unfortunately .png allows no interaction.
#the following avoids plot blocking the execution while in non-interactive mode
plt.show(block=False) 
#and the following closes the plot while next iteration will generate new instance.
plt.close() 

Donde "var" identifica la trama en el bucle para que no se sobrescriba.

patowski1981
fuente
-1

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

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

Ken Mueller
fuente
Ver la respuesta de @ nico-schlömer
Josh Wolff