Merencanakan dengan cara yang tidak menghalangi dengan Matplotlib

138

Saya telah bermain dengan Numpy dan matplotlib dalam beberapa hari terakhir. Saya mengalami masalah saat mencoba membuat plot plot fungsi tanpa memblokir eksekusi. Saya tahu sudah ada banyak utas di sini tentang SO yang mengajukan pertanyaan serupa, dan saya sudah cukup banyak mencari di Google tetapi belum berhasil membuat ini berhasil.

Saya telah mencoba menggunakan show (block = False) seperti yang disarankan beberapa orang, tetapi yang saya dapatkan hanyalah jendela beku. Jika saya cukup memanggil show (), hasilnya diplot dengan benar tetapi eksekusi diblokir sampai jendela ditutup. Dari utas lain yang saya baca, saya curiga apakah show (block = False) berfungsi atau tidak tergantung pada backend. Apakah ini benar? Bagian belakang saya adalah Qt4Agg. Bisakah Anda melihat kode saya dan memberi tahu saya jika Anda melihat sesuatu yang salah? Ini kode saya. Terima kasih atas bantuannya.

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. Saya lupa mengatakan bahwa saya ingin memperbarui jendela yang ada setiap kali saya merencanakan sesuatu, alih-alih membuat yang baru.

opetroch
sumber
1
Sudahkah Anda mencoba mode interaktif matplotlib plt.ion()sebelumnya plt.show()? Ini kemudian harus non-blocking karena setiap plot ditelurkan ke thread anak.
Anzel
@ Anzel Saya baru saja mencobanya, tetapi sepertinya tidak ada bedanya.
opetroch
3
Bagaimana Anda menjalankan skrip Anda? Jika saya menjalankan kode contoh Anda dari terminal / command prompt, sepertinya berfungsi dengan baik, tapi saya pikir saya pernah mengalami masalah ketika mencoba melakukan hal-hal seperti ini dari IPython QtConsole atau IDE.
Marius
1
@Marius Aha !! Kamu benar. Memang saya menjalankannya dari konsol IDE saya (PyCharm). Saat menjalankannya dari prompt cmd, plt.show (blok = Salah), berfungsi dengan baik! Apakah saya akan bertanya terlalu banyak jika saya bertanya apakah Anda sudah menemukan ide / solusi untuk itu? Terima kasih banyak!
opetroch
Saya benar-benar tidak tahu maaf. Saya tidak benar-benar memahami detail bagaimana matplotlib berinteraksi dengan konsol, jadi saya biasanya hanya beralih ke menjalankan dari command prompt jika saya perlu melakukan hal-hal ini matplotlib.
Marius

Jawaban:

167

Saya menghabiskan waktu yang lama mencari solusi, dan menemukan jawaban ini .

Sepertinya, untuk mendapatkan apa yang Anda (dan saya) inginkan, Anda memerlukan kombinasi plt.ion(), plt.show()(bukan dengan block=False) dan, yang paling penting, plt.pause(.001)(atau waktu apa pun yang Anda inginkan). The jeda diperlukan karena peristiwa GUI terjadi sedangkan kode utama sedang tidur, termasuk menggambar. Mungkin saja ini diimplementasikan dengan mengambil waktu dari thread tidur, jadi mungkin IDE mengacaukannya — saya tidak tahu.

Berikut ini adalah implementasi yang berfungsi untuk saya di 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()
krs013
sumber
3
Jawaban Anda banyak membantu saya dalam memecahkan masalah serupa yang saya alami. Sebelumnya, saya telah plt.drawmengikuti plt.show(block = False)tetapi kemudian berhenti bekerja: Gambar tidak merespons, menutupnya jatuh iPython. Solusi saya menghapus setiap instance plt.draw()dan menggantinya plt.pause(0.001). Alih-alih diikuti oleh plt.show(block = False)seperti plt.drawsebelumnya, itu didahului oleh plt.ion()dan plt.show(). Saya sekarang memiliki MatplotlibDeprecationWarningtetapi membiarkan saya memplot angka saya, jadi saya senang dengan solusi ini.
blue_chip
3
Perhatikan bahwa dalam python 2.7, Anda perlu menggunakan raw_inputtidak input. Lihat di sini
Chris
Solusi yang sangat berguna ketika pendekatan "menghidupkan" reaktif tidak dimungkinkan! Adakah yang tahu bagaimana cara menyingkirkan peringatan penghinaan?
Frederic Fortier
Tolong bisakah seseorang memberi tahu saya mengapa saya mendapatkan prompt perintah freezen ketika saya mencoba menambahkan plt.ion sebelum plt.show?
Gabriel Augusto
@GabrielAugusto Saya tidak yakin apa yang bisa menyebabkan itu, dan saya tidak yakin apa yang Anda maksud. Saya baru saja menguji contoh ini dalam Python 3.6 dan masih berfungsi. Jika Anda menggunakan pola yang sama dan membeku, mungkin ada yang salah dengan instalasi Anda. Anda harus memeriksa apakah plot normal berfungsi lebih dulu. Jika Anda mencoba sesuatu yang berbeda, tidak ada banyak yang bisa dilakukan di komentar. Dalam kedua kasus tersebut, Anda dapat mempertimbangkan untuk mengajukan pertanyaan terpisah.
krs013
23

Trik sederhana yang bekerja untuk saya adalah sebagai berikut:

  1. Gunakan blok = Argumen salah di dalam show: plt.show (blok = Salah)
  2. Gunakan plt.show () lain di akhir skrip .py.

Contoh :

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

