Package: estout
estoutShort descriptions for main functions (see below for more details):collect_stats:
extracts a given set of attributes from results object generated by
stats packages (e.g.statsmodelsandlinearmodels)to_df: takes a
list ofcollect_statsoutputs and merges them as separate columns in a pandas DataFrameto_tex: takes
one or more DataFrames and creates tex code to build table with each
DataFrame as a different panelto_pdf: takes
one or more tex tables (either as strings or paths to tex files) and
merges them in a pdf documentInstallpipinstallestoutHow to useFirst, we set up an example dataset and run a few regressions to
showcase the functions in this module.importnumpyasnpimportpandasaspdimportstatsmodels.apiassmfromlinearmodelsimportPanelOLSimportestoutnp.random.seed(123)df=pd.DataFrame(np.random.rand(9,3),columns=['y','x','z'],index=pd.MultiIndex.from_product([[1,2,3],[1,2,3]],names=['firmid','time'])).assign(cons=1)sm1=sm.OLS(df['y'],df[['cons','x']]).fit()sm2=sm.OLS(df['y'],df[['cons','x','z']]).fit().get_robustcov_results(cov_type='HAC',maxlags=2)lmres=PanelOLS(df['y'],df[['cons','x','z']],entity_effects=True).fit(cov_type='clustered',cluster_entity=True)Extracting statistics after fitting a modelBelow, we collect just the default set of statistics from thesm1object.estout.collect_stats(sm1){'package': 'statsmodels',
 'ynames': ['y'],
 'xnames': ['cons', 'x'],
 'params': cons    0.507852
 x       0.345003
 dtype: float64,
 'tstats': cons    3.905440
 x       1.292246
 dtype: float64,
 'pvalues': cons    0.005858
 x       0.237293
 dtype: float64,
 'covmat':           cons         x
 cons  0.016910 -0.030531
 x    -0.030531  0.071278,
 'se': cons    0.130037
 x       0.266979
 dtype: float64,
 'nobs': 9,
 'r2': 0.19260886185799486}This list of default statistics is given by the functions implemented in
thestatsmodels_resultsmodule (sincesm1was generated by thestatsmodelspackage).The same functions exist in thelinearmodels_resultsmodule.print(estout.statsmodels_results.__all__)print(estout.linearmodels_results.__all__)['ynames', 'xnames', 'params', 'tstats', 'pvalues', 'covmat', 'se', 'nobs', 'r2']
['ynames', 'xnames', 'params', 'tstats', 'pvalues', 'covmat', 'se', 'nobs', 'r2']I might add to (but never subtract from) this list in future versions if
I find that there are other statistics I use very often.If there are other statistics that you need, and they are reported as
attributes in the results object, you can request them using theadd_statsparameter:estout.collect_stats(sm1,get_default_stats=False,add_stats={'xnames':'model.exog_names','Adj. R2':'rsquared_adj'}){'package': 'statsmodels',
 'xnames': ['cons', 'x'],
 'Adj. R2': 0.07726727069485129}Theadd_statsparameter also takes custom functions for statistics
that are not reported as an individual attribute of the results object.
These custom functions must return scalars.defallvars(lmres):returnlmres.model.dependent.vars+lmres.model.exog.varsestout.collect_stats(lmres,get_default_stats=False,add_stats={'all':allvars}){'package': 'linearmodels', 'all': ['y', 'cons', 'x', 'z']}Add scalar statistics not available as attributes of the results object
(using theadd_literalsparamter):estout.collect_stats(sm1,get_default_stats=False,add_literals={'Fixed Effects':'No','Nr observations':123}){'package': 'statsmodels', 'Fixed Effects': 'No', 'Nr observations': 123}Combining model results into a DataFrameStart by collecting stats from each model and combining them in a list.allmodels=[]forresin[sm1,sm2,lmres]:allmodels.append(estout.collect_stats(res))Then export them to a DataFrame.estout.to_df(allmodels)012consparams0.51***0.70***0.73***tstats(3.91)(21.48)(167.36)xparams0.350.57**0.64*tstats(1.29)(2.85)(2.26)zparams-0.64**-0.77**tstats(-3.55)(-2.91)r20.1930.4870.352nobs999We can choose to report only a subset of the regressors.estout.to_df(allmodels,which_xvars=['x','z'])012xparams0.350.57**0.64*tstats(1.29)(2.85)(2.26)zparams-0.64**-0.77**tstats(-3.55)(-2.91)r20.1930.4870.352nobs999Report other statistics under the parameter values.estout.to_df(allmodels,stats_body=['params','se','pvalues'],which_xvars=['x'])012xparams0.350.57**0.64*se(0.27)(0.20)(0.28)pvalues(0.237)(0.029)(0.086)r20.1930.4870.352nobs999Change the statistics reported at the bottom of the tableestout.to_df(allmodels,stats_bottom=['r2'],which_xvars=['x'])012xparams0.350.57**0.64*tstats(1.29)(2.85)(2.26)r20.1930.4870.352Change the formatting for any of the statistics reported.estout.to_df(allmodels,add_formats={'params':'{:.3}','r2':'{:.2f}'},which_xvars=['x'])012xparams0.3450.571**0.643*tstats(1.29)(2.85)(2.26)r20.190.490.35nobs999Replace names of regressor (or bottom stats) with labels.estout.to_df(allmodels,labels={'cons':'Intercept','nobs':'Observations'},which_xvars=['cons'])012Interceptparams0.51***0.70***0.73***tstats(3.91)(21.48)(167.36)r20.1930.4870.352Observations999Since the output ofto_dfis a
pd.DataFrame, it is easy to add more information at the bottom of the
table without having to re-runcollect_stats.df=estout.to_df(allmodels)df.loc['Fixed effects',:]=['No','No','Entity']df012consparams0.51***0.70***0.73***tstats(3.91)(21.48)(167.36)xparams0.350.57**0.64*tstats(1.29)(2.85)(2.26)zparams-0.64**-0.77**tstats(-3.55)(-2.91)r20.1930.4870.352nobs999Fixed effectsNoNoEntityExporting to LaTexWith theestout.to_texfunction, we can combine one or more DataFrames
into a single LaTex table (each DataFrame will be a separate panel in
the LaTex table).In the example below, we just return the tex code as a string, but the
function also takes anoutfileparameter that allows us to store the
output in a.texfile. Either the file path or the string can be used
in theestout.to_pdffunction to create a PDF out of this tex code.tbl=estout.to_tex([df,df],panel_title=['Panel A: Some title','Panel B: Some title'],col_groups=[{'Group1':[1,2]}]*2,col_names=[['Model 1','Model 2','Model 3']]*2,hlines=[[0,1,4,13],[1,4,13]])print(tbl)\newpage 
 \clearpage 
 \begin{table}[!h] \footnotesize 
