2013-12-19 40 views
5

當使用nbconvert到latex & PDF時,是否可以從ipython筆記本中的熊貓數據框中獲得格式良好的表格?pandas dataframe as latex or html table nbconvert

默認情況下,它似乎只是一個左對齊的數字塊,以僞造的字體顯示。

我想要更多的東西在筆記本或乳膠表中的數據框的HTML顯示。保存並顯示HTML呈現的數據框的.png圖像也不錯,但究竟該如何做到這一點已被證明是難以捉摸的。

最小的情況是,我只想用一個簡單的中心對齊的表格來表示一個不錯的字體。

我還沒有嘗試過使用.to_latex()方法從筆記本或nbconvert輸出中獲取熊貓數據框的乳膠表。我也嘗試過(在閱讀ipython開發列表討論之後,並遵循自定義顯示邏輯筆記本示例),使用_repr_html_和_repr_latex_方法創建自定義類,分別返回_to_html()和_to_latex()的結果。我認爲nb轉換的一個主要問題是pdflatex不滿意數據框to_latex()輸出中的{'或'//'。但我不想在檢查之前開始擺弄這件事,我沒有遺漏任何東西。

謝謝。

+0

您不需要創建自定義類以將formater添加到現有類中:http:// nbvi ewer.ipython.org/github/ipython/ipython/blob/master/examples/notebooks/Custom%20Display%20Logic.ipynb#Adding-IPython-display-support-to-existing-objects,並且沒有熊貓沒有辦法制作來自表IIRC的乳膠。 – Matt

+0

我在這裏做了類似的事情,但我對解決方案不滿意。 http://stackoverflow.com/questions/24574976/save-the-out-table-of-a-pandas-dataframe-as-a-figure?lq=1 – Keith

回答

5

我爲此寫了我自己的基於mako的模板方案。我認爲這實際上是一個非常簡單的工作流程,如果你承諾爲自己闖過一次。在那之後,你開始看到模板化所需格式的元數據,所以它可以從代碼中分解出來(並且不代表第三方依賴)是解決它的一個很好的方法。

這是我想出的工作流程。

  1. 編寫接受您的數據幀作爲參數(可能還有其他參數),並把它轉換成你想要的(如下所示)TeX的格式.mako模板。創建一個包裝類(我稱之爲to_tex),它使得你需要的API(例如,你可以傳遞你的數據對象,並在內部處理對mako渲染命令的調用)。

  2. 在包裝類中,決定您希望如何輸出。將TeX代碼打印到屏幕上?使用一個子進程來實際編譯成PDF?

在我的情況,我正在生成一份研究報告的初步結果,並需要格式化表格與嵌套列名等複雜的雙排序結構這是一個什麼樣的表的一個看起來像一個例子:

Example output from templated TeX tool

下面是該灰鯖模板(警告,毛):

<%page args="df, table_title, group_var, sort_var"/> 
<% 
""" 
Template for country/industry two-panel double sorts TeX table. 
Inputs: 
------- 
df: pandas DataFrame 
    Must be 17 x 12 and have rows and columns that positionally 
    correspond to the entries of the table. 

table_title: string 
    String used for the title of the table. 

group_var: string 
    String naming the grouping variable for the horizontal sorts. 
    Should be 'Country' or 'Industry'. 

sort_var: string (raw) 
    String naming the variable that is being sorted, e.g. 
    "beta" or "ivol". Note that if you want the symbol to 
    be rendered as a TeX symbol, then pass a raw Python 
    string as the arg and include the needed TeX markup in 
    the passed string. If the string isn't raw, some of the 
    TeX markup might be interpreted as special characters. 

Returns: 
-------- 
When used with mako.template.Template.render, will produce 
a raw TeX string that can be rendered into a PDF containing 
the specified data. 

Author: 
------- 
Ely M. Spears, 05/21/2013 

""" 
# Python imports and helper function definitions. 
import numpy as np 
def format_helper(x): 
    return str(np.round(x,2)) 
%> 


<%text> 
\documentclass[10pt]{article} 
\usepackage[top=1in, bottom=1in, left=1in, right=1in]{geometry} 
\usepackage{array} 
\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} 
\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} 
\setlength{\parskip}{1em} 
\setlength{\parindent}{0in} 
\renewcommand*\arraystretch{1.5} 
\author{Ely Spears} 


