Enregistrez la table" Out [] " d'une base de données pandas comme une figure

Cela peut sembler être une fonctionnalité inutile, mais il serait très utile pour moi. Je voudrais enregistrer la sortie que je reçois à L'intérieur de Canopy IDE. Je ne pense pas que ce soit spécifique à la canopée, mais pour la clarté, c'est ce que j'utilise. Par exemple, ma console[2] est ce que je voudrais:

enter image description here

je pense que le formatage est assez agréable et reproduire ceci à chaque fois au lieu de simplement sauver la sortie serait une perte de temps. Donc ma question est, comment puis-je obtenir une poignée sur cette figure? Idéalement, l'implémentation serait similaire aux méthodes standard, tel qu'il pourrait être fait comme ceci:

from matplotlib.backends.backend_pdf import PdfPages

pp = PdfPages('Output.pdf')
fig = plt.figure() 
ax = fig.add_subplot(1, 1, 1)
df.plot(how='table')
pp.savefig()
pp.close()

NOTE: je me rends compte que d'une question très semblable a été posée ( comment sauvegarder les données Pandas dataframe / series comme une figure?) mais elle n'a jamais reçu de réponse et je pense que j'ai énoncé la question plus clairement.

18
demandé sur Community 2014-07-04 17:00:56

3 réponses

Voici une solution un peu hackish, mais elle fait le travail. Vous avez voulu un .pdf, mais vous obtenez un bonus .png. :)

import numpy as np
import pandas as pd
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.pyplot as plt

from PySide.QtGui import QImage
from PySide.QtGui import QPainter
from PySide.QtCore import QSize
from PySide.QtWebKit import QWebPage

arrays = [np.hstack([ ['one']*3, ['two']*3]), ['Dog', 'Bird', 'Cat']*2]
columns = pd.MultiIndex.from_arrays(arrays, names=['foo', 'bar'])
df =pd.DataFrame(np.zeros((3,6)),columns=columns,index=pd.date_range('20000103',periods=3))

h = "<!DOCTYPE html> <html> <body> <p> " + df.to_html() + " </p> </body> </html>";
page = QWebPage()
page.setViewportSize(QSize(5000,5000))

frame = page.mainFrame()
frame.setHtml(h, "text/html")

img = QImage(1000,700, QImage.Format(5))
painter = QPainter(img)
frame.render(painter)
painter.end()
a = img.save("html.png")

pp = PdfPages('html.pdf')
fig = plt.figure(figsize=(8,6),dpi=1080) 
ax = fig.add_subplot(1, 1, 1)
img2 = plt.imread("html.png")
plt.axis('off')
ax.imshow(img2)
pp.savefig()
pp.close()

Modifications de bienvenue.

7
répondu Keith 2014-07-07 14:38:27

il s'agit, je crois, d'une table HTML que votre IDE rend. C'est ce que fait IPython notebook.

Vous pouvez obtenir une poignée comme ceci:

from IPython.display import HTML
import pandas as pd
data = pd.DataFrame({'spam':['ham','green','five',0,'kitties'],
                     'eggs':[0,1,2,3,4]})
h = HTML(data.to_html())
h

et l'enregistrer dans un fichier HTML:

my_file = open('some_file.html', 'w')
my_file.write(h.data)
my_file.close()
4
répondu Laurence Billingham 2014-07-07 09:08:46

je pense que ce qui est nécessaire ici est une façon cohérente de outputing une table à un fichier pdf parmi les graphiques sortie au pdf.

ma première pensée est de ne pas utiliser le backend matplotlib i.e.

from matplotlib.backends.backend_pdf import PdfPages

parce qu'il semblait quelque peu limité dans les options de formatage et s'est penché vers le formatage de la table comme une image (rendant ainsi le texte de la table dans un format non sélectionnable)

si vous voulez mélanger la sortie dataframe et les tracés matplotlib dans un pdf sans utiliser le matplotlib PDF backend, je peux penser à deux façons.

  1. générez votre pdf de figures de matplotlib comme avant et insérez ensuite les pages contenant la table de datagramme. Je considère cela comme une option difficile.
  2. utilisez une autre bibliothèque pour générer le pdf. J'illustre une option pour le faire ci-dessous.

tout d'Abord, installez xhtml2pdf bibliothèque. Cela semble un peu patchily soutenu, mais est active sur Github et a quelques utilisation de base de la documentation ici. Vous pouvez l'installer via pip i.e. pip install xhtml2pdf

