Menyimpan tokoh Matplotlib interaktif

119

Apakah ada cara untuk menyimpan figur Matplotlib sedemikian rupa sehingga dapat dibuka kembali dan interaksi biasa dipulihkan? (Seperti format .fig di MATLAB?)

Saya mendapati diri saya menjalankan skrip yang sama berkali-kali untuk menghasilkan gambar interaktif ini. Atau saya mengirimkan beberapa file PNG statis kepada kolega saya untuk menunjukkan berbagai aspek plot. Saya lebih suka mengirim objek gambar dan meminta mereka berinteraksi dengannya sendiri.

Matt
sumber

Jawaban:

30

Ini akan menjadi fitur yang hebat, tetapi AFAIK tidak diimplementasikan di Matplotlib dan kemungkinan akan sulit untuk diimplementasikan sendiri karena cara gambar disimpan.

Saya akan menyarankan (a) memisahkan pemrosesan data dari pembuatan gambar (yang menyimpan data dengan nama unik) dan menulis skrip penghasil gambar (memuat file tertentu dari data yang disimpan) dan mengedit sesuai keinginan Anda atau (b ) simpan sebagai format PDF / SVG / PostScript dan edit di beberapa editor gambar mewah seperti Adobe Illustrator (atau Inkscape ).

EDIT posting Musim Gugur 2012 : Seperti yang ditunjukkan orang lain di bawah (meskipun disebutkan di sini karena ini adalah jawaban yang diterima), Matplotlib sejak versi 1.2 memungkinkan Anda untuk membuat acar. Seperti yang dinyatakan dalam catatan rilis , ini adalah fitur eksperimental dan tidak mendukung penyimpanan gambar dalam satu versi matplotlib dan dibuka di versi lain. Biasanya juga tidak aman untuk memulihkan acar dari sumber yang tidak tepercaya.

Untuk berbagi / kemudian mengedit plot (yang membutuhkan pemrosesan data yang signifikan terlebih dahulu dan mungkin perlu di-tweak beberapa bulan kemudian, katakanlah saat peer review untuk publikasi ilmiah), saya tetap merekomendasikan alur kerja (1) memiliki skrip pemrosesan data yang sebelum membuat plot menyimpan data yang diproses (yang masuk ke dalam plot Anda) ke dalam file, dan (2) memiliki skrip pembuatan plot terpisah (yang Anda sesuaikan seperlunya) untuk membuat ulang plot. Dengan cara ini untuk setiap plot Anda dapat dengan cepat menjalankan skrip dan membuatnya kembali (dan dengan cepat menyalin pengaturan plot Anda dengan data baru). Meskipun demikian, membuat gambar menjadi lebih nyaman untuk analisis data jangka pendek / interaktif / eksplorasi.

dr jimbob
sumber
2
Agak terkejut ini tidak diterapkan .. Tapi ok, saya akan menyimpan data yang diproses dalam file perantara dan mengirimkannya dan skrip untuk merencanakan ke rekan kerja. Terima kasih.
Matt
2
Saya curiga penerapannya sulit, itulah sebabnya MATLAB bekerja dengan sangat buruk. Kembali ketika saya menggunakannya, angka-angka yang digunakan untuk crash MATLAB, dan bahkan versi yang sedikit berbeda tidak dapat membaca file .fig satu sama lain.
Adrian Ratnapala
6
picklesekarang bekerja pada gambar MPL, jadi ini dapat dilakukan dan tampaknya berfungsi dengan baik - hampir seperti file gambar ".fig" Matlab. Lihat jawaban saya di bawah (untuk sekarang?) Untuk contoh bagaimana melakukannya.
Demis
@Demis: seperti yang ditunjukkan ptomato dalam jawabannya di bawah, itu sudah ada saat itu.
strpeter
@strpeter - Angka Matlab tidak dapat dijadikan acar pada tahun 2010 seperti yang ditunjukkan dalam komentar ini . Fitur eksperimental ditambahkan dengan matplotlib 1.2 yang dirilis pada musim gugur 2012 . Seperti disebutkan di sana, Anda seharusnya tidak mengharapkannya bekerja antara versi matplotlib dan Anda tidak boleh membuka acar yang berasal dari sumber yang tidak tepercaya.
dr jimbob
63

Saya baru tahu bagaimana melakukan ini. "Dukungan acar eksperimental" yang disebutkan oleh @pelson bekerja dengan cukup baik.

Coba ini:

# Plot something
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([1,2,3],[10,-10,30])

Setelah tweaker interaktif Anda, simpan objek gambar sebagai file biner:

import pickle
pickle.dump(fig, open('FigureObject.fig.pickle', 'wb')) # This is for Python 3 - py2 may need `file` instead of `open`

Nanti, buka gambar dan tweak harus disimpan dan interaktivitas GUI harus ada:

import pickle
figx = pickle.load(open('FigureObject.fig.pickle', 'rb'))

figx.show() # Show the figure, edit it, etc.!