\begin{document} 
\begin{table} \caption{</%text>${table_title}<%text>} 
\begin{center} 
    \begin{tabular}{ | p{2.5cm} c c c c c p{1cm} c c c c c c p{1cm} |} 
    \hline 
    & \multicolumn{6}{c}{CAPM $\beta$} & \multicolumn{6}{c}{CAPM $\alpha$ (\%p.a.)} & \\ 
    \cline{2-7} \cline{9-14} 
    & \multicolumn{6}{c}{</%text>${group_var}<%text> </%text>${sort_var}<%text> is:} & \multicolumn{6}{c}{</%text>${group_var}<%text> </%text>${sort_var}<%text> is:} & \\ 
    Stock </%text>${sort_var}<%text> is: & Low & 2 & 3 & 4 & High & Low - High & & Low & 2 & 3 & 4 & High & Low - High \\ 
    \hline 
    \multicolumn{4}{|l}{Panel A. Point estimates} & & & & & & & & & & \\ 
    \hline 
    Low   & </%text>${' & '.join(df.ix[0].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[0].map(format_helper).values[6:])}<%text> \\ 
    2    & </%text>${' & '.join(df.ix[1].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[1].map(format_helper).values[6:])}<%text> \\ 
    3    & </%text>${' & '.join(df.ix[2].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[2].map(format_helper).values[6:])}<%text> \\ 
    4    & </%text>${' & '.join(df.ix[3].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[3].map(format_helper).values[6:])}<%text> \\ 
    High   & </%text>${' & '.join(df.ix[4].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[4].map(format_helper).values[6:])}<%text> \\ 
    Low - High  & </%text>${' & '.join(df.ix[5].map(format_helper).values[0:5])}<%text> & & & </%text>${' & '.join(df.ix[5].map(format_helper).values[6:11])}<%text> & \\ 


    \multicolumn{6}{|l}{</%text>${group_var}<%text> effect (average of Low - High \underline{column})}  
     & </%text>${format_helper(df.ix[6,5])}<%text> & & & & & & & </%text>${format_helper(df.ix[6,11])}<%text> \\ 


    \multicolumn{6}{|l}{Within-</%text>${group_var}<%text> effect (average of Low - High \underline{row})} 
     & </%text>${format_helper(df.ix[7,5])}<%text> & & & & & & & </%text>${format_helper(df.ix[7,11])}<%text> \\ 


    \multicolumn{13}{|l}{Total effect} & </%text>${format_helper(df.ix[8,11])}<%text> \\ 
    \hline 
    \multicolumn{4}{|l}{Panel B. t-statistics} & & & & & & & & & & \\ 
    \hline 
    Low   & </%text>${' & '.join(df.ix[9].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[9].map(format_helper).values[6:])}<%text> \\ 
    2    & </%text>${' & '.join(df.ix[10].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[10].map(format_helper).values[6:])}<%text> \\ 
    3    & </%text>${' & '.join(df.ix[11].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[11].map(format_helper).values[6:])}<%text> \\ 
    4    & </%text>${' & '.join(df.ix[12].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[12].map(format_helper).values[6:])}<%text> \\ 
    High   & </%text>${' & '.join(df.ix[13].map(format_helper).values[0:6])}<%text> & & </%text>${' & '.join(df.ix[13].map(format_helper).values[6:])}<%text> \\ 
    Low - High  & </%text>${' & '.join(df.ix[14].map(format_helper).values[0:5])}<%text> & & & </%text>${' & '.join(df.ix[14].map(format_helper).values[6:11])}<%text> & \\ 


    \multicolumn{6}{|l}{</%text>${group_var}<%text> effect (average of Low - High \underline{column})}  
     & </%text>${format_helper(df.ix[15,5])}<%text> & & & & & & & </%text>${format_helper(df.ix[15,11])}<%text> \\ 


    \multicolumn{6}{|l}{Within-</%text>${group_var}<%text> effect (average of Low - High \underline{row})} 
     & </%text>${format_helper(df.ix[16,5])}<%text> & & & & & & & </%text>${format_helper(df.ix[16,11])}<%text> \\ 
    \hline 
    \end{tabular} 
\end{center} 
\end{table} 
\end{document} 
</%text> 

我的包裝to_tex.py看起來像這樣(用例在if __name__ == "__main__"部分使用):