\addtocounter{table}{0} 
\caption{\textbf{Table title}} 
\par {Table description} 

 \vspace{2mm} 

 \begin{tabular*}{\textwidth}{@{\extracolsep{\fill}}l*{3}{c}} 
 \hline \noalign{\smallskip} 
\multicolumn{4}{@{} l}{Panel A: Some title} \\ 
 \hline \noalign{\smallskip} 
& \multicolumn{2}{c}{Group1}  \\ 
\cline{2-3}  
 & Model 1 & Model 2 & Model 3 \\ 
 \hline \noalign{\smallskip} 
cons & 0.51*** & 0.70*** & 0.73*** \\ 
 & (3.91) & (21.48) & (167.36) \\ 
x & 0.35 & 0.57** & 0.64* \\ 
 & (1.29) & (2.85) & (2.26) \\ 
z &  & -0.64** & -0.77** \\ 
 &  & (-3.55) & (-2.91) \\ 
r2 & 0.193 & 0.487 & 0.352 \\ 
nobs & 9 & 9 & 9 \\ 
Fixed effects & No & No & Entity \\ 
 \hline \noalign{\smallskip} 
\end{tabular*}
 \smallskip 
\begin{tabular*}{\textwidth}{@{\extracolsep{\fill}}l*{3}{c}} 
\multicolumn{4}{@{} l}{Panel B: Some title} \\ 
 \hline \noalign{\smallskip} 
& \multicolumn{2}{c}{Group1}  \\ 
\cline{2-3}  
 & Model 1 & Model 2 & Model 3 \\ 
 \hline \noalign{\smallskip} 
cons & 0.51*** & 0.70*** & 0.73*** \\ 
 & (3.91) & (21.48) & (167.36) \\ 
x & 0.35 & 0.57** & 0.64* \\ 
 & (1.29) & (2.85) & (2.26) \\ 
z &  & -0.64** & -0.77** \\ 
 &  & (-3.55) & (-2.91) \\ 
r2 & 0.193 & 0.487 & 0.352 \\ 
nobs & 9 & 9 & 9 \\ 
Fixed effects & No & No & Entity \\ 
 \hline \noalign{\smallskip} 
\end{tabular*} 
\label{} 
 \end{table}Exporting to PDFWith theestout.to_pdffunction, we can combine the LaTex code for
multiple tables (like the ones produced byestout.to_tex) into a
single .tex document.By default, the resulting .tex file is run through TexLive’spdflatexutility to produce a PDF document with the tables (setmake_pdf=False
if you do not want the PDF to be automatically produced).You can also setopen_pdfto True if you want the resulting pdf to be
opened after it is produced.For the code below to work, you need to haveTexLiveinstalled (and
change the path below to a valid path on your system).estout.to_pdf(outfile='../_outputs/paper.tex',table_tex_code=[tbl,tbl],make_pdf=True,open_pdf=False)PDF creation successful!This produced a PDF with two tables (given by thetbltex string),
each with two panels (given by thedfDataFrame above).Note that thetable_tex_codeparameter also accepts paths to tex
files. Those tex files must have the complete table environment for that
table (i.e. from
).