C'è un modo per staccare i grafici matplotlib in modo che il calcolo possa continuare?
-
19-08-2019 - |
Domanda
Dopo queste istruzioni nell'interprete Python si ottiene una finestra con una trama:
from matplotlib.pyplot import *
plot([1,2,3])
show()
# other code
Sfortunatamente, non so come continuare a esplorare interattivamente la figura creata da show ()
mentre il programma esegue ulteriori calcoli.
È possibile? A volte i calcoli sono lunghi e sarebbe utile se procedessero durante l'esame dei risultati intermedi.
Soluzione
Utilizza le chiamate di matplotlib
che non si bloccano:
Usando 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 della modalità interattiva:
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()
Altri suggerimenti
Utilizza la parola chiave "blocco" per sovrascrivere il comportamento di blocco, ad es.
from matplotlib.pyplot import show, plot
plot(1)
show(block=False)
# your code
per continuare il tuo codice.
È meglio verificare sempre con la libreria che si sta utilizzando se supporta l'utilizzo in non bloccando .
Ma se vuoi una soluzione più generica o se non c'è altro modo, puoi eseguire qualsiasi cosa blocchi in un processo separato usando multprocessing
incluso in Python. Il calcolo continuerà:
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()
Questo ha il sovraccarico di avviare un nuovo processo ed è talvolta più difficile eseguire il debug su scenari complessi, quindi preferirei l'altra soluzione (usando matplotlib
s chiamate API non bloccate )
Prova
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 ()
dice:
In modalità non interattiva, mostra tutte le figure e blocca fino a quando le figure non sono state chiuse; in modalità interattiva non ha alcun effetto se le figure non sono state create prima di un passaggio dalla modalità non interattiva a quella interattiva (non consigliata). In tal caso visualizza le cifre ma non si blocca.
Un singolo argomento di parola chiave sperimentale,
block
, può essere impostato suTrue
oFalse
per sovrascrivere il comportamento di blocco descritto sopra.
Potresti voler leggere questo documento nella documentazione di matplotlib
, intitolata:
IMPORTANTE : solo per chiarire qualcosa. Presumo che i comandi si trovino all'interno di uno script .py
e che lo script venga chiamato usando ad es. python script.py
dalla console.
Un modo semplice che funziona per me è:
- Usa il blocco = Falso all'interno di show: plt.show (blocco = Falso)
- Usa un altro show () alla fine dello script .py.
Esempio di script.py
file:
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()
Nel mio caso, volevo che apparissero diverse finestre mentre venivano calcolate. Per riferimento, questo è il modo:
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 abbastanza utile per l'interfaccia OO di matplotlib .
Beh, ho avuto grossi problemi a capire i comandi non bloccanti ... Ma alla fine sono riuscito a rielaborare il " Libro di cucina / Matplotlib / Animazioni - Animazione degli elementi della trama selezionati " esempio, quindi funziona con i thread ( e passa i dati tra i thread tramite variabili globali o attraverso un
Lo script è disponibile qui: Animating_selected_plot_elements-thread.py - altrimenti incollato di seguito ( con meno commenti ) come riferimento:
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!
Spero che questo aiuti qualcuno,
Cheers!
In molti casi è più conveniente per salvare l'immagine come file .png sul disco rigido. Ecco perché:
Vantaggi:
- Puoi aprirlo, guardarlo e chiuderlo in qualsiasi momento del processo. Ciò è particolarmente utile quando l'applicazione è in esecuzione a lungo tempo.
- Non viene visualizzato nulla e non sei obbligato ad aprire le finestre. Ciò è particolarmente utile quando hai a che fare con molte figure.
- L'immagine è accessibile per riferimento futuro e non viene persa quando si chiude la finestra della figura.
Svantaggio:
- L'unica cosa che mi viene in mente è che dovrai andare a cercare la cartella e aprire tu stesso l'immagine.
Se lavori in console, ad esempio IPython
puoi usare plt.show (block = False)
come indicato nelle altre risposte. Ma se sei pigro potresti semplicemente digitare:
plt.show(0)
Quale sarà lo stesso.
Ho dovuto anche aggiungere plt.pause (0.001)
al mio codice per farlo funzionare davvero all'interno di un ciclo for (altrimenti mostrerebbe solo il primo e l'ultimo grafico):
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)
Volevo anche che i miei grafici fossero visualizzati per eseguire il resto del codice (e quindi continuare a essere visualizzati) anche se si verifica un errore (a volte uso i grafici per il debug). Ho codificato questo piccolo hack in modo che qualsiasi trama all'interno di questa istruzione con
si comporti come tale.
Questo è probabilmente un po 'troppo non standard e non consigliabile per il codice di produzione. Probabilmente ci sono molti "gotchas" nascosti " in questo codice.
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
Se / quando implemento una corretta "tenere aperte le trame (anche se si verifica un errore) e consentire la visualizzazione di nuove trame", vorrei che lo script uscisse correttamente se nessuna interferenza dell'utente lo dice diversamente (per batch scopi di esecuzione).
Potrei usare qualcosa come una domanda di timeout " Fine dello script! \ nPremere p se si desidera mettere in pausa l'output di stampa (hai 5 secondi): " da https://stackoverflow.com/questions/26704840 / angolo-cases-for-my-attendere-per-user-input-interruzione-implementazione .
Sul mio sistema show () non si blocca, anche se volevo che lo script attendesse che l'utente interagisse con il grafico (e raccogliesse i dati usando i callback 'pick_event') prima di continuare.
Per bloccare l'esecuzione fino alla chiusura della finestra della trama, ho usato quanto segue:
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
Si noti, tuttavia, che canvas.start_event_loop_default () ha prodotto il seguente avviso:
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)
sebbene lo script sia ancora in esecuzione.
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
Secondo me, le risposte in questo thread forniscono metodi che non funzionano per tutti i sistemi e in situazioni più complesse come le animazioni. Suggerisco di dare un'occhiata alla risposta di MiKTeX nel seguente thread, in cui è stato trovato un metodo affidabile: Come attendere fino alla fine dell'animazione matplotlib?
Se vuoi aprire più figure, mantenendo tutte aperte, questo codice ha funzionato per me:
show(block=False)
draw()
L'OP chiede di rimuovere i grafici matplotlib
. La maggior parte delle risposte presuppone l'esecuzione dei comandi da un interprete python. Il caso d'uso presentato qui è la mia preferenza per testare il codice in un terminale (es. Bash) in cui viene eseguito un file.py
e vuoi che vengano visualizzati i grafici ma che lo script python sia completo e tornare a un prompt dei comandi.
Questo file autonomo utilizza multiprocessing
per avviare un processo separato per la stampa di dati con matplotlib
. Il thread principale viene chiuso utilizzando os._exit (1)
menzionato in questo post. os._exit ()
forza l'uscita principale ma lascia il processo figlio matplotlib
vivo e reattivo fino alla chiusura della finestra del diagramma. È un processo completamente separato.
Questo approccio è un po 'come una sessione di sviluppo di Matlab con finestre di figure che presentano un prompt dei comandi reattivo. Con questo approccio, hai perso tutti i contatti con il processo di figura, ma va bene per lo sviluppo e il debug. Chiudi la finestra e continua i test.
multiprocessing
è progettato per l'esecuzione di codice solo per Python che lo rende forse più adatto di sottoprocesso
. multiprocessing
è multipiattaforma, quindi dovrebbe funzionare bene su Windows o Mac con poca o nessuna regolazione. Non è necessario controllare il sistema operativo sottostante. Questo è stato testato su 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
L'esecuzione di file.py
fa apparire una finestra di figura, quindi __main__
esce ma la figura multiprocessing
+ matplotlib
la finestra rimane reattiva con zoom, pan e altri pulsanti perché è un processo indipendente.
Controlla i processi al prompt dei comandi di bash con:
ps ax | grep -v grep | grep file.py
Usa plt.show (block = False)
e alla fine della tua chiamata di script plt.show ()
.
Ciò garantirà che la finestra non venga chiusa al termine dello script.