une fois que vous avez fait cela, Voici un exemple barebones intégrant une figure matplotlib, puis la table (tout le texte sélectionnable), puis une autre figure. Vous pouvez jouer avec CSS etc pour modifier le formatage à vos spécifications exactes, mais je pense que cela remplit le bref:

from xhtml2pdf import pisa             # this is the module that will do the work
import numpy as np
import pandas as pd
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.pyplot as plt

# Utility function
def convertHtmlToPdf(sourceHtml, outputFilename):
    # open output file for writing (truncated binary)
    resultFile = open(outputFilename, "w+b")

    # convert HTML to PDF
    pisaStatus = pisa.CreatePDF(
            sourceHtml,                # the HTML to convert
            dest=resultFile,           # file handle to recieve result
            path='.')                  # this path is needed so relative paths for 
                                       # temporary image sources work

    # close output file
    resultFile.close()                 # close output file

    # return True on success and False on errors
    return pisaStatus.err

# Main program
if __name__=='__main__':   

    arrays = [np.hstack([ ['one']*3, ['two']*3]), ['Dog', 'Bird', 'Cat']*2]
    columns = pd.MultiIndex.from_arrays(arrays, names=['foo', 'bar'])
    df = pd.DataFrame(np.zeros((3,6)),columns=columns,index=pd.date_range('20000103',periods=3))

    # Define your data
    sourceHtml = '<html><head>'         
    # add some table CSS in head
    sourceHtml += '''<style>
                     table, td, th {
                           border-style: double;
                           border-width: 3px;
                     }

                     td,th {
                           padding: 5px;
                     }
                     </style>'''
    sourceHtml += '</head><body>'
    #Add a matplotlib figure(s)
    plt.plot(range(20))
    plt.savefig('tmp1.jpg')
    sourceHtml += '\n<p><img src="tmp1.jpg"></p>'

    # Add the dataframe
    sourceHtml += '\n<p>' + df.to_html() + '</p>'

    #Add another matplotlib figure(s)
    plt.plot(range(70,100))
    plt.savefig('tmp2.jpg')
    sourceHtml += '\n<p><img src="tmp2.jpg"></p>'

    sourceHtml += '</body></html>'
    outputFilename = 'test.pdf'

    convertHtmlToPdf(sourceHtml, outputFilename)

Remarque: Il semble y avoir un bug dans xhtml2pdf au moment de la rédaction, ce qui signifie que certains CSS ne sont pas respectés. Ce qui est particulièrement pertinent à cette question, c'est qu'il semble impossible d'avoir des doubles bordures autour de la table


EDIT

en réponse aux commentaires, il est devenu évident que certains utilisateurs (au moins @Keith qui a répondu et a accordé une prime!) veulent la table selectable, mais certainement sur un axe matplotlib. Ceci est un peu plus conforme à la méthode originale. Donc, voici un méthode utilisant le pdf backend pour matplotlib et matplotlib les objets. Je ne pense pas que le tableau semble aussi bon - en particulier l'affichage des en-têtes de colonne hiérarchiques, mais c'est une question de choix, je suppose. Je suis redevable à cette réponse et commentaires pour la façon de formater les axes pour l'affichage de table.

import numpy as np
import pandas as pd
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.pyplot as plt

# Main program
if __name__=='__main__':   
    pp = PdfPages('Output.pdf')
    arrays = [np.hstack([ ['one']*3, ['two']*3]), ['Dog', 'Bird', 'Cat']*2]
    columns = pd.MultiIndex.from_arrays(arrays, names=['foo', 'bar'])
    df =pd.DataFrame(np.zeros((3,6)),columns=columns,index=pd.date_range('20000103',periods=3))

    plt.plot(range(20))
    pp.savefig()
    plt.close()

    # Calculate some sizes for formatting - constants are arbitrary - play around
    nrows, ncols = len(df)+1, len(df.columns) + 10
    hcell, wcell = 0.3, 1.
    hpad, wpad = 0, 0   

    #put the table on a correctly sized figure    
    fig=plt.figure(figsize=(ncols*wcell+wpad, nrows*hcell+hpad))
    plt.gca().axis('off')
    matplotlib_tab = pd.tools.plotting.table(plt.gca(),df, loc='center')    
    pp.savefig()
    plt.close()

    #Add another matplotlib figure(s)
    plt.plot(range(70,100))
    pp.savefig()
    plt.close()

    pp.close()
2
répondu J Richard Snape 2017-05-23 11:45:32