Catatan : plt.show()adalah baris terakhir dari skrip saya.

seralouk
sumber
7
Ini menghasilkan (bagi saya, di Linux, Anaconda, Python 2.7, backend default) jendela kosong yang tetap kosong sampai akhir eksekusi, ketika akhirnya diisi. Tidak berguna untuk memperbarui plot di tengah-tengah eksekusi. :-(
sh37211
@ sh37211 Tidak yakin apa tujuan Anda. Dalam beberapa kasus Anda mencoba untuk merencanakan sesuatu tetapi setelah perintah plot Anda memiliki perintah lain, maka ini berguna karena memungkinkan Anda untuk merencanakan dan menjalankan perintah lainnya. Lihat posting ini untuk lebih lanjut tentang ini: stackoverflow.com/questions/458209/… . Jika Anda ingin memperbarui plot maka harus dengan cara lain.
seralouk
17

Anda dapat menghindari memblokir eksekusi dengan menulis plot ke array, lalu menampilkan array di utas yang berbeda. Berikut adalah contoh membuat dan menampilkan plot secara bersamaan menggunakan pf.screen dari 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()

Hasil:

Animasi sinus

Penafian: Saya adalah pengelola pyformulas.

Referensi: Matplotlib: save plot to numpy array

Gambar default
sumber
9

Banyak dari jawaban ini sangat menggembung dan dari apa yang dapat saya temukan, jawabannya tidak terlalu sulit untuk dipahami.

Anda dapat menggunakan plt.ion()jika Anda ingin, tetapi saya menemukan menggunakanplt.draw() sama efektifnya

Untuk proyek spesifik saya, saya sedang merencanakan gambar, tetapi Anda dapat menggunakan plot()atau scatter()atau apa pun figimage(), itu tidak masalah.

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

Atau

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

Jika Anda menggunakan angka yang sebenarnya.
Saya menggunakan @ krs013, dan jawaban @Default Picture untuk mencari tahu.
Semoga ini menyelamatkan seseorang dari meluncurkan setiap gambar pada utas terpisah, atau dari harus membaca novel ini hanya untuk mencari tahu ini

iggy12345
sumber
3

Merencanakan hidup

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

jika jumlah data terlalu banyak, Anda dapat menurunkan kecepatan pembaruan dengan penghitung sederhana

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

Memegang Plot setelah Program Keluar

Ini adalah masalah saya yang sebenarnya yang tidak dapat menemukan jawaban yang memuaskan, saya ingin merencanakan yang tidak menutup setelah skrip selesai (seperti MATLAB),

Jika Anda memikirkannya, setelah skrip selesai, program diakhiri dan tidak ada cara logis untuk menahan plot dengan cara ini, jadi ada dua opsi

  1. blokir skrip agar tidak keluar (itu plt.show () dan bukan yang saya inginkan)
  2. jalankan plot pada utas terpisah (terlalu rumit)

ini tidak memuaskan bagi saya jadi saya menemukan solusi lain di luar kotak

SaveToFile dan Lihat di penampil eksternal

Untuk ini, penyimpanan dan tampilan harus cepat dan pemirsa tidak boleh mengunci file dan memperbarui konten secara otomatis

Memilih Format untuk Menyimpan

format berbasis vektor kecil dan cepat

  • SVG baik tetapi tidak dapat menemukan penampil yang baik untuk itu kecuali browser web yang secara default membutuhkan penyegaran manual
  • PDF dapat mendukung format vektor dan ada pemirsa ringan yang mendukung pembaruan langsung

Penampil Ringan Cepat dengan Pembaruan Langsung

Untuk PDF ada beberapa opsi bagus

  • Pada Windows saya menggunakan SumatraPDF yang gratis, cepat dan ringan (hanya menggunakan 1,8 MB RAM untuk kasus saya)

  • Di Linux ada beberapa opsi seperti Evince (GNOME) dan Ocular (KDE)

Kode Sampel & Hasil

Kode sampel untuk menghasilkan plot ke file

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")

setelah pertama kali dijalankan, buka file output di salah satu pemirsa yang disebutkan di atas dan nikmati.

Berikut ini adalah screenshot VSCode bersama SumatraPDF, juga prosesnya cukup cepat untuk mendapatkan tingkat pembaruan semi-live (saya bisa mendekati 10Hz pada pengaturan saya hanya menggunakan time.sleep()antar interval) pyPlot, Non-Blocking

Ali80
sumber
2

Jawaban Iggy adalah yang termudah untuk saya ikuti, tetapi saya mendapatkan kesalahan berikut ketika melakukan subplotperintah berikutnya yang tidak ada ketika saya hanya melakukan show:

MatplotlibDeprecationWarning: Menambahkan sumbu menggunakan argumen yang sama dengan sumbu sebelumnya saat ini menggunakan kembali contoh sebelumnya. Dalam versi yang akan datang, instance baru akan selalu dibuat dan dikembalikan. Sementara itu, peringatan ini dapat ditekan, dan perilaku masa depan dipastikan, dengan memberikan label unik untuk setiap instance sumbu.

Untuk menghindari kesalahan ini, ada baiknya menutup (atau menghapus ) plot setelah pengguna masuk.

Inilah kode yang berfungsi untuk saya:

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()
Pro Q
sumber
0

Paket Python drawow memungkinkan untuk memperbarui plot secara real time dengan cara yang tidak menghalangi.
Ini juga berfungsi dengan webcam dan OpenCV misalnya untuk merencanakan langkah-langkah untuk setiap frame.
Lihat posting asli .

Ismael EL ATIFI
sumber