Anda bahkan dapat mengekstrak data dari plot:

data = figx.axes[0].lines[0].get_data()

(Ini berfungsi untuk garis, pcolor & imshow - pcolormesh bekerja dengan beberapa trik untuk merekonstruksi data yang diratakan .)

Saya mendapat tip bagus dari Menyimpan Angka Matplotlib Menggunakan Acar .

Demis
sumber
Saya percaya ini tidak kuat untuk perubahan versi dll, dan tidak kompatibel silang antara py2.x & py3.x. Juga saya pikir pickledokumentasinya menyatakan bahwa kita perlu mengatur lingkungan yang sama seperti ketika objek itu diawetkan (disimpan), tetapi saya telah menemukan Anda tidak perlu import matplotlib.pyplot as pltketika melepasnya (memuat) - ini menyimpan pernyataan impor dalam file acar .
Demis
5
Anda harus mempertimbangkan untuk menutup file yang dibuka: misalnyawith open('FigureObject.fig.pickle', 'rb') as file: figx = pickle.load(file)
strpeter
1
Saya baru saja mendapatkan: 'AttributeError:' Gambar 'objek tidak memiliki atribut' _cachedRenderer ''
user2673238
Jika Anda tidak ingin skrip berlanjut dan mungkin segera dihentikan setelahnya figx.show(), Anda harus memanggil plt.show(), yang memblokir.
maechler
38

Pada Matplotlib 1.2, kami sekarang memiliki dukungan acar eksperimental . Cobalah dan lihat apakah itu berfungsi dengan baik untuk kasus Anda. Jika Anda mengalami masalah, beri tahu kami di milis Matplotlib atau dengan membuka masalah di github.com/matplotlib/matplotlib .

pelson
sumber
2
Segala alasan mengapa fitur yang berguna ini dapat ditambahkan ke "Simpan sebagai" gambar itu sendiri. Menambahkan .pkl mungkin?
dashesy
Ide bagus @dashesy. Saya akan mendukungnya jika Anda ingin mencoba menerapkannya?
pelson
1
Apakah ini hanya berfungsi pada subset backend? Ketika saya mencoba membuat acar sosok sederhana di OSX, saya mengerti PicklingError: Can't pickle <type '_macosx.GraphicsContext'>: it's not found as _macosx.GraphicsContext.
mulai
Hal di atas PicklingErrorhanya terjadi jika Anda menelepon plt.show()sebelum melakukan pengawetan. Jadi tempatkan saja plt.show()setelahnya pickle.dump().
salomonvh
Di py3.5 saya di MacOS 10.11, urutan fig.show()tampaknya tidak masalah - mungkin bug itu telah diperbaiki. Saya bisa membuat acar sebelum / sesudah show()tanpa masalah.
Demis
7

Mengapa tidak mengirim skrip Python saja? File .fig MATLAB mengharuskan penerima untuk memiliki MATLAB untuk menampilkannya, jadi itu hampir setara dengan mengirim skrip Python yang membutuhkan Matplotlib untuk ditampilkan.

Sebagai alternatif (penafian: Saya belum mencoba ini), Anda dapat mencoba mengawetkan gambar:

import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
ptomato
sumber
3
Sayangnya, figur matplotlib tidak bisa dijadikan acar, jadi pendekatan itu tidak akan berhasil. Di balik layar, ada terlalu banyak ekstensi C yang tidak mendukung pengawetan. Saya sepenuhnya setuju untuk hanya mengirim skrip + data, meskipun ... Saya kira saya tidak pernah benar-benar melihat poin dari matlab yang disimpan .fig, jadi saya tidak pernah menggunakannya. Mengirim kode dan data yang berdiri sendiri kepada seseorang telah menjadi yang termudah dalam jangka panjang, menurut pengalaman saya. Namun, alangkah baiknya jika sosok matplotlib objek di mana dapat dijadikan acar.
Joe Kington
1
Bahkan data kami yang telah diproses sebelumnya agak besar dan prosedur pembuatan plotnya rumit. Sepertinya satu-satunya jalan lain. Terima kasih.
Matt
1
Angka-angka tampaknya sekarang bisa dijadikan acar - ini bekerja dengan cukup baik! Contoh di bawah ini.
Demis
Manfaat acar adalah Anda tidak perlu menyesuaikan semua spasi / posisi gambar / subplot secara terprogram. Anda dapat menggunakan GUI plot MPL untuk membuat gambar terlihat bagus, dll., Lalu simpan MyPlot.fig.picklefile - dengan mempertahankan kemampuan selanjutnya untuk menyesuaikan presentasi plot sesuai kebutuhan. Ini juga yang hebat tentang .figfile Matlab . Berguna terutama saat Anda perlu mengubah ukuran / rasio aspek gambar (untuk dimasukkan ke dalam presentasi / makalah).
Demis
1

Pertanyaan bagus. Ini teks dokumen dari pylab.save:

pylab tidak lagi menyediakan fungsi simpan, meskipun fungsi pylab lama masih tersedia sebagai matplotlib.mlab.save (Anda masih bisa merujuknya di pylab sebagai "mlab.save"). Namun, untuk file teks biasa, kami merekomendasikan numpy.savetxt. Untuk menyimpan array numpy, kami merekomendasikan numpy.save, dan numpy.load analognya, yang tersedia di pylab sebagai np.save dan np.load.

Steve Tjoa
sumber
Ini menyimpan data dari objek pylab, tetapi tidak memungkinkan Anda untuk membuat ulang gambar tersebut.
dr jimbob
Benar. Saya harus menjelaskan bahwa jawabannya bukanlah rekomendasi untuk digunakan pylab.save. Bahkan, dari teks dokumen, tampaknya seseorang tidak boleh menggunakannya.
Steve Tjoa
Apakah ada metode eksternal untuk mengirim Gambar 3D? Bahkan mungkin GUI sederhana untuk exe ..
CromeX
0

Saya menemukan cara yang relatif sederhana (namun sedikit tidak konvensional) untuk menyimpan gambar matplotlib saya. Ini bekerja seperti ini:

import libscript

import matplotlib.pyplot as plt
import numpy as np

t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2*np.pi*t)

#<plot>
plt.plot(t, s)
plt.xlabel('time (s)')
plt.ylabel('voltage (mV)')
plt.title('About as simple as it gets, folks')
plt.grid(True)
plt.show()
#</plot>

save_plot(fileName='plot_01.py',obj=sys.argv[0],sel='plot',ctx=libscript.get_ctx(ctx_global=globals(),ctx_local=locals()))

dengan fungsi yang save_plotdidefinisikan seperti ini (versi sederhana untuk memahami logika):

def save_plot(fileName='',obj=None,sel='',ctx={}):
    """
    Save of matplolib plot to a stand alone python script containing all the data and configuration instructions to regenerate the interactive matplotlib figure.

    Parameters
    ----------
    fileName : [string] Path of the python script file to be created.
    obj : [object] Function or python object containing the lines of code to create and configure the plot to be saved.
    sel : [string] Name of the tag enclosing the lines of code to create and configure the plot to be saved.
    ctx : [dict] Dictionary containing the execution context. Values for variables not defined in the lines of code for the plot will be fetched from the context.

    Returns
    -------
    Return ``'done'`` once the plot has been saved to a python script file. This file contains all the input data and configuration to re-create the original interactive matplotlib figure.
    """
    import os
    import libscript

    N_indent=4

    src=libscript.get_src(obj=obj,sel=sel)
    src=libscript.prepend_ctx(src=src,ctx=ctx,debug=False)
    src='\n'.join([' '*N_indent+line for line in src.split('\n')])

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(src+'\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

atau mendefinisikan fungsi save_plotseperti ini (versi yang lebih baik menggunakan kompresi zip untuk menghasilkan file gambar yang lebih ringan):

def save_plot(fileName='',obj=None,sel='',ctx={}):

    import os
    import json
    import zlib
    import base64
    import libscript

    N_indent=4
    level=9#0 to 9, default: 6
    src=libscript.get_src(obj=obj,sel=sel)
    obj=libscript.load_obj(src=src,ctx=ctx,debug=False)
    bin=base64.b64encode(zlib.compress(json.dumps(obj),level))

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(' '*N_indent+'import base64\n')
        f.write(' '*N_indent+'import zlib\n')
        f.write(' '*N_indent+'import json\n')
        f.write(' '*N_indent+'import libscript\n')
        f.write(' '*N_indent+'bin="'+str(bin)+'"\n')
        f.write(' '*N_indent+'obj=json.loads(zlib.decompress(base64.b64decode(bin)))\n')
        f.write(' '*N_indent+'libscript.exec_obj(obj=obj,tempfile=False)\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

Ini membuat saya menggunakan modul libscriptsendiri, yang sebagian besar bergantung pada modul inspectdan ast. Saya dapat mencoba membagikannya di Github jika ada minat yang diungkapkan (pertama-tama perlu pembersihan dan saya harus memulai dengan Github).

Ide di balik save_plotfungsi dan libscriptmodul ini adalah untuk mengambil instruksi python yang membuat gambar (menggunakan modul inspect), menganalisisnya (menggunakan modul ast) untuk mengekstrak semua variabel, fungsi, dan impor modul yang diandalkan, mengekstraknya dari konteks eksekusi dan membuat serialisasi. sebagai instruksi python (kode untuk variabel akan seperti t=[0.0,2.0,0.01]... dan kode untuk modul akan seperti import matplotlib.pyplot as plt...) diawali dengan petunjuk gambar. Instruksi python yang dihasilkan disimpan sebagai skrip python yang eksekusinya akan membangun kembali gambar matplotlib asli.

Seperti yang dapat Anda bayangkan, ini berfungsi dengan baik untuk sebagian besar (jika tidak semua) figur matplotlib.

Astrum 42
sumber