""" 
to_tex.py 

Class for handling strings of TeX code and producing the 
rendered PDF via PDF LaTeX. Assumes ability to call PDFLaTeX 
via the operating system. 
""" 
class to_tex(object): 
    """ 
    Publishes a TeX string to a PDF rendering with pdflatex. 
    """ 
    def __init__(self, tex_string, tex_file, display=False): 
     """ 
     Publish a string to a .tex file, which will be 
     rendered into a .pdf file via pdflatex. 
     """ 
     self.tex_string = tex_string 
     self.tex_file  = tex_file 
     self.__to_tex_file() 
     self.__to_pdf_file(display) 
     print "Render status:", self.render_status 

    def __to_tex_file(self): 
     """ 
     Writes a tex string to a file. 
     """ 
     with open(self.tex_file, 'w') as t_file: 
      t_file.write(self.tex_string) 

    def __to_pdf_file(self, display=False): 
     """ 
     Compile a tex file to a pdf file with the 
     same file path and name. 
     """ 
     try: 
      import os 
      from subprocess import Popen 
      proc = Popen(["pdflatex", "-output-directory", os.path.dirname(self.tex_file), self.tex_file]) 
      proc.communicate() 
      self.render_status = "success" 
     except Exception as e: 
      self.render_status = str(e) 

     # Launch a display of the pdf if requested. 
     if (self.render_status == "success") and display: 
      try: 
       proc = Popen(["evince", self.tex_file.replace(".tex", ".pdf")]) 
       proc.communicate() 
      except: 
       pass 

if __name__ == "__main__": 
    from mako.template import Template 
    template_file = "path/to/template.mako" 
    t = Template(filename=template_file) 
    tex_str = t.render(arg1="arg1", ...) 
    tex_wrapper = to_tex(tex_str,) 

我的選擇是直接泵TeX的字符串pdflatex並作爲選項顯示它。

的實際使用這一個數據幀一小段代碼是在這裏:

# Assume calculation work is done prior to this ... 
all_beta = pandas.concat([beta_df, beta_tstat_df], axis=0) 
all_alpha = pandas.concat([alpha_df, alpha_tstat_df], axis=0) 
all_df = pandas.concat([all_beta, all_alpha], axis=1) 

# Render result in TeX 
tex_mako = "/my_project/templates/mako/two_panel_double_sort_table.mako" 
tex_file = "/my_project/some_tex_file_name.tex" 

from mako.template import Template 
t = Template(filename=tex_mako) 
tex_str = t.render(all_df, table_title, group_var, tex_risk_name) 

import my_project.to_tex as to_tex 
tex_obj = to_tex.to_tex(tex_str, tex_file) 
+1

這很好,任何機會,你可以使它成爲一個包(或者也許再次https://gist.github.com/takluyver/5098835)我們應該真的有一個IPython表格包,吐出許多格式! – Matt

+0

這將很有趣。也許在假期停機時間,我可以做到這一點,但沒有承諾。 – ely

5

有是在這個Github issue討論一個更簡單的方法。基本上,您必須將_repr_latex_方法添加到DataFrame類,該過程爲documented from pandas in their official documentation

我在這樣的筆記本做:

import pandas as pd 

pd.set_option('display.notebook_repr_html', True) 

def _repr_latex_(self): 
    return "\centering{%s}" % self.to_latex() 

pd.DataFrame._repr_latex_ = _repr_latex_ # monkey patch pandas DataFrame 

下面的代碼:

d = {'one' : [1., 2., 3., 4.], 
    'two' : [4., 3., 2., 1.]} 
df = pd.DataFrame(d) 
df 

變成一個HTML表,如果在筆記本評估現場,並將其轉換成一個(居中)PDF格式的表格:

$ ipython nbconvert --to latex --post PDF notebook.ipynb 
+0

這很方便。我通過定義一個繼承dataframe數據類型的簡單自定義類並添加了一個類似_repr_latex_方法以及格式化等來完成類似的事情。我猜,直接修補dataframe類更加輕量。 –

+0

你好,不錯的工作!我還爲to_latex方法添加了「escape = False」,因爲我喜歡使用LaTeX樣式字符串作爲列名稱。 –

+0

我提交了一個[PR](https://github.com/pydata/pandas/pull/11778),它剛剛合併到下一個修復此問題的熊貓版本(0.18)中。所以從該版本開始,轉換應該順利進行。 –