web-dev-qa-db-fra.com

Enregistrement de figures interactives Matplotlib

Existe-t-il un moyen de sauvegarder une figure Matplotlib de telle sorte qu'elle puisse être rouverte et que les interactions types soient restaurées? (Comme le format .fig dans MATLAB?)

Je me retrouve à exécuter plusieurs fois les mêmes scripts pour générer ces figures interactives. Ou j'envoie à mes collègues plusieurs fichiers PNG statiques pour montrer différents aspects d'un tracé. Je préfère envoyer l'objet figure et les faire interagir avec lui-même.

94
Matt

Ce serait une fonctionnalité intéressante, mais autant que je sache, il n’est pas implémenté dans Matplotlib et sera probablement difficile à implémenter vous-même en raison de la façon dont les chiffres sont stockés.

Je suggérerais soit (a) de séparer le traitement des données de la génération de la figure (qui enregistre les données avec un nom unique) et d’écrire un script de génération de la figure (chargement d’un fichier spécifié des données sauvegardées) et de le modifier selon vos besoins ou (b ) enregistrez au format PDF / SVG / PostScript et modifiez-le dans un éditeur de chiffres fantaisie comme Adobe Illustrator (ou Inkscape ).

EDIT post Automne 2012: Comme d'autres l'ont fait remarquer ci-dessous (bien que ce soit la réponse acceptée), Matplotlib, depuis la version 1.2, vous permettait de conserver des chiffres. Comme les notes de version indiquent , il s'agit d'une fonctionnalité expérimentale qui ne prend pas en charge l'enregistrement d'une figure dans une version de matplotlib et son ouverture dans une autre. Il est également généralement peu sûr de restaurer un cornichon à partir d'une source non fiable. 

Pour les parcelles de partage/d'édition ultérieure (qui nécessitent un traitement de données important en premier et peuvent nécessiter une modification quelques mois plus tard, par exemple lors de l'examen d'une publication scientifique par des pairs), je recommande tout de même que le flux de travail de (1) comporte un script de traitement de données avant de générer un tracé. enregistre les données traitées (qui entrent dans votre tracé) dans un fichier et (2) disposent d'un script de génération de tracé séparé (que vous ajustez si nécessaire) pour recréer le tracé. Ainsi, pour chaque tracé, vous pouvez rapidement exécuter un script et le générer à nouveau (et copier rapidement vos paramètres de tracé avec les nouvelles données). Cela dit, choisir un chiffre pourrait être utile pour l'analyse de données à court terme/interactive/exploratoire.

20
dr jimbob

Je viens de découvrir comment faire cela. Le "support de cornichons expérimental" mentionné par @pelson fonctionne assez bien.

Essaye ça:

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

Après vos ajustements interactifs, enregistrez l'objet figure dans un fichier binaire:

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

Plus tard, ouvrez la figure et les réglages devraient être enregistrés et l'interactivité de l'interface graphique devrait être présente:

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

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

Vous pouvez même extraire les données des parcelles:

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

(Cela fonctionne pour les lignes, pcolor & imshow - pcolormesh fonctionne avec quelques astuces pour reconstruire les données aplaties .)

J'ai eu l'excellent conseil de Sauvegarder des figurines Matplotlib avec Pickle.

40
Demis

Depuis Matplotlib 1.2, nous avons maintenant le support expérimental pickle . Essayez-le et voyez si cela fonctionne bien pour votre cas. Si vous rencontrez des problèmes, veuillez nous en informer sur la liste de diffusion Matplotlib ou en ouvrant un problème sur github.com/matplotlib/matplotlib .

38
pelson

Pourquoi ne pas simplement envoyer le script Python? Les fichiers .fig de MATLAB nécessitent que le destinataire dispose de MATLAB pour les afficher. Cela équivaut à l'envoi d'un script Python nécessitant l'affichage de Matplotlib.

Alternativement (disclaimer: je n'ai pas encore essayé), vous pouvez essayer de décaper la figure:

import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
7
ptomato

Bonne question. Voici le texte de la doc de pylab.save:

pylab ne fournit plus de fonction de sauvegarde, bien que l'ancien pylab La fonction est toujours disponible en tant que matplotlib.mlab.save (vous pouvez toujours vous y reporter dans pylab sous le nom "mlab.save"). Cependant, pour le texte brut fichiers, nous vous recommandons numpy.savetxt. Pour sauvegarder des tableaux numpy, nous recommandons numpy.save, et son numpy.load analogique, qui sont disponible dans pylab sous les noms np.save et np.load.

1
Steve Tjoa

J'ai trouvé un moyen relativement simple (mais légèrement peu conventionnel) de sauvegarder mes figurines matplotlib. Cela fonctionne comme ceci:

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

avec la fonction save_plot définie comme ceci (version simple pour comprendre la logique):

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'

ou en définissant la fonction save_plot comme ceci (meilleure version utilisant la compression Zip pour produire des fichiers de figures plus légers):

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'

Cela fait utiliser un module libscript à moi, qui repose principalement sur les modules inspect et ast. Je peux essayer de le partager sur Github si un intérêt s’exprime (cela nécessiterait au préalable un nettoyage et le lancement de Github).

L’idée de cette fonction save_plot et de ce module libscript est de récupérer les instructions python qui créent la figure (en utilisant le module inspect), de les analyser (en utilisant le module ast) afin d’extraire toutes les variables, fonctions et importations dont dépend le module, de les extraire de l’exécution context et les sérialiser en tant qu’instructions python (le code pour les variables ressemblera à t=[0.0,2.0,0.01] ... et le code pour les modules ressemblera à import matplotlib.pyplot as plt ...) précédé des instructions illustrées. Les instructions python résultantes sont enregistrées en tant que script python dont l’exécution reconstruira la figure matplotlib d’origine.

Comme vous pouvez l’imaginer, cela fonctionne bien pour la plupart (sinon la totalité) des figures matplotlib.

0
Astrum42